diff --git a/Make.config b/Make.config index 6842ebe01c79..0aff2357632d 100644 --- a/Make.config +++ b/Make.config @@ -643,22 +643,7 @@ JENKINS_RESULTS_DIRECTORY ?= $(abspath $(TOP)/jenkins-results) # Clone files instead of copying them on APFS file systems. Much faster. CP:=$(shell df -t apfs / >/dev/null 2>&1 && echo "cp -c" || echo "cp") -# WORKAROUND, Ideally it should be xcode-$(XCODE_PRODUCT_BUILD_VERSION) but mono does not build binaries for each xcode release -# Xcode 12.4 Build 12D4e -XCODE_IOS_ARCHIVE_VERSION=xcode-12D4e -# Xcode 12.4 Build 12D4e -XCODE_MACOS_ARCHIVE_VERSION=xcode-12D4e -MONO_IOS_FILENAME:=ios-release-Darwin-$(MONO_HASH).7z -MONO_IOS_URL:=https://download.mono-project.com/mono-sdks/$(XCODE_IOS_ARCHIVE_VERSION)/$(MONO_IOS_FILENAME) - -# Setup various variables depending on whether mono is downloaded or built from source -ifeq ($(MONO_BUILD_FROM_SOURCE),) -MONO_IOS_SDK_DESTDIR:=$(abspath $(TOP)/builds/downloads/$(basename $(MONO_IOS_FILENAME))) -MONO_BUILD_MODE=download-mono -else -MONO_IOS_SDK_DESTDIR:=$(abspath $(MONO_PATH)/sdks/out) -MONO_BUILD_MODE=compile-mono -endif +MONO_IOS_SDK_DESTDIR:=$(abspath $(TOP)/builds/downloads/mono-ios-sdk-destdir) # This variable includes all the platforms we support, even those that might be disabled in this build. ALL_PLATFORMS=iOS tvOS watchOS macOS diff --git a/builds/.gitignore b/builds/.gitignore index dad605eee05e..e258969d6dcd 100644 --- a/builds/.gitignore +++ b/builds/.gitignore @@ -3,31 +3,3 @@ dotnet-install.sh BundledNETCorePlatformsPackageVersion.txt downloads .stamp* -.deps.*.mk -*.config.cache -install -mac32 -mac64 -llvm -llvm64 -simulator86 -simulator64 -watchsimulator -tvsimulator -tools64 -cross -cross64 -crosstv -cross-watch -target7 -target7s -target64 -targettv -targetwatch -watchbcl -mono-ios-sdk-destdir -*.dylib -*.o -x86-64-slice* -*.pkg - diff --git a/builds/Makefile b/builds/Makefile index 3a33d9754283..4847fb7d9b4e 100644 --- a/builds/Makefile +++ b/builds/Makefile @@ -10,68 +10,22 @@ else DOTNET_ARCH=x64 endif -## -## Mono download vs. build -## - -download: download-mono -download-mono: \ - downloads/$(basename $(MONO_IOS_FILENAME)) \ - -downloads/$(basename $(MONO_IOS_FILENAME)): MONO_URL=$(MONO_IOS_URL) - include $(TOP)/mk/colors.mk -DOWNLOADS = \ - downloads/$(MONO_IOS_FILENAME) \ - -# This target downloads the mono archives, there's one for Xamarin.iOS and one for Xamarin.Mac. -# If doing many clean builds, it's possible to copy the downloaded zip file to ~/Library/Caches/xamarin-macios -# to avoid having to download it every time. The zip files have to be copied manually, otherwise -# we'd end up filling up a lot of hard drives around the world. -$(DOWNLOADS): - $(Q) mkdir -p downloads - $(Q) echo "Downloading $(MONO_URL)..." - $(Q) if test -f ~/Library/Caches/xamarin-macios/$(notdir $@); then \ - echo "Found a cached version of $(MONO_URL) in ~/Library/Caches/xamarin-macios/$(notdir $@)."; \ - $(CP) ~/Library/Caches/xamarin-macios/$(notdir $@) $@.tmp; \ - else \ - EC=0; \ - $(CURL_RETRY) $(MONO_URL) --output $@.tmp || EC=$$?; \ - if [[ x$$EC == x22 ]]; then \ - MSG="Could not download the archive %s because the URL doesn't exist. This can happen if bumping mono very soon after the corresponding commit was pushed to mono (i.e. the archive hasn't been built yet). If so, please wait a bit and try again."; \ - printf "$(COLOR_RED)*** $$MSG$(COLOR_CLEAR)\n" "$(notdir $@)"; \ - if test -n "$$FAILURE_REASON_PATH"; then printf "$$MSG\n" "[$(notdir $@)]($(MONO_URL))" >> "$$FAILURE_REASON_PATH"; fi; \ - fi; \ - if [[ x$$EC != x0 ]]; then exit $$EC; fi; \ - if [[ "x$$MACIOS_CACHE_DOWNLOADS" != "x" ]]; then \ - mkdir -p ~/Library/Caches/xamarin-macios/; \ - $(CP) $@.tmp ~/Library/Caches/xamarin-macios/"$(notdir $@)"; \ - echo "Cached the download of $(notdir $@) in ~/Library/Caches/xamarin-macios"; \ - fi; \ - fi - $(Q) mv $@.tmp $@ - $(Q) echo "Downloaded $(MONO_URL)" - -downloads/%: downloads/%.7z - $(Q) echo "Unzipping $*..." - $(Q) rm -Rf $@.tmp - $(Q) 7z x $< -o$@.tmp - $(Q) find $@.tmp -exec touch {} + - $(Q) mv $@.tmp $@ - $(Q) echo "Unzipped $*." - -downloads/%: downloads/%.nupkg - $(Q) echo "Unzipping $*..." - $(Q) rm -Rf $@.tmp - $(Q) unzip -d $@.tmp $< - $(Q) find $@.tmp -exec touch {} + - $(Q) mv $@.tmp $@ - $(Q) echo "Unzipped $*." +DOTNET_FILENAME=$(DOTNET_VERSION).tar.gz +DOTNET_CACHE_FILENAME=$(HOME)/Library/Caches/xamarin-macios/$(DOTNET_FILENAME) downloads/$(DOTNET_INSTALL_NAME): dotnet-install.sh $(Q) echo "Downloading and installing .NET $(DOTNET_VERSION) into $@..." - $(Q) ./dotnet-install.sh --install-dir "$@.tmp" --version "$(DOTNET_VERSION)" --architecture $(DOTNET_ARCH) --no-path $$DOTNET_INSTALL_EXTRA_ARGS + $(Q) if test -f $(DOTNET_CACHE_FILENAME); then \ + echo "Found a cached version of .NET $(DOTNET_VERSION) in $(DOTNET_CACHE_FILENAME)."; \ + mkdir -p "$@.tmp"; \ + tar -xzf $(DOTNET_CACHE_FILENAME) -C "$@.tmp"; \ + else \ + ./dotnet-install.sh --install-dir "$@.tmp" --version "$(DOTNET_VERSION)" --architecture $(DOTNET_ARCH) --no-path --keep-zip --zip-path "downloads/$(DOTNET_FILENAME)" $$DOTNET_INSTALL_EXTRA_ARGS; \ + cp -c downloads/$(DOTNET_FILENAME) $(DOTNET_CACHE_FILENAME); \ + echo "Cached the download of $(DOTNET_FILENAME) in ~/Library/Caches/xamarin-macios"; \ + fi $(Q) rm -Rf "$@" $(Q) mv "$@.tmp" "$@" $(Q) echo "Downloaded and installed .NET $(DOTNET_VERSION) into $@." @@ -106,10 +60,6 @@ dotnet-install.sh: Makefile $(Q) chmod +x $@.tmp $(Q) mv $@.tmp $@ -.stamp-download-mono: $(TOP)/Make.config $(TOP)/mk/mono.mk - $(MAKE) download-mono - $(Q) touch $@ - ifdef CUSTOM_DOTNET DOWNLOAD_DOTNET_VERSION=$(CUSTOM_DOTNET_VERSION) else @@ -168,17 +118,7 @@ dotnet:: $(DOTNET_DOWNLOADS) all-local:: $(DOTNET_DOWNLOADS) clean-local:: - $(Q) rm -Rf downloads .stamp-download-mono - -all-local:: .stamp-mono-ios-sdk-destdir - -.stamp-mono-ios-sdk-destdir: .stamp-$(MONO_BUILD_MODE) - ln -sf $(MONO_IOS_SDK_DESTDIR) mono-ios-sdk-destdir - $(Q) touch $@ - -# -# .NET -# + $(Q) rm -Rf downloads DOTNET_COMMON_DIRECTORIES += \ $(foreach platform,$(DOTNET_PLATFORMS_UPPERCASE),$(DOTNET_DESTDIR)/$($(platform)_NUGET_SDK_NAME)) \ @@ -212,4 +152,3 @@ $(DOTNET_COMMON_DIRECTORIES): install-dotnet: $(DOTNET_COMMON_TARGETS) install-local:: install-dotnet all-local:: install-dotnet - diff --git a/builds/Mono.framework-Info.plist b/builds/Mono.framework-Info.plist deleted file mode 100644 index 216a744ca55b..000000000000 --- a/builds/Mono.framework-Info.plist +++ /dev/null @@ -1,58 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleIdentifier - xamarin.ios.mono-framework - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - Mono - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 3.12 - NSPrincipalClass - - CFBundleExecutable - Mono - - BuildMachineOSBuild - 13F34 - CFBundleDevelopmentRegion - en - CFBundleSupportedPlatforms - - iPhoneOS - - DTCompiler - com.apple.compilers.llvm.clang.1_0 - DTPlatformBuild - 12D508 - DTPlatformName - iphoneos - DTPlatformVersion - 8.2 - DTSDKBuild - 12D508 - DTSDKName - iphoneos8.2 - DTXcode - 0620 - DTXcodeBuild - 6C131e - MinimumOSVersion - 12.2 - UIDeviceFamily - - 1 - 2 - - - diff --git a/builds/Mono.framework-tvos.Info.plist b/builds/Mono.framework-tvos.Info.plist deleted file mode 100644 index c0db855ee5ab..000000000000 --- a/builds/Mono.framework-tvos.Info.plist +++ /dev/null @@ -1,56 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleIdentifier - xamarin.tvos.mono-framework - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - Mono - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 3.12 - NSPrincipalClass - - CFBundleExecutable - Mono - BuildMachineOSBuild - 13F34 - CFBundleDevelopmentRegion - en - CFBundleSupportedPlatforms - - AppleTVOS - - DTCompiler - com.apple.compilers.llvm.clang.1_0 - DTPlatformBuild - 12D508 - DTPlatformName - appletvos - DTPlatformVersion - 9.0 - DTSDKBuild - 12D508 - DTSDKName - appletvos9.0 - DTXcode - 0620 - DTXcodeBuild - 6C131e - MinimumOSVersion - 12.2 - UIDeviceFamily - - 3 - - - diff --git a/builds/Mono.framework-watchos.Info.plist b/builds/Mono.framework-watchos.Info.plist deleted file mode 100644 index d49a2c154e7f..000000000000 --- a/builds/Mono.framework-watchos.Info.plist +++ /dev/null @@ -1,56 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleIdentifier - xamarin.watchos.mono-framework - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - Mono - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 3.12 - NSPrincipalClass - - CFBundleExecutable - Mono - BuildMachineOSBuild - 13F34 - CFBundleDevelopmentRegion - en - CFBundleSupportedPlatforms - - WatchOS - - DTCompiler - com.apple.compilers.llvm.clang.1_0 - DTPlatformBuild - 12D508 - DTPlatformName - watchos - DTPlatformVersion - 2.0 - DTSDKBuild - 12D508 - DTSDKName - watchos2.2 - DTXcode - 0620 - DTXcodeBuild - 6C131e - MinimumOSVersion - 4.0 - UIDeviceFamily - - 4 - - - diff --git a/builds/mac-System.config b/builds/mac-System.config deleted file mode 100644 index ebb9b2987fd1..000000000000 --- a/builds/mac-System.config +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/BaseStep.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/BaseStep.cs new file mode 100644 index 000000000000..39210adc9eeb --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/BaseStep.cs @@ -0,0 +1,83 @@ +// +// BaseStep.cs +// +// Author: +// Jb Evain (jbevain@novell.com) +// +// (C) 2007 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. +// + +using Mono.Cecil; + +namespace Mono.Linker.Steps { + + public abstract class BaseStep : IStep { + + private LinkContext _context; + + public LinkContext Context { + get { return _context; } + } + + public AnnotationStore Annotations { + get { return _context.Annotations; } + } + + public Tracer Tracer { + get { return _context.Tracer; } + } + + public MarkingHelpers MarkingHelpers => _context.MarkingHelpers; + + public void Process (LinkContext context) + { + _context = context; + + if (!ConditionToProcess ()) + return; + + Process (); + + foreach (AssemblyDefinition assembly in context.GetAssemblies ()) + ProcessAssembly (assembly); + + EndProcess (); + } + + protected virtual bool ConditionToProcess () + { + return true; + } + + protected virtual void Process () + { + } + + protected virtual void EndProcess () + { + } + + protected virtual void ProcessAssembly (AssemblyDefinition assembly) + { + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/BlacklistStep.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/BlacklistStep.cs new file mode 100644 index 000000000000..6448374c6f06 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/BlacklistStep.cs @@ -0,0 +1,146 @@ +// +// Blacklist.cs +// +// Author: +// Jb Evain (jb@nurv.fr) +// +// (C) 2007 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. +// + +using System; +using System.Linq; +using System.IO; +using System.Reflection; +using System.Xml; +using System.Xml.XPath; + +using Mono.Cecil; + +namespace Mono.Linker.Steps { + + public class BlacklistStep : BaseStep { + + protected override void Process () + { + foreach (string name in Assembly.GetExecutingAssembly ().GetManifestResourceNames ()) { + if (!name.EndsWith (".xml", StringComparison.OrdinalIgnoreCase) || !ShouldProcessAssemblyResource (GetAssemblyName (name))) + continue; + + try { + Context.LogMessage ($"Processing resource linker descriptor: {name}"); + AddToPipeline (GetResolveStep (name)); + } catch (XmlException ex) { + /* This could happen if some broken XML file is included. */ + Context.LogMessage ($"Error processing {name}: {ex}"); + } + } + + foreach (var asm in Context.GetAssemblies ()) { + foreach (var rsc in asm.Modules + .SelectMany (mod => mod.Resources) + .Where (res => res.ResourceType == ResourceType.Embedded) + .Where (res => res.Name.EndsWith (".xml", StringComparison.OrdinalIgnoreCase)) + .Where (res => ShouldProcessAssemblyResource (GetAssemblyName (res.Name))) + .Cast ()) { + try { + Context.LogMessage ($"Processing embedded resource linker descriptor: {rsc.Name}"); + + AddToPipeline (GetExternalResolveStep (rsc, asm)); + } catch (XmlException ex) { + /* This could happen if some broken XML file is embedded. */ + Context.LogMessage ($"Error processing {rsc.Name}: {ex}"); + } + } + } + } + + static string GetAssemblyName (string descriptor) + { + int pos = descriptor.LastIndexOf ('.'); + if (pos == -1) + return descriptor; + + return descriptor.Substring (0, pos); + } + + bool ShouldProcessAssemblyResource (string name) + { + AssemblyDefinition assembly = GetAssemblyIfReferenced (name); + + if (assembly is null) + return false; + + switch (Annotations.GetAction (assembly)) { + case AssemblyAction.Link: + case AssemblyAction.AddBypassNGen: + case AssemblyAction.AddBypassNGenUsed: + case AssemblyAction.Copy: + return true; + default: + return false; + } + } + + AssemblyDefinition GetAssemblyIfReferenced (string name) + { + foreach (AssemblyDefinition assembly in Context.GetAssemblies ()) + if (assembly.Name.Name == name) + return assembly; + + return null; + } + + protected virtual void AddToPipeline (IStep resolveStep) + { + Context.Pipeline.AddStepAfter (typeof (BlacklistStep), resolveStep); + } + + protected virtual IStep GetExternalResolveStep (EmbeddedResource resource, AssemblyDefinition assembly) + { + return new ResolveFromXmlStep (GetExternalDescriptor (resource), resource.Name, assembly, "resource " + resource.Name + " in " + assembly.FullName); + } + + static ResolveFromXmlStep GetResolveStep (string descriptor) + { + return new ResolveFromXmlStep (GetDescriptor (descriptor), "descriptor " + descriptor + " from " + Assembly.GetExecutingAssembly ().FullName); + } + + protected static XPathDocument GetExternalDescriptor (EmbeddedResource resource) + { + using (var sr = new StreamReader (resource.GetResourceStream ())) { + return new XPathDocument (sr); + } + } + + static XPathDocument GetDescriptor (string descriptor) + { + using (StreamReader sr = new StreamReader (GetResource (descriptor))) { + return new XPathDocument (sr); + } + } + + static Stream GetResource (string descriptor) + { + return Assembly.GetExecutingAssembly ().GetManifestResourceStream (descriptor); + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/CleanStep.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/CleanStep.cs new file mode 100644 index 000000000000..fd6c1063046d --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/CleanStep.cs @@ -0,0 +1,106 @@ +// +// CleanStep.cs +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// (C) 2006 Jb Evain +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using Mono.Cecil; + +namespace Mono.Linker.Steps { + + public class CleanStep : BaseStep { + + protected override void ProcessAssembly (AssemblyDefinition assembly) + { + if (Annotations.GetAction (assembly) == AssemblyAction.Link) + CleanAssembly (assembly); + } + + static void CleanAssembly (AssemblyDefinition asm) + { + foreach (TypeDefinition type in asm.MainModule.Types) + CleanType (type); + } + + static void CleanType (TypeDefinition type) + { + if (type.HasProperties) + CleanProperties (type); + if (type.HasEvents) + CleanEvents (type); + + if (type.HasNestedTypes) + foreach (var nested in type.NestedTypes) + CleanType (nested); + } + + static MethodDefinition CheckMethod (TypeDefinition type, MethodDefinition method) + { + if (method is null) + return null; + + return type.Methods.Contains (method) ? method : null; + } + + static void CleanEvents (TypeDefinition type) + { + var events = type.Events; + + for (int i = 0; i < events.Count; i++) { + var evt = events [i]; + evt.AddMethod = CheckMethod (type, evt.AddMethod); + evt.InvokeMethod = CheckMethod (type, evt.InvokeMethod); + evt.RemoveMethod = CheckMethod (type, evt.RemoveMethod); + + if (!IsEventUsed (evt)) + events.RemoveAt (i--); + } + } + + static bool IsEventUsed (EventDefinition evt) + { + return evt.AddMethod is not null || evt.InvokeMethod is not null || evt.RemoveMethod is not null; + } + + static void CleanProperties (TypeDefinition type) + { + var properties = type.Properties; + + for (int i = 0; i < properties.Count; i++) { + var prop = properties [i]; + prop.GetMethod = CheckMethod (type, prop.GetMethod); + prop.SetMethod = CheckMethod (type, prop.SetMethod); + + if (!IsPropertyUsed (prop)) + properties.RemoveAt (i--); + } + } + + static bool IsPropertyUsed (PropertyDefinition prop) + { + return prop.GetMethod is not null || prop.SetMethod is not null; + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/IStep.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/IStep.cs new file mode 100644 index 000000000000..c01ef1e6b3fa --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/IStep.cs @@ -0,0 +1,34 @@ +// +// IStep.cs +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// (C) 2006 Jb Evain +// +// 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. +// + +namespace Mono.Linker.Steps { + + public interface IStep { + void Process (LinkContext context); + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/LoadI18nAssemblies.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/LoadI18nAssemblies.cs new file mode 100644 index 000000000000..da8bb3921746 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/LoadI18nAssemblies.cs @@ -0,0 +1,103 @@ +// +// LoadI18nAssemblies.cs +// +// Author: +// Jb Evain (jbevain@novell.com) +// +// (C) 2007 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. +// + +using System; +using System.Linq; +using Mono.Cecil; + +namespace Mono.Linker.Steps { + + public class LoadI18nAssemblies : BaseStep { + + static readonly byte [] _pktoken = new byte [] { 0x07, 0x38, 0xeb, 0x9f, 0x13, 0x2e, 0xd7, 0x56 }; + + I18nAssemblies _assemblies; + + public LoadI18nAssemblies (I18nAssemblies assemblies) + { + _assemblies = assemblies; + } + + protected override bool ConditionToProcess () + { + return _assemblies != I18nAssemblies.None && + Context.GetAssemblies ().FirstOrDefault (a => a.Name.Name == "mscorlib")?.MainModule.GetType ("System.MonoType") is not null; + } + + protected override void Process () + { + LoadAssembly (GetAssemblyName (I18nAssemblies.Base)); + + LoadI18nAssembly (I18nAssemblies.CJK); + LoadI18nAssembly (I18nAssemblies.MidEast); + LoadI18nAssembly (I18nAssemblies.Other); + LoadI18nAssembly (I18nAssemblies.Rare); + LoadI18nAssembly (I18nAssemblies.West); + } + + bool ShouldCopyAssembly (I18nAssemblies current) + { + return (current & _assemblies) != 0; + } + + void LoadI18nAssembly (I18nAssemblies asm) + { + if (!ShouldCopyAssembly (asm)) + return; + + AssemblyNameReference name = GetAssemblyName (asm); + LoadAssembly (name); + } + + void LoadAssembly (AssemblyNameReference name) + { + AssemblyDefinition assembly = Context.Resolve (name); + Context.Annotations.SetAction (assembly, AssemblyAction.Copy); + ResolveFromAssemblyStep.ProcessLibrary (Context, assembly, ResolveFromAssemblyStep.RootVisibility.Any); + } + + AssemblyNameReference GetAssemblyName (I18nAssemblies assembly) + { + AssemblyNameReference name = new AssemblyNameReference ("I18N", GetCorlibVersion ()); + if (assembly != I18nAssemblies.Base) + name.Name += "." + assembly; + + name.PublicKeyToken = _pktoken; + return name; + } + + Version GetCorlibVersion () + { + foreach (AssemblyDefinition assembly in Context.GetAssemblies ()) + if (assembly.Name.Name == "mscorlib") + return assembly.Name.Version; + + return new Version (); + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/LoadReferencesStep.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/LoadReferencesStep.cs new file mode 100644 index 000000000000..06684edb61cb --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/LoadReferencesStep.cs @@ -0,0 +1,61 @@ +// +// LoadReferencesStep.cs +// +// Author: +// Jb Evain (jbevain@novell.com) +// +// (C) 2007 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. +// + +using System; +using System.Collections.Generic; + +using Mono.Cecil; + +namespace Mono.Linker.Steps { + + public class LoadReferencesStep : BaseStep { + + readonly HashSet references = new HashSet (); + + protected override void ProcessAssembly (AssemblyDefinition assembly) + { + ProcessReferences (assembly); + } + + protected void ProcessReferences (AssemblyDefinition assembly) + { + if (!references.Add (assembly.Name)) + return; + + Context.RegisterAssembly (assembly); + + foreach (AssemblyDefinition referenceDefinition in Context.ResolveReferences (assembly)) { + try { + ProcessReferences (referenceDefinition); + } catch (Exception ex) { + throw new LoadException (string.Format ("Error while processing references of '{0}'", assembly.FullName), ex); + } + } + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/MarkStep.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/MarkStep.cs new file mode 100644 index 000000000000..ff46157cbaeb --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/MarkStep.cs @@ -0,0 +1,3350 @@ +// +// MarkStep.cs +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// (C) 2006 Jb Evain +// (C) 2007 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. +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text.RegularExpressions; + +using Mono.Cecil; +using Mono.Cecil.Cil; +using Mono.Collections.Generic; + +namespace Mono.Linker.Steps { + + public partial class MarkStep : IStep { + + protected LinkContext _context; + protected Queue _methods; + protected List _virtual_methods; + protected Queue _assemblyLevelAttributes; + protected Queue _lateMarkedAttributes; + protected List _typesWithInterfaces; + protected List _unreachableBodies; + + public MarkStep () + { + _methods = new Queue (); + _virtual_methods = new List (); + _assemblyLevelAttributes = new Queue (); + _lateMarkedAttributes = new Queue (); + _typesWithInterfaces = new List (); + _unreachableBodies = new List (); + } + + public AnnotationStore Annotations => _context.Annotations; + public Tracer Tracer => _context.Tracer; + + public virtual void Process (LinkContext context) + { + _context = context; + + Initialize (); + Process (); + Complete (); + } + + void Initialize () + { + foreach (AssemblyDefinition assembly in _context.GetAssemblies ()) + InitializeAssembly (assembly); + } + + protected virtual void InitializeAssembly (AssemblyDefinition assembly) + { + Tracer.Push (assembly); + try { + switch (_context.Annotations.GetAction (assembly)) { + case AssemblyAction.Copy: + case AssemblyAction.Save: + MarkEntireAssembly (assembly); + break; + case AssemblyAction.Link: + case AssemblyAction.AddBypassNGen: + case AssemblyAction.AddBypassNGenUsed: + MarkAssembly (assembly); + + foreach (TypeDefinition type in assembly.MainModule.Types) + InitializeType (type); + + break; + } + } finally { + Tracer.Pop (); + } + } + + void Complete () + { + foreach (var body in _unreachableBodies) { + Annotations.SetAction (body.Method, MethodAction.ConvertToThrow); + } + } + + void InitializeType (TypeDefinition type) + { + if (type.HasNestedTypes) { + foreach (var nested in type.NestedTypes) + InitializeType (nested); + } + + if (!Annotations.IsMarked (type)) + return; + + MarkType (type); + + if (type.HasFields) + InitializeFields (type); + if (type.HasMethods) + InitializeMethods (type.Methods); + } + + protected bool IsFullyPreserved (TypeDefinition type) + { + if (Annotations.TryGetPreserve (type, out TypePreserve preserve) && preserve == TypePreserve.All) + return true; + + switch (Annotations.GetAction (type.Module.Assembly)) { + case AssemblyAction.Save: + case AssemblyAction.Copy: + case AssemblyAction.CopyUsed: + case AssemblyAction.AddBypassNGen: + case AssemblyAction.AddBypassNGenUsed: + return true; + } + + return false; + } + + void InitializeFields (TypeDefinition type) + { + foreach (FieldDefinition field in type.Fields) + if (Annotations.IsMarked (field)) + MarkField (field); + } + + void InitializeMethods (Collection methods) + { + foreach (MethodDefinition method in methods) + if (Annotations.IsMarked (method)) + EnqueueMethod (method); + } + + void MarkEntireType (TypeDefinition type) + { + if (type.HasNestedTypes) { + foreach (TypeDefinition nested in type.NestedTypes) + MarkEntireType (nested); + } + + Annotations.Mark (type); + MarkCustomAttributes (type); + MarkTypeSpecialCustomAttributes (type); + + if (type.HasInterfaces) { + foreach (InterfaceImplementation iface in type.Interfaces) { + MarkInterfaceImplementation (iface); + } + } + + MarkGenericParameterProvider (type); + + if (type.HasFields) { + foreach (FieldDefinition field in type.Fields) { + MarkField (field); + } + } + + if (type.HasMethods) { + foreach (MethodDefinition method in type.Methods) { + Annotations.Mark (method); + Annotations.SetAction (method, MethodAction.ForceParse); + EnqueueMethod (method); + } + } + + if (type.HasProperties) { + foreach (var property in type.Properties) { + MarkProperty (property); + } + } + + if (type.HasEvents) { + foreach (var ev in type.Events) { + MarkEvent (ev); + } + } + } + + void Process () + { + while (ProcessPrimaryQueue () || ProcessLazyAttributes () || ProcessLateMarkedAttributes ()) + + // deal with [TypeForwardedTo] pseudo-attributes + foreach (AssemblyDefinition assembly in _context.GetAssemblies ()) { + if (!assembly.MainModule.HasExportedTypes) + continue; + + foreach (var exported in assembly.MainModule.ExportedTypes) { + bool isForwarder = exported.IsForwarder; + var declaringType = exported.DeclaringType; + while (!isForwarder && (declaringType is not null)) { + isForwarder = declaringType.IsForwarder; + declaringType = declaringType.DeclaringType; + } + + if (!isForwarder) + continue; + TypeDefinition type = exported.Resolve (); + if (type is null) + continue; + if (!Annotations.IsMarked (type)) + continue; + Tracer.Push (type); + try { + _context.MarkingHelpers.MarkExportedType (exported, assembly.MainModule); + } finally { + Tracer.Pop (); + } + } + } + } + + bool ProcessPrimaryQueue () + { + if (QueueIsEmpty ()) + return false; + + while (!QueueIsEmpty ()) { + ProcessQueue (); + ProcessVirtualMethods (); + ProcessMarkedTypesWithInterfaces (); + ProcessPendingBodies (); + DoAdditionalProcessing (); + } + + return true; + } + + void ProcessQueue () + { + while (!QueueIsEmpty ()) { + MethodDefinition method = _methods.Dequeue (); + Tracer.Push (method); + try { + ProcessMethod (method); + } catch (Exception e) { + throw new MarkException (string.Format ("Error processing method: '{0}' in assembly: '{1}'", method.FullName, method.Module.Name), e, method); + } finally { + Tracer.Pop (); + } + } + } + + bool QueueIsEmpty () + { + return _methods.Count == 0; + } + + protected virtual void EnqueueMethod (MethodDefinition method) + { + _methods.Enqueue (method); + } + + void ProcessVirtualMethods () + { + foreach (MethodDefinition method in _virtual_methods) { + Tracer.Push (method); + ProcessVirtualMethod (method); + Tracer.Pop (); + } + } + + void ProcessMarkedTypesWithInterfaces () + { + // We may mark an interface type later on. Which means we need to reprocess any time with one or more interface implementations that have not been marked + // and if an interface type is found to be marked and implementation is not marked, then we need to mark that implementation + + // copy the data to avoid modified while enumerating error potential, which can happen under certain conditions. + var typesWithInterfaces = _typesWithInterfaces.ToArray (); + + foreach (var type in typesWithInterfaces) { + // Exception, types that have not been flagged as instantiated yet. These types may not need their interfaces even if the + // interface type is marked + if (!Annotations.IsInstantiated (type)) + continue; + + MarkInterfaceImplementations (type); + } + } + + void ProcessPendingBodies () + { + for (int i = 0; i < _unreachableBodies.Count; i++) { + var body = _unreachableBodies [i]; + if (Annotations.IsInstantiated (body.Method.DeclaringType)) { + MarkMethodBody (body); + _unreachableBodies.RemoveAt (i--); + } + } + } + + void ProcessVirtualMethod (MethodDefinition method) + { + var overrides = Annotations.GetOverrides (method); + if (overrides is null) + return; + + foreach (OverrideInformation @override in overrides) + ProcessOverride (@override); + } + + void ProcessOverride (OverrideInformation overrideInformation) + { + var method = overrideInformation.Override; + var @base = overrideInformation.Base; + if (!Annotations.IsMarked (method.DeclaringType)) + return; + + if (Annotations.IsProcessed (method)) + return; + + if (Annotations.IsMarked (method)) + return; + + var isInstantiated = Annotations.IsInstantiated (method.DeclaringType); + + // We don't need to mark overrides until it is possible that the type could be instantiated + // Note : The base type is interface check should be removed once we have base type sweeping + if (IsInterfaceOverrideThatDoesNotNeedMarked (overrideInformation, isInstantiated)) + return; + + if (!isInstantiated && !@base.IsAbstract && _context.IsOptimizationEnabled (CodeOptimizations.OverrideRemoval)) + return; + + MarkMethod (method); + ProcessVirtualMethod (method); + } + + bool IsInterfaceOverrideThatDoesNotNeedMarked (OverrideInformation overrideInformation, bool isInstantiated) + { + if (!overrideInformation.IsOverrideOfInterfaceMember || isInstantiated) + return false; + + if (overrideInformation.MatchingInterfaceImplementation is not null) + return !Annotations.IsMarked (overrideInformation.MatchingInterfaceImplementation); + + var interfaceType = overrideInformation.InterfaceType; + var overrideDeclaringType = overrideInformation.Override.DeclaringType; + + if (!IsInterfaceImplementationMarked (overrideDeclaringType, interfaceType)) { + var derivedInterfaceTypes = Annotations.GetDerivedInterfacesForInterface (interfaceType); + + // There are no derived interface types that could be marked, it's safe to skip marking this override + if (derivedInterfaceTypes is null) + return true; + + // If none of the other interfaces on the type that implement the interface from the @base type are marked, then it's safe to skip + // marking this override + if (!derivedInterfaceTypes.Any (d => IsInterfaceImplementationMarked (overrideDeclaringType, d))) + return true; + } + + return false; + } + + bool IsInterfaceImplementationMarked (TypeDefinition type, TypeDefinition interfaceType) + { + return type.HasInterface (@interfaceType, out InterfaceImplementation implementation) && Annotations.IsMarked (implementation); + } + + void MarkMarshalSpec (IMarshalInfoProvider spec) + { + if (!spec.HasMarshalInfo) + return; + + if (spec.MarshalInfo is CustomMarshalInfo marshaler) + MarkType (marshaler.ManagedType); + } + + void MarkCustomAttributes (ICustomAttributeProvider provider) + { + if (!provider.HasCustomAttributes) + return; + + bool markOnUse = _context.KeepUsedAttributeTypesOnly && Annotations.GetAction (GetAssemblyFromCustomAttributeProvider (provider)) == AssemblyAction.Link; + + Tracer.Push (provider); + try { + foreach (CustomAttribute ca in provider.CustomAttributes) { + if (IsUserDependencyMarker (ca.AttributeType) && provider is MemberReference mr) { + MarkUserDependency (mr, ca); + + if (_context.KeepDependencyAttributes) { + MarkCustomAttribute (ca); + continue; + } + + if (Annotations.GetAction (mr.Module.Assembly) == AssemblyAction.Link) + continue; + } + + if (markOnUse) { + _lateMarkedAttributes.Enqueue (new AttributeProviderPair (ca, provider)); + continue; + } + + MarkCustomAttribute (ca); + MarkSpecialCustomAttributeDependencies (ca); + } + } finally { + Tracer.Pop (); + } + } + + static AssemblyDefinition GetAssemblyFromCustomAttributeProvider (ICustomAttributeProvider provider) + { + return provider switch { + MemberReference mr => mr.Module.Assembly, + AssemblyDefinition ad => ad, + ModuleDefinition md => md.Assembly, + InterfaceImplementation ii => ii.InterfaceType.Module.Assembly, + GenericParameterConstraint gpc => gpc.ConstraintType.Module.Assembly, + ParameterDefinition pd => pd.ParameterType.Module.Assembly, + MethodReturnType mrt => mrt.ReturnType.Module.Assembly, + _ => throw new NotImplementedException (provider.GetType ().ToString ()), + }; + } + + protected virtual bool IsUserDependencyMarker (TypeReference type) + { + return PreserveDependencyLookupStep.IsPreserveDependencyAttribute (type); + } + + protected virtual void MarkUserDependency (MemberReference context, CustomAttribute ca) + { + if (ca.HasProperties && ca.Properties [0].Name == "Condition") { + var condition = ca.Properties [0].Argument.Value as string; + switch (condition) { + case "": + case null: + break; + case "DEBUG": + if (!_context.KeepMembersForDebugger) + return; + + break; + default: + // Don't have yet a way to match the general condition so everything is excluded + return; + } + } + + AssemblyDefinition assembly; + var args = ca.ConstructorArguments; + if (args.Count >= 3 && args [2].Value is string assemblyName) { + if (!_context.Resolver.AssemblyCache.TryGetValue (assemblyName, out assembly)) { + _context.LogMessage (MessageImportance.Low, $"Could not resolve '{assemblyName}' assembly dependency"); + return; + } + } else { + assembly = null; + } + + TypeDefinition td; + if (args.Count >= 2 && args [1].Value is string typeName) { + td = FindType (assembly ?? context.Module.Assembly, typeName); + + if (td is null) { + _context.LogMessage (MessageImportance.Low, $"Could not resolve '{typeName}' type dependency"); + return; + } + } else { + td = context.DeclaringType.Resolve (); + } + + string member = null; + string [] signature = null; + if (args.Count >= 1 && args [0].Value is string memberSignature) { + memberSignature = memberSignature.Replace (" ", ""); + var sign_start = memberSignature.IndexOf ('('); + var sign_end = memberSignature.LastIndexOf (')'); + if (sign_start > 0 && sign_end > sign_start) { + var parameters = memberSignature.Substring (sign_start + 1, sign_end - sign_start - 1); + signature = string.IsNullOrEmpty (parameters) ? Array.Empty () : parameters.Split (','); + member = memberSignature.Substring (0, sign_start); + } else { + member = memberSignature; + } + } + + if (member == "*") { + MarkEntireType (td); + return; + } + + if (MarkDependencyMethod (td, member, signature)) + return; + + if (MarkDependencyField (td, member)) + return; + + _context.LogMessage (MessageImportance.High, $"Could not resolve dependency member '{member}' declared in type '{td.FullName}'"); + } + + static TypeDefinition FindType (AssemblyDefinition assembly, string fullName) + { + fullName = fullName.ToCecilName (); + + var type = assembly.MainModule.GetType (fullName); + return type?.Resolve (); + } + + bool MarkDependencyMethod (TypeDefinition type, string name, string [] signature) + { + bool marked = false; + + int arity_marker = name.IndexOf ('`'); + if (arity_marker < 1 || !int.TryParse (name.Substring (arity_marker + 1), out int arity)) { + arity = 0; + } else { + name = name.Substring (0, arity_marker); + } + + foreach (var m in type.Methods) { + if (m.Name != name) + continue; + + if (m.GenericParameters.Count != arity) + continue; + + if (signature is null) { + MarkIndirectlyCalledMethod (m); + marked = true; + continue; + } + + var mp = m.Parameters; + if (mp.Count != signature.Length) + continue; + + int i = 0; + for (; i < signature.Length; ++i) { + if (mp [i].ParameterType.FullName != signature [i].Trim ().ToCecilName ()) { + i = -1; + break; + } + } + + if (i < 0) + continue; + + MarkIndirectlyCalledMethod (m); + marked = true; + } + + return marked; + } + + bool MarkDependencyField (TypeDefinition type, string name) + { + foreach (var f in type.Fields) { + if (f.Name == name) { + MarkField (f); + return true; + } + } + + return false; + } + + void LazyMarkCustomAttributes (ICustomAttributeProvider provider, AssemblyDefinition assembly) + { + if (!provider.HasCustomAttributes) + return; + + foreach (CustomAttribute ca in provider.CustomAttributes) + _assemblyLevelAttributes.Enqueue (new AttributeProviderPair (ca, assembly)); + } + + protected virtual void MarkCustomAttribute (CustomAttribute ca) + { + Tracer.Push ((object) ca.AttributeType ?? (object) ca); + try { + Annotations.Mark (ca); + MarkMethod (ca.Constructor); + + MarkCustomAttributeArguments (ca); + + TypeReference constructor_type = ca.Constructor.DeclaringType; + TypeDefinition type = constructor_type.Resolve (); + + if (type is null) { + HandleUnresolvedType (constructor_type); + return; + } + + MarkCustomAttributeProperties (ca, type); + MarkCustomAttributeFields (ca, type); + } finally { + Tracer.Pop (); + } + } + + protected virtual bool ShouldMarkCustomAttribute (CustomAttribute ca, ICustomAttributeProvider provider) + { + var attr_type = ca.AttributeType; + + if (_context.KeepUsedAttributeTypesOnly) { + switch (attr_type.FullName) { + // These are required by the runtime + case "System.ThreadStaticAttribute": + case "System.ContextStaticAttribute": + case "System.Runtime.CompilerServices.IsByRefLikeAttribute": + return true; + // Attributes related to `fixed` keyword used to declare fixed length arrays + case "System.Runtime.CompilerServices.FixedBufferAttribute": + return true; + case "System.Runtime.InteropServices.InterfaceTypeAttribute": + case "System.Runtime.InteropServices.GuidAttribute": + case "System.Runtime.CompilerServices.InternalsVisibleToAttribute": + return true; + } + + if (!Annotations.IsMarked (attr_type.Resolve ())) + return false; + } + + return true; + } + + protected virtual bool ShouldMarkTypeStaticConstructor (TypeDefinition type) + { + if (Annotations.HasPreservedStaticCtor (type)) + return false; + + if (type.IsBeforeFieldInit && _context.IsOptimizationEnabled (CodeOptimizations.BeforeFieldInit)) + return false; + + return true; + } + + protected void MarkStaticConstructor (TypeDefinition type) + { + if (MarkMethodIf (type.Methods, IsNonEmptyStaticConstructor) is not null) + Annotations.SetPreservedStaticCtor (type); + } + + protected virtual bool ShouldMarkTopLevelCustomAttribute (AttributeProviderPair app, MethodDefinition resolvedConstructor) + { + var ca = app.Attribute; + + if (!ShouldMarkCustomAttribute (app.Attribute, app.Provider)) + return false; + + // If an attribute's module has not been marked after processing all types in all assemblies and the attribute itself has not been marked, + // then surely nothing is using this attribute and there is no need to mark it + if (!Annotations.IsMarked (resolvedConstructor.Module) && + !Annotations.IsMarked (ca.AttributeType) && + Annotations.GetAction (resolvedConstructor.Module.Assembly) == AssemblyAction.Link) + return false; + + if (ca.Constructor.DeclaringType.Namespace == "System.Diagnostics") { + string attributeName = ca.Constructor.DeclaringType.Name; + if (attributeName == "DebuggerDisplayAttribute" || attributeName == "DebuggerTypeProxyAttribute") { + var displayTargetType = GetDebuggerAttributeTargetType (app.Attribute, (AssemblyDefinition) app.Provider); + if (displayTargetType is null || !Annotations.IsMarked (displayTargetType)) + return false; + } + } + + return true; + } + + protected void MarkSecurityDeclarations (ISecurityDeclarationProvider provider) + { + // most security declarations are removed (if linked) but user code might still have some + // and if the attributes references types then they need to be marked too + if ((provider is null) || !provider.HasSecurityDeclarations) + return; + + foreach (var sd in provider.SecurityDeclarations) + MarkSecurityDeclaration (sd); + } + + protected virtual void MarkSecurityDeclaration (SecurityDeclaration sd) + { + if (!sd.HasSecurityAttributes) + return; + + foreach (var sa in sd.SecurityAttributes) + MarkSecurityAttribute (sa); + } + + protected virtual void MarkSecurityAttribute (SecurityAttribute sa) + { + TypeReference security_type = sa.AttributeType; + TypeDefinition type = security_type.Resolve (); + if (type is null) { + HandleUnresolvedType (security_type); + return; + } + + MarkType (security_type); + MarkSecurityAttributeProperties (sa, type); + MarkSecurityAttributeFields (sa, type); + } + + protected void MarkSecurityAttributeProperties (SecurityAttribute sa, TypeDefinition attribute) + { + if (!sa.HasProperties) + return; + + foreach (var named_argument in sa.Properties) + MarkCustomAttributeProperty (named_argument, attribute); + } + + protected void MarkSecurityAttributeFields (SecurityAttribute sa, TypeDefinition attribute) + { + if (!sa.HasFields) + return; + + foreach (var named_argument in sa.Fields) + MarkCustomAttributeField (named_argument, attribute); + } + + protected void MarkCustomAttributeProperties (CustomAttribute ca, TypeDefinition attribute) + { + if (!ca.HasProperties) + return; + + foreach (var named_argument in ca.Properties) + MarkCustomAttributeProperty (named_argument, attribute); + } + + protected void MarkCustomAttributeProperty (CustomAttributeNamedArgument namedArgument, TypeDefinition attribute) + { + PropertyDefinition property = GetProperty (attribute, namedArgument.Name); + Tracer.Push (property); + if (property is not null) + MarkMethod (property.SetMethod); + + MarkCustomAttributeArgument (namedArgument.Argument); + Tracer.Pop (); + } + + PropertyDefinition GetProperty (TypeDefinition type, string propertyname) + { + while (type is not null) { + PropertyDefinition property = type.Properties.FirstOrDefault (p => p.Name == propertyname); + if (property is not null) + return property; + + type = type.BaseType?.Resolve (); + } + + return null; + } + + protected void MarkCustomAttributeFields (CustomAttribute ca, TypeDefinition attribute) + { + if (!ca.HasFields) + return; + + foreach (var named_argument in ca.Fields) + MarkCustomAttributeField (named_argument, attribute); + } + + protected void MarkCustomAttributeField (CustomAttributeNamedArgument namedArgument, TypeDefinition attribute) + { + FieldDefinition field = GetField (attribute, namedArgument.Name); + if (field is not null) + MarkField (field); + + MarkCustomAttributeArgument (namedArgument.Argument); + } + + FieldDefinition GetField (TypeDefinition type, string fieldname) + { + while (type is not null) { + FieldDefinition field = type.Fields.FirstOrDefault (f => f.Name == fieldname); + if (field is not null) + return field; + + type = type.BaseType?.Resolve (); + } + + return null; + } + + MethodDefinition GetMethodWithNoParameters (TypeDefinition type, string methodname) + { + while (type is not null) { + MethodDefinition method = type.Methods.FirstOrDefault (m => m.Name == methodname && !m.HasParameters); + if (method is not null) + return method; + + type = type.BaseType.Resolve (); + } + + return null; + } + + void MarkCustomAttributeArguments (CustomAttribute ca) + { + if (!ca.HasConstructorArguments) + return; + + foreach (var argument in ca.ConstructorArguments) + MarkCustomAttributeArgument (argument); + } + + void MarkCustomAttributeArgument (CustomAttributeArgument argument) + { + var at = argument.Type; + + if (at.IsArray) { + var et = at.GetElementType (); + + MarkType (et); + if (argument.Value is null) + return; + + foreach (var caa in (CustomAttributeArgument []) argument.Value) + MarkCustomAttributeArgument (caa); + + return; + } + + if (at.Namespace == "System") { + switch (at.Name) { + case "Type": + MarkType (argument.Type); + MarkType ((TypeReference) argument.Value); + return; + + case "Object": + var boxed_value = (CustomAttributeArgument) argument.Value; + MarkType (boxed_value.Type); + MarkCustomAttributeArgument (boxed_value); + return; + } + } + } + + protected bool CheckProcessed (IMetadataTokenProvider provider) + { + if (Annotations.IsProcessed (provider)) + return true; + + Annotations.Processed (provider); + return false; + } + + protected void MarkAssembly (AssemblyDefinition assembly) + { + if (CheckProcessed (assembly)) + return; + + ProcessModule (assembly); + + MarkAssemblyCustomAttributes (assembly); + + MarkSecurityDeclarations (assembly); + + foreach (ModuleDefinition module in assembly.Modules) + LazyMarkCustomAttributes (module, assembly); + } + + void MarkEntireAssembly (AssemblyDefinition assembly) + { + MarkCustomAttributes (assembly); + MarkCustomAttributes (assembly.MainModule); + + if (assembly.MainModule.HasExportedTypes) { + // TODO: This needs more work accross all steps + } + + foreach (TypeDefinition type in assembly.MainModule.Types) + MarkEntireType (type); + } + + void ProcessModule (AssemblyDefinition assembly) + { + // Pre-mark if there is any methods as they need to be executed + // at assembly load time + foreach (TypeDefinition type in assembly.MainModule.Types) { + if (type.Name == "" && type.HasMethods) { + MarkType (type); + break; + } + } + } + + bool ProcessLazyAttributes () + { + if (Annotations.HasMarkedAnyIndirectlyCalledMethods () && MarkDisablePrivateReflectionAttribute ()) + return true; + + var startingQueueCount = _assemblyLevelAttributes.Count; + if (startingQueueCount == 0) + return false; + + var skippedItems = new List (); + var markOccurred = false; + + while (_assemblyLevelAttributes.Count != 0) { + var assemblyLevelAttribute = _assemblyLevelAttributes.Dequeue (); + var customAttribute = assemblyLevelAttribute.Attribute; + + var resolved = customAttribute.Constructor.Resolve (); + if (resolved is null) { + HandleUnresolvedMethod (customAttribute.Constructor); + continue; + } + + if (!ShouldMarkTopLevelCustomAttribute (assemblyLevelAttribute, resolved)) { + skippedItems.Add (assemblyLevelAttribute); + continue; + } + + string attributeFullName = customAttribute.Constructor.DeclaringType.FullName; + switch (attributeFullName) { + case "System.Diagnostics.DebuggerDisplayAttribute": + MarkTypeWithDebuggerDisplayAttribute (GetDebuggerAttributeTargetType (assemblyLevelAttribute.Attribute, (AssemblyDefinition) assemblyLevelAttribute.Provider), customAttribute); + break; + case "System.Diagnostics.DebuggerTypeProxyAttribute": + MarkTypeWithDebuggerTypeProxyAttribute (GetDebuggerAttributeTargetType (assemblyLevelAttribute.Attribute, (AssemblyDefinition) assemblyLevelAttribute.Provider), customAttribute); + break; + } + + markOccurred = true; + MarkCustomAttribute (customAttribute); + } + + // requeue the items we skipped in case we need to make another pass + foreach (var item in skippedItems) + _assemblyLevelAttributes.Enqueue (item); + + return markOccurred; + } + + bool ProcessLateMarkedAttributes () + { + var startingQueueCount = _lateMarkedAttributes.Count; + if (startingQueueCount == 0) + return false; + + var skippedItems = new List (); + var markOccurred = false; + + while (_lateMarkedAttributes.Count != 0) { + var attributeProviderPair = _lateMarkedAttributes.Dequeue (); + var customAttribute = attributeProviderPair.Attribute; + + var resolved = customAttribute.Constructor.Resolve (); + if (resolved is null) { + HandleUnresolvedMethod (customAttribute.Constructor); + continue; + } + + if (!ShouldMarkCustomAttribute (customAttribute, attributeProviderPair.Provider)) { + skippedItems.Add (attributeProviderPair); + continue; + } + + markOccurred = true; + MarkCustomAttribute (customAttribute); + MarkSpecialCustomAttributeDependencies (customAttribute); + } + + // requeue the items we skipped in case we need to make another pass + foreach (var item in skippedItems) + _lateMarkedAttributes.Enqueue (item); + + return markOccurred; + } + + protected void MarkField (FieldReference reference) + { + if (reference.DeclaringType is GenericInstanceType) + MarkType (reference.DeclaringType); + + FieldDefinition field = reference.Resolve (); + + if (field is null) { + HandleUnresolvedField (reference); + return; + } + + MarkField (field); + } + + void MarkField (FieldDefinition field) + { + if (CheckProcessed (field)) + return; + + MarkType (field.DeclaringType); + MarkType (field.FieldType); + MarkCustomAttributes (field); + MarkMarshalSpec (field); + DoAdditionalFieldProcessing (field); + + var parent = field.DeclaringType; + if (!Annotations.HasPreservedStaticCtor (parent)) + MarkStaticConstructor (parent); + + if (Annotations.HasSubstitutedInit (field)) { + Annotations.SetPreservedStaticCtor (parent); + Annotations.SetSubstitutedInit (parent); + } + + Annotations.Mark (field); + } + + protected virtual bool IgnoreScope (IMetadataScope scope) + { + AssemblyDefinition assembly = ResolveAssembly (scope); + return Annotations.GetAction (assembly) != AssemblyAction.Link; + } + + void MarkScope (IMetadataScope scope) + { + if (scope is IMetadataTokenProvider provider) + Annotations.Mark (provider); + } + + protected virtual void MarkSerializable (TypeDefinition type) + { + MarkDefaultConstructor (type); + MarkMethodsIf (type.Methods, IsSpecialSerializationConstructor); + } + + protected virtual TypeDefinition MarkType (TypeReference reference) + { + if (reference is null) + return null; + + reference = GetOriginalType (reference); + + if (reference is FunctionPointerType) + return null; + + if (reference is GenericParameter) + return null; + + // if (IgnoreScope (reference.Scope)) + // return null; + + TypeDefinition type = reference.Resolve (); + + if (type is null) { + HandleUnresolvedType (reference); + return null; + } + + if (CheckProcessed (type)) + return null; + + Tracer.Push (type); + + MarkScope (type.Scope); + MarkType (type.BaseType); + MarkType (type.DeclaringType); + MarkCustomAttributes (type); + MarkSecurityDeclarations (type); + + if (type.IsMulticastDelegate ()) { + MarkMulticastDelegate (type); + } + + if (type.IsSerializable ()) + MarkSerializable (type); + + if (!_context.IsFeatureExcluded ("etw") && BCL.EventTracingForWindows.IsEventSourceImplementation (type, _context)) { + MarkEventSourceProviders (type); + } + + MarkTypeSpecialCustomAttributes (type); + + MarkGenericParameterProvider (type); + + // keep fields for value-types and for classes with LayoutKind.Sequential or Explicit + if (type.IsValueType || !type.IsAutoLayout) + MarkFields (type, type.IsEnum); + + // There are a number of markings we can defer until later when we know it's possible a reference type could be instantiated + // For example, if no instance of a type exist, then we don't need to mark the interfaces on that type + // However, for some other types there is no benefit to deferring + if (type.IsInterface) { + // There's no benefit to deferring processing of an interface type until we know a type implementing that interface is marked + MarkRequirementsForInstantiatedTypes (type); + } else if (type.IsValueType) { + // Note : Technically interfaces could be removed from value types in some of the same cases as reference types, however, it's harder to know when + // a value type instance could exist. You'd have to track initobj and maybe locals types. Going to punt for now. + MarkRequirementsForInstantiatedTypes (type); + } else if (IsFullyPreserved (type)) { + // Here for a couple reasons: + // * Edge case to cover a scenario where a type has preserve all, implements interfaces, but does not have any instance ctors. + // Normally TypePreserve.All would cause an instance ctor to be marked and that would in turn lead to MarkInterfaceImplementations being called + // Without an instance ctor, MarkInterfaceImplementations is not called and then TypePreserve.All isn't truly respected. + // * If an assembly has the action Copy and had ResolveFromAssemblyStep ran for the assembly, then InitializeType will have led us here + // When the entire assembly is preserved, then all interfaces, base, etc will be preserved on the type, so we need to make sure + // all of these types are marked. For example, if an interface implementation is of a type in another assembly that is linked, + // and there are no other usages of that interface type, then we need to make sure the interface type is still marked because + // this type is going to retain the interface implementation + MarkRequirementsForInstantiatedTypes (type); + } else if (AlwaysMarkTypeAsInstantiated (type)) { + MarkRequirementsForInstantiatedTypes (type); + } + + if (type.HasInterfaces) + _typesWithInterfaces.Add (type); + + if (type.HasMethods) { + MarkMethodsIf (type.Methods, IsVirtualNeededByTypeDueToPreservedScope); + if (ShouldMarkTypeStaticConstructor (type)) + MarkStaticConstructor (type); + + MarkMethodsIf (type.Methods, HasSerializationAttribute); + } + + DoAdditionalTypeProcessing (type); + + Tracer.Pop (); + + Annotations.Mark (type); + + ApplyPreserveInfo (type); + + return type; + } + + // Allow subclassers to mark additional things in the main processing loop + protected virtual void DoAdditionalProcessing () + { + } + + // Allow subclassers to mark additional things + protected virtual void DoAdditionalTypeProcessing (TypeDefinition type) + { + } + + // Allow subclassers to mark additional things + protected virtual void DoAdditionalFieldProcessing (FieldDefinition field) + { + } + + // Allow subclassers to mark additional things + protected virtual void DoAdditionalPropertyProcessing (PropertyDefinition property) + { + } + + // Allow subclassers to mark additional things + protected virtual void DoAdditionalEventProcessing (EventDefinition evt) + { + } + + // Allow subclassers to mark additional things + protected virtual void DoAdditionalInstantiatedTypeProcessing (TypeDefinition type) + { + } + + void MarkAssemblyCustomAttributes (AssemblyDefinition assembly) + { + if (!assembly.HasCustomAttributes) + return; + + foreach (CustomAttribute attribute in assembly.CustomAttributes) + _assemblyLevelAttributes.Enqueue (new AttributeProviderPair (attribute, assembly)); + } + + TypeDefinition GetDebuggerAttributeTargetType (CustomAttribute ca, AssemblyDefinition asm) + { + TypeReference targetTypeReference = null; + foreach (var property in ca.Properties) { + if (property.Name == "Target") { + targetTypeReference = (TypeReference) property.Argument.Value; + break; + } + + if (property.Name == "TargetTypeName") { + if (TypeNameParser.TryParseTypeAssemblyQualifiedName ((string) property.Argument.Value, out string typeName, out string assemblyName)) { + if (string.IsNullOrEmpty (assemblyName)) + targetTypeReference = asm.MainModule.GetType (typeName); + else + targetTypeReference = _context.GetAssemblies ().FirstOrDefault (a => a.Name.Name == assemblyName)?.MainModule.GetType (typeName); + } + break; + } + } + + return targetTypeReference?.Resolve (); + } + + void MarkTypeSpecialCustomAttributes (TypeDefinition type) + { + if (!type.HasCustomAttributes) + return; + + foreach (CustomAttribute attribute in type.CustomAttributes) { + var attrType = attribute.Constructor.DeclaringType; + switch (attrType.Name) { + case "XmlSchemaProviderAttribute" when attrType.Namespace == "System.Xml.Serialization": + MarkXmlSchemaProvider (type, attribute); + break; + case "DebuggerDisplayAttribute" when attrType.Namespace == "System.Diagnostics": + MarkTypeWithDebuggerDisplayAttribute (type, attribute); + break; + case "DebuggerTypeProxyAttribute" when attrType.Namespace == "System.Diagnostics": + MarkTypeWithDebuggerTypeProxyAttribute (type, attribute); + break; + case "EventDataAttribute" when attrType.Namespace == "System.Diagnostics.Tracing": + MarkMethodsIf (type.Methods, MethodDefinitionExtensions.IsPublicInstancePropertyMethod); + break; + case "TypeDescriptionProviderAttribute" when attrType.Namespace == "System.ComponentModel": + MarkTypeConverterLikeDependency (attribute, l => l.IsDefaultConstructor ()); + break; + } + } + } + + // + // Used for known framework attributes which can be applied to any element + // + bool MarkSpecialCustomAttributeDependencies (CustomAttribute ca) + { + var dt = ca.Constructor.DeclaringType; + if (dt.Name == "TypeConverterAttribute" && dt.Namespace == "System.ComponentModel") { + MarkTypeConverterLikeDependency (ca, l => + l.IsDefaultConstructor () || + l.Parameters.Count == 1 && l.Parameters [0].ParameterType.IsTypeOf ("System", "Type")); + return true; + } + + return false; + } + + void MarkMethodSpecialCustomAttributes (MethodDefinition method) + { + if (!method.HasCustomAttributes) + return; + + foreach (CustomAttribute attribute in method.CustomAttributes) { + switch (attribute.Constructor.DeclaringType.FullName) { + case "System.Web.Services.Protocols.SoapHeaderAttribute": + MarkSoapHeader (method, attribute); + break; + } + } + } + + void MarkXmlSchemaProvider (TypeDefinition type, CustomAttribute attribute) + { + if (TryGetStringArgument (attribute, out string name)) + MarkNamedMethod (type, name); + } + + protected virtual void MarkTypeConverterLikeDependency (CustomAttribute attribute, Func predicate) + { + var args = attribute.ConstructorArguments; + if (args.Count < 1) + return; + + TypeDefinition tdef = null; + switch (attribute.ConstructorArguments [0].Value) { + case string s: + tdef = ResolveFullyQualifiedTypeName (s); + break; + case TypeReference type: + tdef = type.Resolve (); + break; + } + + if (tdef is null) + return; + + MarkMethodsIf (tdef.Methods, predicate); + } + + void MarkTypeWithDebuggerDisplayAttribute (TypeDefinition type, CustomAttribute attribute) + { + if (_context.KeepMembersForDebugger) { + + string displayString = (string) attribute.ConstructorArguments [0].Value; + + Regex regex = new Regex ("{[^{}]+}", RegexOptions.Compiled); + + foreach (Match match in regex.Matches (displayString)) { + // Remove '{' and '}' + string realMatch = match.Value.Substring (1, match.Value.Length - 2); + + // Remove ",nq" suffix if present + // (it asks the expression evaluator to remove the quotes when displaying the final value) + if (Regex.IsMatch (realMatch, @".+,\s*nq")) { + realMatch = realMatch.Substring (0, realMatch.LastIndexOf (',')); + } + + if (realMatch.EndsWith ("()")) { + string methodName = realMatch.Substring (0, realMatch.Length - 2); + MethodDefinition method = GetMethodWithNoParameters (type, methodName); + if (method is not null) { + MarkMethod (method); + continue; + } + } else { + FieldDefinition field = GetField (type, realMatch); + if (field is not null) { + MarkField (field); + continue; + } + + PropertyDefinition property = GetProperty (type, realMatch); + if (property is not null) { + if (property.GetMethod is not null) { + MarkMethod (property.GetMethod); + } + if (property.SetMethod is not null) { + MarkMethod (property.SetMethod); + } + continue; + } + } + + while (type is not null) { + MarkMethods (type); + MarkFields (type, includeStatic: true); + type = type.BaseType?.Resolve (); + } + return; + } + } + } + + void MarkTypeWithDebuggerTypeProxyAttribute (TypeDefinition type, CustomAttribute attribute) + { + if (_context.KeepMembersForDebugger) { + object constructorArgument = attribute.ConstructorArguments [0].Value; + TypeReference proxyTypeReference = constructorArgument as TypeReference; + if (proxyTypeReference is null) { + if (constructorArgument is string proxyTypeReferenceString) { + proxyTypeReference = type.Module.GetType (proxyTypeReferenceString, runtimeName: true); + } + } + + if (proxyTypeReference is null) { + return; + } + + MarkType (proxyTypeReference); + + TypeDefinition proxyType = proxyTypeReference.Resolve (); + if (proxyType is not null) { + MarkMethods (proxyType); + MarkFields (proxyType, includeStatic: true); + } + } + } + + static bool TryGetStringArgument (CustomAttribute attribute, out string argument) + { + argument = null; + + if (attribute.ConstructorArguments.Count < 1) + return false; + + argument = attribute.ConstructorArguments [0].Value as string; + + return argument is not null; + } + + protected int MarkNamedMethod (TypeDefinition type, string method_name) + { + if (!type.HasMethods) + return 0; + + int count = 0; + foreach (MethodDefinition method in type.Methods) { + if (method.Name != method_name) + continue; + + MarkMethod (method); + count++; + } + + return count; + } + + void MarkSoapHeader (MethodDefinition method, CustomAttribute attribute) + { + if (!TryGetStringArgument (attribute, out string member_name)) + return; + + MarkNamedField (method.DeclaringType, member_name); + MarkNamedProperty (method.DeclaringType, member_name); + } + + void MarkNamedField (TypeDefinition type, string field_name) + { + if (!type.HasFields) + return; + + foreach (FieldDefinition field in type.Fields) { + if (field.Name != field_name) + continue; + + MarkField (field); + } + } + + void MarkNamedProperty (TypeDefinition type, string property_name) + { + if (!type.HasProperties) + return; + + foreach (PropertyDefinition property in type.Properties) { + if (property.Name != property_name) + continue; + + Tracer.Push (property); + MarkMethod (property.GetMethod); + MarkMethod (property.SetMethod); + Tracer.Pop (); + } + } + + void MarkInterfaceImplementations (TypeDefinition type) + { + if (!type.HasInterfaces) + return; + + foreach (var iface in type.Interfaces) { + // Only mark interface implementations of interface types that have been marked. + // This enables stripping of interfaces that are never used + var resolvedInterfaceType = iface.InterfaceType.Resolve (); + if (resolvedInterfaceType is null) { + HandleUnresolvedType (iface.InterfaceType); + continue; + } + + if (ShouldMarkInterfaceImplementation (type, iface, resolvedInterfaceType)) + MarkInterfaceImplementation (iface); + } + } + + void MarkGenericParameterProvider (IGenericParameterProvider provider) + { + if (!provider.HasGenericParameters) + return; + + foreach (GenericParameter parameter in provider.GenericParameters) + MarkGenericParameter (parameter); + } + + void MarkGenericParameter (GenericParameter parameter) + { + MarkCustomAttributes (parameter); + if (!parameter.HasConstraints) + return; + + foreach (var constraint in parameter.Constraints) { + MarkCustomAttributes (constraint); + MarkType (constraint.ConstraintType); + } + } + + bool IsVirtualNeededByTypeDueToPreservedScope (MethodDefinition method) + { + if (!method.IsVirtual) + return false; + + var base_list = Annotations.GetBaseMethods (method); + if (base_list is null) + return false; + + foreach (MethodDefinition @base in base_list) { + // Just because the type is marked does not mean we need interface methods. + // if the type is never instantiated, interfaces will be removed + if (@base.DeclaringType.IsInterface) + continue; + + // If the type is marked, we need to keep overrides of abstract members defined in assemblies + // that are copied. However, if the base method is virtual, then we don't need to keep the override + // until the type could be instantiated + if (!@base.IsAbstract) + continue; + + if (IgnoreScope (@base.DeclaringType.Scope)) + return true; + + if (IsVirtualNeededByTypeDueToPreservedScope (@base)) + return true; + } + + return false; + } + + bool IsVirtualNeededByInstantiatedTypeDueToPreservedScope (MethodDefinition method) + { + if (!method.IsVirtual) + return false; + + var base_list = Annotations.GetBaseMethods (method); + if (base_list is null) + return false; + + foreach (MethodDefinition @base in base_list) { + if (IgnoreScope (@base.DeclaringType.Scope)) + return true; + + if (IsVirtualNeededByTypeDueToPreservedScope (@base)) + return true; + } + + return false; + } + + static bool IsSpecialSerializationConstructor (MethodDefinition method) + { + if (!method.IsInstanceConstructor ()) + return false; + + var parameters = method.Parameters; + if (parameters.Count != 2) + return false; + + return parameters [0].ParameterType.Name == "SerializationInfo" && + parameters [1].ParameterType.Name == "StreamingContext"; + } + + protected void MarkMethodsIf (Collection methods, Func predicate) + { + foreach (MethodDefinition method in methods) + if (predicate (method)) + MarkMethod (method); + } + + protected MethodDefinition MarkMethodIf (Collection methods, Func predicate) + { + foreach (MethodDefinition method in methods) { + if (predicate (method)) { + return MarkMethod (method); + } + } + + return null; + } + + protected bool MarkDefaultConstructor (TypeDefinition type) + { + if (type?.HasMethods != true) + return false; + + return MarkMethodIf (type.Methods, MethodDefinitionExtensions.IsDefaultConstructor) is not null; + } + + static bool IsNonEmptyStaticConstructor (MethodDefinition method) + { + if (!method.IsStaticConstructor ()) + return false; + + if (!method.HasBody || !method.IsIL) + return true; + + if (method.Body.CodeSize != 1) + return true; + + return method.Body.Instructions [0].OpCode.Code != Code.Ret; + } + + static bool HasSerializationAttribute (MethodDefinition method) + { + if (!method.HasCustomAttributes) + return false; + foreach (var ca in method.CustomAttributes) { + var cat = ca.AttributeType; + if (cat.Namespace != "System.Runtime.Serialization") + continue; + switch (cat.Name) { + case "OnDeserializedAttribute": + case "OnDeserializingAttribute": + case "OnSerializedAttribute": + case "OnSerializingAttribute": + return true; + } + } + return false; + } + + protected virtual bool AlwaysMarkTypeAsInstantiated (TypeDefinition td) + { + switch (td.Name) { + // These types are created from native code which means we are unable to track when they are instantiated + // Since these are such foundational types, let's take the easy route and just always assume an instance of one of these + // could exist + case "Delegate": + case "MulticastDelegate": + case "ValueType": + case "Enum": + return td.Namespace == "System"; + } + + return false; + } + + void MarkEventSourceProviders (TypeDefinition td) + { + foreach (var nestedType in td.NestedTypes) { + if (BCL.EventTracingForWindows.IsProviderName (nestedType.Name)) + MarkStaticFields (nestedType); + } + } + + protected virtual void MarkMulticastDelegate (TypeDefinition type) + { + MarkMethodCollection (type.Methods); + } + + TypeDefinition ResolveFullyQualifiedTypeName (string name) + { + if (!TypeNameParser.TryParseTypeAssemblyQualifiedName (name, out string typeName, out string assemblyName)) + return null; + + foreach (var assemblyDefinition in _context.GetAssemblies ()) { + if (assemblyName is not null && assemblyDefinition.Name.Name != assemblyName) + continue; + + var foundType = assemblyDefinition.MainModule.GetType (typeName); + if (foundType is null) + continue; + + return foundType; + } + + return null; + } + + protected TypeReference GetOriginalType (TypeReference type) + { + while (type is TypeSpecification) { + if (type is GenericInstanceType git) + MarkGenericArguments (git); + + if (type is IModifierType mod) + MarkModifierType (mod); + + if (type is FunctionPointerType fnptr) { + MarkParameters (fnptr); + MarkType (fnptr.ReturnType); + break; // FunctionPointerType is the original type + } + + type = ((TypeSpecification) type).ElementType; + } + + return type; + } + + void MarkParameters (FunctionPointerType fnptr) + { + if (!fnptr.HasParameters) + return; + + for (int i = 0; i < fnptr.Parameters.Count; i++) { + MarkType (fnptr.Parameters [i].ParameterType); + } + } + + void MarkModifierType (IModifierType mod) + { + MarkType (mod.ModifierType); + } + + void MarkGenericArguments (IGenericInstance instance) + { + foreach (TypeReference argument in instance.GenericArguments) + MarkType (argument); + + MarkGenericArgumentConstructors (instance); + } + + void MarkGenericArgumentConstructors (IGenericInstance instance) + { + var arguments = instance.GenericArguments; + + var generic_element = GetGenericProviderFromInstance (instance); + if (generic_element is null) + return; + + var parameters = generic_element.GenericParameters; + + if (arguments.Count != parameters.Count) + return; + + for (int i = 0; i < arguments.Count; i++) { + var argument = arguments [i]; + var parameter = parameters [i]; + + if (!parameter.HasDefaultConstructorConstraint) + continue; + + var argument_definition = argument.Resolve (); + MarkDefaultConstructor (argument_definition); + } + } + + static IGenericParameterProvider GetGenericProviderFromInstance (IGenericInstance instance) + { + if (instance is GenericInstanceMethod method) + return method.ElementMethod.Resolve (); + + if (instance is GenericInstanceType type) + return type.ElementType.Resolve (); + + return null; + } + + void ApplyPreserveInfo (TypeDefinition type) + { + ApplyPreserveMethods (type); + + if (!Annotations.TryGetPreserve (type, out TypePreserve preserve)) + return; + + switch (preserve) { + case TypePreserve.All: + MarkFields (type, true); + MarkMethods (type); + break; + case TypePreserve.Fields: + if (!MarkFields (type, true, true)) + _context.LogMessage ($"Type {type.FullName} has no fields to preserve"); + break; + case TypePreserve.Methods: + if (!MarkMethods (type)) + _context.LogMessage ($"Type {type.FullName} has no methods to preserve"); + break; + } + } + + void ApplyPreserveMethods (TypeDefinition type) + { + var list = Annotations.GetPreservedMethods (type); + if (list is null) + return; + + MarkMethodCollection (list); + } + + void ApplyPreserveMethods (MethodDefinition method) + { + var list = Annotations.GetPreservedMethods (method); + if (list is null) + return; + + MarkMethodCollection (list); + } + + protected bool MarkFields (TypeDefinition type, bool includeStatic, bool markBackingFieldsOnlyIfPropertyMarked = false) + { + if (!type.HasFields) + return false; + + foreach (FieldDefinition field in type.Fields) { + if (!includeStatic && field.IsStatic) + continue; + + if (markBackingFieldsOnlyIfPropertyMarked && field.Name.EndsWith (">k__BackingField", StringComparison.Ordinal)) { + // We can't reliably construct the expected property name from the backing field name for all compilers + // because csc shortens the name of the backing field in some cases + // For example: + // Field Name = .Bar>k__BackingField + // Property Name = IFoo.Bar + // + // instead we will search the properties and find the one that makes use of the current backing field + var propertyDefinition = SearchPropertiesForMatchingFieldDefinition (field); + if (propertyDefinition is not null && !Annotations.IsMarked (propertyDefinition)) + continue; + } + MarkField (field); + } + + return true; + } + + static PropertyDefinition SearchPropertiesForMatchingFieldDefinition (FieldDefinition field) + { + foreach (var property in field.DeclaringType.Properties) { + var instr = property.GetMethod?.Body?.Instructions; + if (instr is null) + continue; + + foreach (var ins in instr) { + if (ins?.Operand == field) + return property; + } + } + + return null; + } + + protected void MarkStaticFields (TypeDefinition type) + { + if (!type.HasFields) + return; + + foreach (FieldDefinition field in type.Fields) { + if (field.IsStatic) + MarkField (field); + } + } + + protected virtual bool MarkMethods (TypeDefinition type) + { + if (!type.HasMethods) + return false; + + MarkMethodCollection (type.Methods); + return true; + } + + void MarkMethodCollection (IList methods) + { + foreach (MethodDefinition method in methods) + MarkMethod (method); + } + + protected void MarkIndirectlyCalledMethod (MethodDefinition method) + { + MarkMethod (method); + Annotations.MarkIndirectlyCalledMethod (method); + } + + protected virtual MethodDefinition MarkMethod (MethodReference reference) + { + reference = GetOriginalMethod (reference); + + if (reference.DeclaringType is ArrayType) + return null; + + Tracer.Push (reference); + if (reference.DeclaringType is GenericInstanceType) + MarkType (reference.DeclaringType); + + // if (IgnoreScope (reference.DeclaringType.Scope)) + // return; + + MethodDefinition method = reference.Resolve (); + + try { + if (method is null) { + HandleUnresolvedMethod (reference); + return null; + } + + if (Annotations.GetAction (method) == MethodAction.Nothing) + Annotations.SetAction (method, MethodAction.Parse); + + EnqueueMethod (method); + } finally { + Tracer.Pop (); + } + Tracer.AddDependency (method); + + return method; + } + + AssemblyDefinition ResolveAssembly (IMetadataScope scope) + { + AssemblyDefinition assembly = _context.Resolve (scope); + MarkAssembly (assembly); + return assembly; + } + + protected MethodReference GetOriginalMethod (MethodReference method) + { + while (method is MethodSpecification) { + if (method is GenericInstanceMethod gim) + MarkGenericArguments (gim); + + method = ((MethodSpecification) method).ElementMethod; + } + + return method; + } + + protected virtual void ProcessMethod (MethodDefinition method) + { + if (CheckProcessed (method)) + return; + + Tracer.Push (method); + MarkType (method.DeclaringType); + MarkCustomAttributes (method); + MarkSecurityDeclarations (method); + + MarkGenericParameterProvider (method); + + if (ShouldMarkAsInstancePossible (method)) + MarkRequirementsForInstantiatedTypes (method.DeclaringType); + + if (method.IsConstructor) { + if (!Annotations.ProcessSatelliteAssemblies && KnownMembers.IsSatelliteAssemblyMarker (method)) + Annotations.ProcessSatelliteAssemblies = true; + } else if (method.IsPropertyMethod ()) + MarkProperty (method.GetProperty ()); + else if (method.IsEventMethod ()) + MarkEvent (method.GetEvent ()); + + if (method.HasParameters) { + foreach (ParameterDefinition pd in method.Parameters) { + MarkType (pd.ParameterType); + MarkCustomAttributes (pd); + MarkMarshalSpec (pd); + } + } + + if (method.HasOverrides) { + foreach (MethodReference ov in method.Overrides) { + MarkMethod (ov); + MarkExplicitInterfaceImplementation (method, ov); + } + } + + MarkMethodSpecialCustomAttributes (method); + + if (method.IsVirtual) + _virtual_methods.Add (method); + + MarkNewCodeDependencies (method); + + MarkBaseMethods (method); + + MarkType (method.ReturnType); + MarkCustomAttributes (method.MethodReturnType); + MarkMarshalSpec (method.MethodReturnType); + + if (method.IsPInvokeImpl || method.IsInternalCall) { + ProcessInteropMethod (method); + } + + if (ShouldParseMethodBody (method)) + MarkMethodBody (method.Body); + + DoAdditionalMethodProcessing (method); + + Annotations.Mark (method); + + ApplyPreserveMethods (method); + Tracer.Pop (); + } + + // Allow subclassers to mark additional things when marking a method + protected virtual void DoAdditionalMethodProcessing (MethodDefinition method) + { + } + + protected virtual bool ShouldMarkAsInstancePossible (MethodDefinition method) + { + // We don't need to mark it multiple times + if (Annotations.IsInstantiated (method.DeclaringType)) + return false; + + if (method.IsInstanceConstructor ()) + return true; + + if (method.DeclaringType.IsInterface) + return true; + + return false; + } + + protected virtual void MarkRequirementsForInstantiatedTypes (TypeDefinition type) + { + if (Annotations.IsInstantiated (type)) + return; + + Annotations.MarkInstantiated (type); + + MarkInterfaceImplementations (type); + + foreach (var method in GetRequiredMethodsForInstantiatedType (type)) + MarkMethod (method); + + DoAdditionalInstantiatedTypeProcessing (type); + } + + /// + /// Collect methods that must be marked once a type is determined to be instantiated. + /// + /// This method is virtual in order to give derived mark steps an opportunity to modify the collection of methods that are needed + /// + /// + /// + protected virtual IEnumerable GetRequiredMethodsForInstantiatedType (TypeDefinition type) + { + foreach (var method in type.Methods) { + if (method.IsFinalizer () || IsVirtualNeededByInstantiatedTypeDueToPreservedScope (method)) + yield return method; + } + } + + void MarkExplicitInterfaceImplementation (MethodDefinition method, MethodReference ov) + { + var resolvedOverride = ov.Resolve (); + + if (resolvedOverride is null) { + HandleUnresolvedMethod (ov); + return; + } + + if (resolvedOverride.DeclaringType.IsInterface) { + foreach (var ifaceImpl in method.DeclaringType.Interfaces) { + var resolvedInterfaceType = ifaceImpl.InterfaceType.Resolve (); + if (resolvedInterfaceType is null) { + HandleUnresolvedType (ifaceImpl.InterfaceType); + continue; + } + + if (resolvedInterfaceType == resolvedOverride.DeclaringType) { + MarkInterfaceImplementation (ifaceImpl); + return; + } + } + } + } + + void MarkNewCodeDependencies (MethodDefinition method) + { + switch (Annotations.GetAction (method)) { + case MethodAction.ConvertToStub: + if (!method.IsInstanceConstructor ()) + return; + + var baseType = method.DeclaringType.BaseType.Resolve (); + if (!MarkDefaultConstructor (baseType)) + throw new NotSupportedException ($"Cannot stub constructor on '{method.DeclaringType}' when base type does not have default constructor"); + + break; + + case MethodAction.ConvertToThrow: + MarkAndCacheConvertToThrowExceptionCtor (); + break; + } + } + + protected virtual void MarkAndCacheConvertToThrowExceptionCtor () + { + if (_context.MarkedKnownMembers.NotSupportedExceptionCtorString is not null) + return; + + var nse = BCL.FindPredefinedType ("System", "NotSupportedException", _context); + if (nse is null) + throw new NotSupportedException ("Missing predefined 'System.NotSupportedException' type"); + + MarkType (nse); + + var nseCtor = MarkMethodIf (nse.Methods, KnownMembers.IsNotSupportedExceptionCtorString); + if (nseCtor is null) + throw new MarkException ($"Could not find constructor on '{nse.FullName}'"); + + _context.MarkedKnownMembers.NotSupportedExceptionCtorString = nseCtor; + + var objectType = BCL.FindPredefinedType ("System", "Object", _context); + if (objectType is null) + throw new NotSupportedException ("Missing predefined 'System.Object' type"); + + MarkType (objectType); + + var objectCtor = MarkMethodIf (objectType.Methods, MethodDefinitionExtensions.IsDefaultConstructor); + if (objectCtor is null) + throw new MarkException ($"Could not find constructor on '{objectType.FullName}'"); + + _context.MarkedKnownMembers.ObjectCtor = objectCtor; + } + + bool MarkDisablePrivateReflectionAttribute () + { + if (_context.MarkedKnownMembers.DisablePrivateReflectionAttributeCtor is not null) + return false; + + var nse = BCL.FindPredefinedType ("System.Runtime.CompilerServices", "DisablePrivateReflectionAttribute", _context); + if (nse is null) + throw new NotSupportedException ("Missing predefined 'System.Runtime.CompilerServices.DisablePrivateReflectionAttribute' type"); + + MarkType (nse); + + var ctor = MarkMethodIf (nse.Methods, MethodDefinitionExtensions.IsDefaultConstructor); + if (ctor is null) + throw new MarkException ($"Could not find constructor on '{nse.FullName}'"); + + _context.MarkedKnownMembers.DisablePrivateReflectionAttributeCtor = ctor; + return true; + } + + void MarkBaseMethods (MethodDefinition method) + { + var base_methods = Annotations.GetBaseMethods (method); + if (base_methods is null) + return; + + foreach (MethodDefinition base_method in base_methods) { + if (base_method.DeclaringType.IsInterface && !method.DeclaringType.IsInterface) + continue; + + MarkMethod (base_method); + MarkBaseMethods (base_method); + } + } + + void ProcessInteropMethod (MethodDefinition method) + { + TypeDefinition returnTypeDefinition = method.ReturnType.Resolve (); + + const bool includeStaticFields = false; + if (returnTypeDefinition is not null && !returnTypeDefinition.IsImport) { + MarkDefaultConstructor (returnTypeDefinition); + MarkFields (returnTypeDefinition, includeStaticFields); + } + + if (method.HasThis && !method.DeclaringType.IsImport) { + MarkFields (method.DeclaringType, includeStaticFields); + } + + foreach (ParameterDefinition pd in method.Parameters) { + TypeReference paramTypeReference = pd.ParameterType; + if (paramTypeReference is TypeSpecification) { + paramTypeReference = (paramTypeReference as TypeSpecification).ElementType; + } + TypeDefinition paramTypeDefinition = paramTypeReference.Resolve (); + if (paramTypeDefinition is not null && !paramTypeDefinition.IsImport) { + MarkFields (paramTypeDefinition, includeStaticFields); + if (pd.ParameterType.IsByReference) { + MarkDefaultConstructor (paramTypeDefinition); + } + } + } + } + + protected virtual bool ShouldParseMethodBody (MethodDefinition method) + { + if (!method.HasBody) + return false; + + switch (Annotations.GetAction (method)) { + case MethodAction.ForceParse: + return true; + case MethodAction.Parse: + AssemblyDefinition assembly = ResolveAssembly (method.DeclaringType.Scope); + switch (Annotations.GetAction (assembly)) { + case AssemblyAction.Link: + case AssemblyAction.Copy: + case AssemblyAction.CopyUsed: + case AssemblyAction.AddBypassNGen: + case AssemblyAction.AddBypassNGenUsed: + return true; + default: + return false; + } + default: + return false; + } + } + + protected void MarkProperty (PropertyDefinition prop) + { + MarkCustomAttributes (prop); + DoAdditionalPropertyProcessing (prop); + } + + protected virtual void MarkEvent (EventDefinition evt) + { + MarkCustomAttributes (evt); + MarkMethodIfNotNull (evt.AddMethod); + MarkMethodIfNotNull (evt.InvokeMethod); + MarkMethodIfNotNull (evt.RemoveMethod); + DoAdditionalEventProcessing (evt); + } + + void MarkMethodIfNotNull (MethodReference method) + { + if (method is null) + return; + + MarkMethod (method); + } + + protected virtual void MarkMethodBody (MethodBody body) + { + if (_context.IsOptimizationEnabled (CodeOptimizations.UnreachableBodies) && IsUnreachableBody (body)) { + MarkAndCacheConvertToThrowExceptionCtor (); + _unreachableBodies.Add (body); + return; + } + + foreach (VariableDefinition var in body.Variables) + MarkType (var.VariableType); + + foreach (ExceptionHandler eh in body.ExceptionHandlers) + if (eh.HandlerType == ExceptionHandlerType.Catch) + MarkType (eh.CatchType); + + foreach (Instruction instruction in body.Instructions) + MarkInstruction (instruction); + + MarkInterfacesNeededByBodyStack (body); + + MarkReflectionLikeDependencies (body); + + PostMarkMethodBody (body); + } + + bool IsUnreachableBody (MethodBody body) + { + return !body.Method.IsStatic + && !Annotations.IsInstantiated (body.Method.DeclaringType) + && MethodBodyScanner.IsWorthConvertingToThrow (body); + } + + + partial void PostMarkMethodBody (MethodBody body); + + void MarkInterfacesNeededByBodyStack (MethodBody body) + { + // If a type could be on the stack in the body and an interface it implements could be on the stack on the body + // then we need to mark that interface implementation. When this occurs it is not safe to remove the interface implementation from the type + // even if the type is never instantiated + var implementations = MethodBodyScanner.GetReferencedInterfaces (_context.Annotations, body); + if (implementations is null) + return; + + foreach (var implementation in implementations) + MarkInterfaceImplementation (implementation); + } + + protected virtual void MarkInstruction (Instruction instruction) + { + switch (instruction.OpCode.OperandType) { + case OperandType.InlineField: + MarkField ((FieldReference) instruction.Operand); + break; + case OperandType.InlineMethod: + MarkMethod ((MethodReference) instruction.Operand); + break; + case OperandType.InlineTok: + object token = instruction.Operand; + if (token is TypeReference) + MarkType ((TypeReference) token); + else if (token is MethodReference) + MarkMethod ((MethodReference) token); + else + MarkField ((FieldReference) token); + break; + case OperandType.InlineType: + MarkType ((TypeReference) instruction.Operand); + break; + default: + break; + } + } + + protected virtual void HandleUnresolvedType (TypeReference reference) + { + if (!_context.IgnoreUnresolved) { + throw new ResolutionException (reference); + } + } + + protected virtual void HandleUnresolvedMethod (MethodReference reference) + { + if (!_context.IgnoreUnresolved) { + throw new ResolutionException (reference); + } + } + + protected virtual void HandleUnresolvedField (FieldReference reference) + { + if (!_context.IgnoreUnresolved) { + throw new ResolutionException (reference); + } + } + + protected virtual bool ShouldMarkInterfaceImplementation (TypeDefinition type, InterfaceImplementation iface, TypeDefinition resolvedInterfaceType) + { + if (Annotations.IsMarked (iface)) + return false; + + if (Annotations.IsMarked (resolvedInterfaceType)) + return true; + + if (!_context.IsOptimizationEnabled (CodeOptimizations.UnusedInterfaces)) + return true; + + // It's hard to know if a com or windows runtime interface will be needed from managed code alone, + // so as a precaution we will mark these interfaces once the type is instantiated + if (resolvedInterfaceType.IsImport || resolvedInterfaceType.IsWindowsRuntime) + return true; + + return IsFullyPreserved (type); + } + + protected virtual void MarkInterfaceImplementation (InterfaceImplementation iface) + { + MarkCustomAttributes (iface); + MarkType (iface.InterfaceType); + Annotations.Mark (iface); + } + + bool HasManuallyTrackedDependency (MethodBody methodBody) + { + return PreserveDependencyLookupStep.HasPreserveDependencyAttribute (methodBody.Method); + } + + // + // Extension point for reflection logic handling customization + // + protected virtual bool ProcessReflectionDependency (MethodBody body, Instruction instruction) + { + return false; + } + + // + // Tries to mark additional dependencies used in reflection like calls (e.g. typeof (MyClass).GetField ("fname")) + // + protected virtual void MarkReflectionLikeDependencies (MethodBody body) + { + if (HasManuallyTrackedDependency (body)) + return; + + var instructions = body.Instructions; + ReflectionPatternDetector detector = new ReflectionPatternDetector (this, body.Method); + + // + // Starting at 1 because all patterns require at least 1 instruction backward lookup + // + for (var i = 1; i < instructions.Count; i++) { + var instruction = instructions [i]; + + if (instruction.OpCode != OpCodes.Call && instruction.OpCode != OpCodes.Callvirt) + continue; + + if (ProcessReflectionDependency (body, instruction)) + continue; + + if (!(instruction.Operand is MethodReference methodCalled)) + continue; + + var methodCalledDefinition = methodCalled.Resolve (); + if (methodCalledDefinition is null) + continue; + + ReflectionPatternContext reflectionContext = new ReflectionPatternContext (_context, body.Method, methodCalledDefinition, i); + try { + detector.Process (ref reflectionContext); + } finally { + reflectionContext.Dispose (); + } + } + } + + /// + /// Helper struct to pass around context information about reflection pattern + /// as a single parameter (and have a way to extend this in the future if we need to easily). + /// Also implements a simple validation mechanism to check that the code does report patter recognition + /// results for all methods it works on. + /// The promise of the pattern recorder is that for a given reflection method, it will either not talk + /// about it ever, or it will always report recognized/unrecognized. + /// + struct ReflectionPatternContext : IDisposable { + readonly LinkContext _context; +#if DEBUG + bool _patternAnalysisAttempted; + bool _patternReported; +#endif + + public MethodDefinition MethodCalling { get; private set; } + public MethodDefinition MethodCalled { get; private set; } + public int InstructionIndex { get; private set; } + + public ReflectionPatternContext (LinkContext context, MethodDefinition methodCalling, MethodDefinition methodCalled, int instructionIndex) + { + _context = context; + MethodCalling = methodCalling; + MethodCalled = methodCalled; + InstructionIndex = instructionIndex; + +#if DEBUG + _patternAnalysisAttempted = false; + _patternReported = false; +#endif + } + + [Conditional ("DEBUG")] + public void AnalyzingPattern () + { +#if DEBUG + _patternAnalysisAttempted = true; +#endif + } + + public void RecordRecognizedPattern (T accessedItem, Action mark) + where T : IMemberDefinition + { +#if DEBUG + Debug.Assert (_patternAnalysisAttempted, "To correctly report all patterns, when starting to analyze a pattern the AnalyzingPattern must be called first."); + _patternReported = true; +#endif + + _context.Tracer.Push ($"Reflection-{accessedItem}"); + try { + mark (); + _context.ReflectionPatternRecorder.RecognizedReflectionAccessPattern (MethodCalling, MethodCalled, accessedItem); + } finally { + _context.Tracer.Pop (); + } + } + + public void RecordUnrecognizedPattern (string message) + { +#if DEBUG + Debug.Assert (_patternAnalysisAttempted, "To correctly report all patterns, when starting to analyze a pattern the AnalyzingPattern must be called first."); + _patternReported = true; +#endif + + _context.ReflectionPatternRecorder.UnrecognizedReflectionAccessPattern (MethodCalling, MethodCalled, message); + } + + public void Dispose () + { +#if DEBUG + Debug.Assert (!_patternAnalysisAttempted || _patternReported, "A reflection pattern was analyzed, but no result was reported."); +#endif + } + } + + class ReflectionPatternDetector { + readonly MarkStep _markStep; + readonly MethodDefinition _methodCalling; + readonly Collection _instructions; + + public ReflectionPatternDetector (MarkStep markStep, MethodDefinition callingMethod) + { + _markStep = markStep; + _methodCalling = callingMethod; + _instructions = _methodCalling.Body.Instructions; + } + + public void Process (ref ReflectionPatternContext reflectionContext) + { + var methodCalled = reflectionContext.MethodCalled; + var instructionIndex = reflectionContext.InstructionIndex; + var methodCalledType = methodCalled.DeclaringType; + + switch (methodCalledType.Name) { + // + // System.Type + // + case "Type" when methodCalledType.Namespace == "System": + + // Some of the overloads are implemented by calling another overload of the same name. + // These "internal" calls are not interesting to analyze, the outermost call is the one + // which needs to be analyzed. The assumption is that all overloads have the same semantics. + // (for example that all overload of GetConstructor if used require the specified type to have a .ctor). + if (_methodCalling.DeclaringType == methodCalled.DeclaringType && _methodCalling.Name == methodCalled.Name) + break; + + switch (methodCalled.Name) { + // + // GetConstructor (Type []) + // GetConstructor (BindingFlags, Binder, Type [], ParameterModifier []) + // GetConstructor (BindingFlags, Binder, CallingConventions, Type [], ParameterModifier []) + // + case "GetConstructor": + if (!methodCalled.IsStatic) + ProcessSystemTypeGetMemberLikeCall (ref reflectionContext, System.Reflection.MemberTypes.Constructor, instructionIndex - 1); + + break; + + // + // GetMethod (string) + // GetMethod (string, BindingFlags) + // GetMethod (string, Type[]) + // GetMethod (string, Type[], ParameterModifier[]) + // GetMethod (string, BindingFlags, Binder, Type[], ParameterModifier[]) + // GetMethod (string, BindingFlags, Binder, CallingConventions, Type[], ParameterModifier[]) + // + // TODO: .NET Core extensions + // GetMethod (string, int, Type[]) + // GetMethod (string, int, Type[], ParameterModifier[]?) + // GetMethod (string, int, BindingFlags, Binder?, Type[], ParameterModifier[]?) + // GetMethod (string, int, BindingFlags, Binder?, CallingConventions, Type[], ParameterModifier[]?) + // + case "GetMethod": + if (!methodCalled.IsStatic) + ProcessSystemTypeGetMemberLikeCall (ref reflectionContext, System.Reflection.MemberTypes.Method, instructionIndex - 1); + + break; + + // + // GetField (string) + // GetField (string, BindingFlags) + // + case "GetField": + if (!methodCalled.IsStatic) + ProcessSystemTypeGetMemberLikeCall (ref reflectionContext, System.Reflection.MemberTypes.Field, instructionIndex - 1); + + break; + + // + // GetEvent (string) + // GetEvent (string, BindingFlags) + // + case "GetEvent": + if (!methodCalled.IsStatic) + ProcessSystemTypeGetMemberLikeCall (ref reflectionContext, System.Reflection.MemberTypes.Event, instructionIndex - 1); + + break; + + // + // GetProperty (string) + // GetProperty (string, BindingFlags) + // GetProperty (string, Type) + // GetProperty (string, Type[]) + // GetProperty (string, Type, Type[]) + // GetProperty (string, Type, Type[], ParameterModifier[]) + // GetProperty (string, BindingFlags, Binder, Type, Type[], ParameterModifier[]) + // + case "GetProperty": + if (!methodCalled.IsStatic) + ProcessSystemTypeGetMemberLikeCall (ref reflectionContext, System.Reflection.MemberTypes.Property, instructionIndex - 1); + + break; + + // + // GetType (string) + // GetType (string, Boolean) + // GetType (string, Boolean, Boolean) + // GetType (string, Func, Func) + // GetType (string, Func, Func, Boolean) + // GetType (string, Func, Func, Boolean, Boolean) + // + case "GetType": + if (!methodCalled.IsStatic) { + break; + } else { + reflectionContext.AnalyzingPattern (); + + var first_arg_instr = GetInstructionAtStackDepth (_instructions, instructionIndex - 1, methodCalled.Parameters.Count); + if (first_arg_instr < 0) { + reflectionContext.RecordUnrecognizedPattern ($"Reflection call '{methodCalled.FullName}' inside '{_methodCalling.FullName}' couldn't be decomposed"); + break; + } + + // + // The next value must be string constant (we don't handle anything else) + // + var first_arg = _instructions [first_arg_instr]; + if (first_arg.OpCode != OpCodes.Ldstr) { + reflectionContext.RecordUnrecognizedPattern ($"Reflection call '{methodCalled.FullName}' inside '{_methodCalling.FullName}' was detected with argument which cannot be analyzed"); + break; + } + + string typeName = (string) first_arg.Operand; + TypeDefinition foundType = _markStep.ResolveFullyQualifiedTypeName (typeName); + if (foundType is null) { + reflectionContext.RecordUnrecognizedPattern ($"Reflection call '{methodCalled.FullName}' inside '{_methodCalling.FullName}' was detected with type name `{typeName}` which can't be resolved."); + break; + } + + reflectionContext.RecordRecognizedPattern (foundType, () => _markStep.MarkType (foundType)); + } + break; + } + + break; + + // + // System.Linq.Expressions.Expression + // + case "Expression" when methodCalledType.Namespace == "System.Linq.Expressions": + Instruction second_argument; + TypeDefinition declaringType; + + if (!methodCalled.IsStatic) + break; + + switch (methodCalled.Name) { + + // + // static Call (Type, String, Type[], Expression[]) + // + case "Call": { + reflectionContext.AnalyzingPattern (); + + var first_arg_instr = GetInstructionAtStackDepth (_instructions, instructionIndex - 1, 4); + if (first_arg_instr < 0) { + reflectionContext.RecordUnrecognizedPattern ($"Expression call '{methodCalled.FullName}' inside '{_methodCalling.FullName}' couldn't be decomposed"); + break; + } + + var first_arg = _instructions [first_arg_instr]; + if (first_arg.OpCode == OpCodes.Ldtoken) + first_arg_instr++; + + declaringType = FindReflectionTypeForLookup (_instructions, first_arg_instr); + if (declaringType is null) { + reflectionContext.RecordUnrecognizedPattern ($"Expression call '{methodCalled.FullName}' inside '{_methodCalling.FullName}' was detected with 1st argument which cannot be analyzed"); + break; + } + + var second_arg_instr = GetInstructionAtStackDepth (_instructions, instructionIndex - 1, 3); + second_argument = _instructions [second_arg_instr]; + if (second_argument.OpCode != OpCodes.Ldstr) { + reflectionContext.RecordUnrecognizedPattern ($"Expression call '{methodCalled.FullName}' inside '{_methodCalling.FullName}' was detected with 2nd argument which cannot be analyzed"); + break; + } + + var name = (string) second_argument.Operand; + + MarkMethodsFromReflectionCall (ref reflectionContext, declaringType, name, null, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + } + + break; + + // + // static Property(Expression, Type, String) + // static Field (Expression, Type, String) + // + case "Property": + case "Field": { + reflectionContext.AnalyzingPattern (); + + var second_arg_instr = GetInstructionAtStackDepth (_instructions, instructionIndex - 1, 2); + if (second_arg_instr < 0) { + reflectionContext.RecordUnrecognizedPattern ($"Expression call '{methodCalled.FullName}' inside '{_methodCalling.FullName}' couldn't be decomposed"); + break; + } + + var second_arg = _instructions [second_arg_instr]; + if (second_arg.OpCode == OpCodes.Ldtoken) + second_arg_instr++; + + declaringType = FindReflectionTypeForLookup (_instructions, second_arg_instr); + if (declaringType is null) { + reflectionContext.RecordUnrecognizedPattern ($"Expression call '{methodCalled.FullName}' inside '{_methodCalling.FullName}' was detected with 2nd argument which cannot be analyzed"); + break; + } + + var third_arg_inst = GetInstructionAtStackDepth (_instructions, instructionIndex - 1, 1); + var third_argument = _instructions [third_arg_inst]; + if (third_argument.OpCode != OpCodes.Ldstr) { + reflectionContext.RecordUnrecognizedPattern ($"Expression call '{methodCalled.FullName}' inside '{_methodCalling.FullName}' was detected with the 3rd argument which cannot be analyzed"); + break; + } + + var name = (string) third_argument.Operand; + + // + // The first argument can be any expression but we are looking only for simple null + // which we can convert to static only field lookup + // + var first_arg_instr = GetInstructionAtStackDepth (_instructions, instructionIndex - 1, 3); + bool staticOnly = false; + + if (first_arg_instr >= 0) { + var first_arg = _instructions [first_arg_instr]; + if (first_arg.OpCode == OpCodes.Ldnull) + staticOnly = true; + } + + if (methodCalled.Name [0] == 'P') + MarkPropertiesFromReflectionCall (ref reflectionContext, declaringType, name, staticOnly); + else + MarkFieldsFromReflectionCall (ref reflectionContext, declaringType, name, staticOnly); + } + + break; + + // + // static New (Type) + // + case "New": { + reflectionContext.AnalyzingPattern (); + + var first_arg_instr = GetInstructionAtStackDepth (_instructions, instructionIndex - 1, 1); + if (first_arg_instr < 0) { + reflectionContext.RecordUnrecognizedPattern ($"Expression call '{methodCalled.FullName}' inside '{_methodCalling.FullName}' couldn't be decomposed"); + break; + } + + var first_arg = _instructions [first_arg_instr]; + if (first_arg.OpCode == OpCodes.Ldtoken) + first_arg_instr++; + + declaringType = FindReflectionTypeForLookup (_instructions, first_arg_instr); + if (declaringType is null) { + reflectionContext.RecordUnrecognizedPattern ($"Expression call '{methodCalled.FullName}' inside '{_methodCalling.FullName}' was detected with 1st argument which cannot be analyzed"); + break; + } + + MarkMethodsFromReflectionCall (ref reflectionContext, declaringType, ".ctor", 0, BindingFlags.Instance, parametersCount: 0); + } + break; + } + + break; + + // + // System.Reflection.RuntimeReflectionExtensions + // + case "RuntimeReflectionExtensions" when methodCalledType.Namespace == "System.Reflection": + switch (methodCalled.Name) { + // + // static GetRuntimeField (this Type type, string name) + // + case "GetRuntimeField": + ProcessSystemTypeGetMemberLikeCall (ref reflectionContext, System.Reflection.MemberTypes.Field, instructionIndex - 1, thisExtension: true); + break; + + // + // static GetRuntimeMethod (this Type type, string name, Type[] parameters) + // + case "GetRuntimeMethod": + ProcessSystemTypeGetMemberLikeCall (ref reflectionContext, System.Reflection.MemberTypes.Method, instructionIndex - 1, thisExtension: true); + break; + + // + // static GetRuntimeProperty (this Type type, string name) + // + case "GetRuntimeProperty": + ProcessSystemTypeGetMemberLikeCall (ref reflectionContext, System.Reflection.MemberTypes.Property, instructionIndex - 1, thisExtension: true); + break; + + // + // static GetRuntimeEvent (this Type type, string name) + // + case "GetRuntimeEvent": + ProcessSystemTypeGetMemberLikeCall (ref reflectionContext, System.Reflection.MemberTypes.Event, instructionIndex - 1, thisExtension: true); + break; + } + + break; + + // + // System.AppDomain + // + case "AppDomain" when methodCalledType.Namespace == "System": + // + // CreateInstance (string assemblyName, string typeName) + // CreateInstance (string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) + // CreateInstance (string assemblyName, string typeName, object? []? activationAttributes) + // + // CreateInstanceAndUnwrap (string assemblyName, string typeName) + // CreateInstanceAndUnwrap (string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) + // CreateInstanceAndUnwrap (string assemblyName, string typeName, object? []? activationAttributes) + // + // CreateInstanceFrom (string assemblyFile, string typeName) + // CreateInstanceFrom (string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) + // CreateInstanceFrom (string assemblyFile, string typeName, object? []? activationAttributes) + // + // CreateInstanceFromAndUnwrap (string assemblyFile, string typeName) + // CreateInstanceFromAndUnwrap (string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) + // CreateInstanceFromAndUnwrap (string assemblyFile, string typeName, object? []? activationAttributes) + // + switch (methodCalled.Name) { + case "CreateInstance": + case "CreateInstanceAndUnwrap": + case "CreateInstanceFrom": + case "CreateInstanceFromAndUnwrap": + ProcessActivatorCallWithStrings (ref reflectionContext, instructionIndex - 1, methodCalled.Parameters.Count < 4); + break; + } + + break; + + // + // System.Reflection.Assembly + // + case "Assembly" when methodCalledType.Namespace == "System.Reflection": + // + // CreateInstance (string typeName) + // CreateInstance (string typeName, bool ignoreCase) + // CreateInstance (string typeName, bool ignoreCase, BindingFlags bindingAttr, Binder? binder, object []? args, CultureInfo? culture, object []? activationAttributes) + // + if (methodCalled.Name == "CreateInstance") { + // + // TODO: This could be supported for `this` only calls + // + reflectionContext.AnalyzingPattern (); + reflectionContext.RecordUnrecognizedPattern ($"Activator call '{methodCalled.FullName}' inside '{_methodCalling.FullName}' is not yet supported"); + break; + } + + break; + + // + // System.Activator + // + case "Activator" when methodCalledType.Namespace == "System": + if (!methodCalled.IsStatic) + break; + + switch (methodCalled.Name) { + // + // static T CreateInstance () + // + case "CreateInstance" when methodCalled.ContainsGenericParameter: + // Not sure it's worth implementing as we cannot expant T and simple cases can be rewritten + reflectionContext.AnalyzingPattern (); + reflectionContext.RecordUnrecognizedPattern ($"Activator call '{methodCalled.FullName}' inside '{_methodCalling.FullName}' is not supported"); + break; + + // + // static CreateInstance (string assemblyName, string typeName) + // static CreateInstance (string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture, object?[]? activationAttributes) + // static CreateInstance (string assemblyName, string typeName, object?[]? activationAttributes) + // + // static CreateInstance (System.Type type) + // static CreateInstance (System.Type type, bool nonPublic) + // static CreateInstance (System.Type type, params object?[]? args) + // static CreateInstance (System.Type type, object?[]? args, object?[]? activationAttributes) + // static CreateInstance (System.Type type, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture) + // static CreateInstance (System.Type type, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture, object?[]? activationAttributes) { throw null; } + // + case "CreateInstance": { + reflectionContext.AnalyzingPattern (); + + var parameters = methodCalled.Parameters; + if (parameters.Count < 1) + break; + + if (parameters [0].ParameterType.MetadataType == MetadataType.String) { + ProcessActivatorCallWithStrings (ref reflectionContext, instructionIndex - 1, parameters.Count < 4); + break; + } + + var first_arg_instr = GetInstructionAtStackDepth (_instructions, instructionIndex - 1, methodCalled.Parameters.Count); + if (first_arg_instr < 0) { + reflectionContext.RecordUnrecognizedPattern ($"Activator call '{methodCalled.FullName}' inside '{_methodCalling.FullName}' couldn't be decomposed"); + break; + } + + if (parameters [0].ParameterType.IsTypeOf ("System", "Type")) { + declaringType = FindReflectionTypeForLookup (_instructions, first_arg_instr + 1); + if (declaringType is null) { + reflectionContext.RecordUnrecognizedPattern ($"Activator call '{methodCalled.FullName}' inside '{_methodCalling.FullName}' was detected with 1st argument expression which cannot be analyzed"); + break; + } + + BindingFlags bindingFlags = BindingFlags.Instance; + int? parametersCount = null; + + if (methodCalled.Parameters.Count == 1) { + parametersCount = 0; + } else { + var second_arg_instr = GetInstructionAtStackDepth (_instructions, instructionIndex - 1, methodCalled.Parameters.Count - 1); + second_argument = _instructions [second_arg_instr]; + switch (second_argument.OpCode.Code) { + case Code.Ldc_I4_0 when parameters [1].ParameterType.MetadataType == MetadataType.Boolean: + parametersCount = 0; + bindingFlags |= BindingFlags.Public; + break; + case Code.Ldc_I4_1 when parameters [1].ParameterType.MetadataType == MetadataType.Boolean: + parametersCount = 0; + break; + case Code.Ldc_I4_S when parameters [1].ParameterType.IsTypeOf ("System.Reflection", "BindingFlags"): + bindingFlags = (BindingFlags) (sbyte) second_argument.Operand; + break; + } + } + + MarkMethodsFromReflectionCall (ref reflectionContext, declaringType, ".ctor", 0, bindingFlags, parametersCount); + break; + } + } + + break; + + // + // static CreateInstanceFrom (string assemblyFile, string typeName) + // static CreateInstanceFrom (string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) + // static CreateInstanceFrom (string assemblyFile, string typeName, object? []? activationAttributes) + // + case "CreateInstanceFrom": + ProcessActivatorCallWithStrings (ref reflectionContext, instructionIndex - 1, methodCalled.Parameters.Count < 4); + break; + } + + break; + } + + } + + // + // Handles static method calls in form of Create (string assemblyFile, string typeName, ......) + // + void ProcessActivatorCallWithStrings (ref ReflectionPatternContext reflectionContext, int startIndex, bool defaultCtorOnly) + { + reflectionContext.AnalyzingPattern (); + + var parameters = reflectionContext.MethodCalled.Parameters; + if (parameters.Count < 2) { + reflectionContext.RecordUnrecognizedPattern ($"Activator call '{reflectionContext.MethodCalled.FullName}' inside '{_methodCalling.FullName}' is not supported"); + return; + } + + if (parameters [0].ParameterType.MetadataType != MetadataType.String && parameters [1].ParameterType.MetadataType != MetadataType.String) { + reflectionContext.RecordUnrecognizedPattern ($"Activator call '{reflectionContext.MethodCalled.FullName}' inside '{_methodCalling.FullName}' is not supported"); + return; + } + + var first_arg_instr = GetInstructionAtStackDepth (_instructions, startIndex, reflectionContext.MethodCalled.Parameters.Count); + if (first_arg_instr < 0) { + reflectionContext.RecordUnrecognizedPattern ($"Activator call '{reflectionContext.MethodCalled.FullName}' inside '{_methodCalling.FullName}' couldn't be decomposed"); + return; + } + + var first_arg = _instructions [first_arg_instr]; + if (first_arg.OpCode != OpCodes.Ldstr) { + reflectionContext.RecordUnrecognizedPattern ($"Activator call '{reflectionContext.MethodCalled.FullName}' inside '{_methodCalling.FullName}' was detected with the 1st argument which cannot be analyzed"); + return; + } + + var second_arg_instr = GetInstructionAtStackDepth (_instructions, startIndex, reflectionContext.MethodCalled.Parameters.Count - 1); + if (second_arg_instr < 0) { + reflectionContext.RecordUnrecognizedPattern ($"Activator call '{reflectionContext.MethodCalled.FullName}' inside '{_methodCalling.FullName}' couldn't be decomposed"); + return; + } + + var second_arg = _instructions [second_arg_instr]; + if (second_arg.OpCode != OpCodes.Ldstr) { + reflectionContext.RecordUnrecognizedPattern ($"Activator call '{reflectionContext.MethodCalled.FullName}' inside '{_methodCalling.FullName}' was detected with the 2nd argument which cannot be analyzed"); + return; + } + + string assembly_name = (string) first_arg.Operand; + if (!_markStep._context.Resolver.AssemblyCache.TryGetValue (assembly_name, out var assembly)) { + reflectionContext.RecordUnrecognizedPattern ($"Activator call '{reflectionContext.MethodCalled.FullName}' inside '{_methodCalling.FullName}' references assembly '{assembly_name}' which could not be found"); + return; + } + + string type_name = (string) second_arg.Operand; + var declaringType = FindType (assembly, type_name); + + if (declaringType is null) { + reflectionContext.RecordUnrecognizedPattern ($"Activator call '{reflectionContext.MethodCalled.FullName}' inside '{_methodCalling.FullName}' references type '{type_name}' which could not be found"); + return; + } + + MarkMethodsFromReflectionCall (ref reflectionContext, declaringType, ".ctor", 0, null, defaultCtorOnly ? 0 : (int?) null); + } + + // + // Handles instance methods called over typeof (Foo) with string name as the first argument + // + void ProcessSystemTypeGetMemberLikeCall (ref ReflectionPatternContext reflectionContext, System.Reflection.MemberTypes memberTypes, int startIndex, bool thisExtension = false) + { + reflectionContext.AnalyzingPattern (); + + int first_instance_arg = reflectionContext.MethodCalled.Parameters.Count; + if (thisExtension) + --first_instance_arg; + + var first_arg_instr = GetInstructionAtStackDepth (_instructions, startIndex, first_instance_arg); + if (first_arg_instr < 0) { + reflectionContext.RecordUnrecognizedPattern ($"Reflection call '{reflectionContext.MethodCalled.FullName}' inside '{_methodCalling.FullName}' couldn't be decomposed"); + return; + } + + var first_arg = _instructions [first_arg_instr]; + BindingFlags? bindingFlags = default; + string name = default; + + if (memberTypes == System.Reflection.MemberTypes.Constructor) { + if (first_arg.OpCode == OpCodes.Ldc_I4_S && reflectionContext.MethodCalled.Parameters.Count > 0 && reflectionContext.MethodCalled.Parameters [0].ParameterType.IsTypeOf ("System.Reflection", "BindingFlags")) { + bindingFlags = (BindingFlags) (sbyte) first_arg.Operand; + } + } else { + // + // The next value must be string constant (we don't handle anything else) + // + if (first_arg.OpCode != OpCodes.Ldstr) { + reflectionContext.RecordUnrecognizedPattern ($"Reflection call '{reflectionContext.MethodCalled.FullName}' inside '{_methodCalling.FullName}' was detected with argument which cannot be analyzed"); + return; + } + + name = (string) first_arg.Operand; + + var pos_arg = _instructions [first_arg_instr + 1]; + if (pos_arg.OpCode == OpCodes.Ldc_I4_S && reflectionContext.MethodCalled.Parameters.Count > 1 && reflectionContext.MethodCalled.Parameters [1].ParameterType.IsTypeOf ("System.Reflection", "BindingFlags")) { + bindingFlags = (BindingFlags) (sbyte) pos_arg.Operand; + } + } + + var declaringType = FindReflectionTypeForLookup (_instructions, first_arg_instr - 1); + if (declaringType is null) { + reflectionContext.RecordUnrecognizedPattern ($"Reflection call '{reflectionContext.MethodCalled.FullName}' inside '{_methodCalling.FullName}' does not use detectable instance type extraction"); + return; + } + + switch (memberTypes) { + case System.Reflection.MemberTypes.Constructor: + MarkMethodsFromReflectionCall (ref reflectionContext, declaringType, ".ctor", 0, bindingFlags); + break; + case System.Reflection.MemberTypes.Method: + MarkMethodsFromReflectionCall (ref reflectionContext, declaringType, name, 0, bindingFlags); + break; + case System.Reflection.MemberTypes.Field: + MarkFieldsFromReflectionCall (ref reflectionContext, declaringType, name); + break; + case System.Reflection.MemberTypes.Property: + MarkPropertiesFromReflectionCall (ref reflectionContext, declaringType, name); + break; + case System.Reflection.MemberTypes.Event: + MarkEventsFromReflectionCall (ref reflectionContext, declaringType, name); + break; + default: + Debug.Fail ("Unsupported member type"); + reflectionContext.RecordUnrecognizedPattern ($"Reflection call '{reflectionContext.MethodCalled.FullName}' inside '{_methodCalling.FullName}' is of unexpected member type."); + break; + } + } + + // + // arity is null for name match regardless of arity + // + void MarkMethodsFromReflectionCall (ref ReflectionPatternContext reflectionContext, TypeDefinition declaringType, string name, int? arity, BindingFlags? bindingFlags, int? parametersCount = null) + { + bool foundMatch = false; + foreach (var method in declaringType.Methods) { + var mname = method.Name; + + // Either exact match or generic method with any arity when unspecified + if (mname != name && !(arity is null && mname.StartsWith (name, StringComparison.Ordinal) && mname.Length > name.Length + 2 && mname [name.Length + 1] == '`')) { + continue; + } + + if ((bindingFlags & (BindingFlags.Instance | BindingFlags.Static)) == BindingFlags.Static && !method.IsStatic) + continue; + + if ((bindingFlags & (BindingFlags.Instance | BindingFlags.Static)) == BindingFlags.Instance && method.IsStatic) + continue; + + if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.Public && !method.IsPublic) + continue; + + if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.NonPublic && method.IsPublic) + continue; + + if (parametersCount is not null && parametersCount != method.Parameters.Count) + continue; + + foundMatch = true; + reflectionContext.RecordRecognizedPattern (method, () => _markStep.MarkIndirectlyCalledMethod (method)); + } + + if (!foundMatch) + reflectionContext.RecordUnrecognizedPattern ($"Reflection call '{reflectionContext.MethodCalled.FullName}' inside '{reflectionContext.MethodCalling.FullName}' could not resolve method `{name}` on type `{declaringType.FullName}`."); + } + + void MarkPropertiesFromReflectionCall (ref ReflectionPatternContext reflectionContext, TypeDefinition declaringType, string name, bool staticOnly = false) + { + bool foundMatch = false; + foreach (var property in declaringType.Properties) { + if (property.Name != name) + continue; + + bool markedAny = false; + + // It is not easy to reliably detect in the IL code whether the getter or setter (or both) are used. + // Be conservative and mark everything for the property. + var getter = property.GetMethod; + if (getter is not null && (!staticOnly || staticOnly && getter.IsStatic)) { + reflectionContext.RecordRecognizedPattern (getter, () => _markStep.MarkIndirectlyCalledMethod (getter)); + markedAny = true; + } + + var setter = property.SetMethod; + if (setter is not null && (!staticOnly || staticOnly && setter.IsStatic)) { + reflectionContext.RecordRecognizedPattern (setter, () => _markStep.MarkIndirectlyCalledMethod (setter)); + markedAny = true; + } + + if (markedAny) { + foundMatch = true; + reflectionContext.RecordRecognizedPattern (property, () => _markStep.MarkProperty (property)); + } + } + + if (!foundMatch) + reflectionContext.RecordUnrecognizedPattern ($"Reflection call '{reflectionContext.MethodCalled.FullName}' inside '{reflectionContext.MethodCalling.FullName}' could not resolve property `{name}` on type `{declaringType.FullName}`."); + } + + void MarkFieldsFromReflectionCall (ref ReflectionPatternContext reflectionContext, TypeDefinition declaringType, string name, bool staticOnly = false) + { + bool foundMatch = false; + foreach (var field in declaringType.Fields) { + if (field.Name != name) + continue; + + if (staticOnly && !field.IsStatic) + continue; + + foundMatch = true; + reflectionContext.RecordRecognizedPattern (field, () => _markStep.MarkField (field)); + break; + } + + if (!foundMatch) + reflectionContext.RecordUnrecognizedPattern ($"Reflection call '{reflectionContext.MethodCalled.FullName}' inside '{reflectionContext.MethodCalling.FullName}' could not resolve field `{name}` on type `{declaringType.FullName}`."); + } + + void MarkEventsFromReflectionCall (ref ReflectionPatternContext reflectionContext, TypeDefinition declaringType, string name) + { + bool foundMatch = false; + foreach (var eventInfo in declaringType.Events) { + if (eventInfo.Name != name) + continue; + + foundMatch = true; + reflectionContext.RecordRecognizedPattern (eventInfo, () => _markStep.MarkEvent (eventInfo)); + } + + if (!foundMatch) + reflectionContext.RecordUnrecognizedPattern ($"Reflection call '{reflectionContext.MethodCalled.FullName}' inside '{reflectionContext.MethodCalling.FullName}' could not resolve event `{name}` on type `{declaringType.FullName}`."); + } + } + + static int GetInstructionAtStackDepth (Collection instructions, int startIndex, int stackSizeToBacktrace) + { + for (int i = startIndex; i >= 0; --i) { + var instruction = instructions [i]; + + switch (instruction.OpCode.StackBehaviourPop) { + case StackBehaviour.Pop0: + break; + case StackBehaviour.Pop1: + case StackBehaviour.Popi: + case StackBehaviour.Popref: + stackSizeToBacktrace++; + break; + case StackBehaviour.Pop1_pop1: + case StackBehaviour.Popi_pop1: + case StackBehaviour.Popi_popi: + case StackBehaviour.Popi_popi8: + case StackBehaviour.Popi_popr4: + case StackBehaviour.Popi_popr8: + case StackBehaviour.Popref_pop1: + case StackBehaviour.Popref_popi: + stackSizeToBacktrace += 2; + break; + case StackBehaviour.Popref_popi_popi: + case StackBehaviour.Popref_popi_popi8: + case StackBehaviour.Popref_popi_popr4: + case StackBehaviour.Popref_popi_popr8: + case StackBehaviour.Popref_popi_popref: + stackSizeToBacktrace += 3; + break; + case StackBehaviour.Varpop: + switch (instruction.OpCode.Code) { + case Code.Call: + case Code.Calli: + case Code.Callvirt: + if (instruction.Operand is MethodReference mr) { + stackSizeToBacktrace += mr.Parameters.Count; + if (mr.Resolve ()?.IsStatic == false) + stackSizeToBacktrace++; + } + + break; + case Code.Newobj: + if (instruction.Operand is MethodReference ctor) { + stackSizeToBacktrace += ctor.Parameters.Count; + } + break; + case Code.Ret: + // TODO: Need method return type for correct stack size but this path should not be hit yet + break; + default: + return -3; + } + break; + } + + switch (instruction.OpCode.StackBehaviourPush) { + case StackBehaviour.Push0: + break; + case StackBehaviour.Push1: + case StackBehaviour.Pushi: + case StackBehaviour.Pushi8: + case StackBehaviour.Pushr4: + case StackBehaviour.Pushr8: + case StackBehaviour.Pushref: + stackSizeToBacktrace--; + break; + case StackBehaviour.Push1_push1: + stackSizeToBacktrace -= 2; + break; + case StackBehaviour.Varpush: + // + // Only call, calli, callvirt will hit this + // + if (instruction.Operand is MethodReference mr && mr.ReturnType.MetadataType != MetadataType.Void) { + stackSizeToBacktrace--; + } + break; + } + + if (stackSizeToBacktrace == 0) + return i; + + if (stackSizeToBacktrace < 0) + return -1; + } + + return -2; + } + + static TypeDefinition FindReflectionTypeForLookup (Collection instructions, int startIndex) + { + while (startIndex >= 1) { + int storeIndex = -1; + var instruction = instructions [startIndex]; + switch (instruction.OpCode.Code) { + // + // Pattern #1 + // + // typeof (Foo).ReflectionCall () + // + case Code.Call: + if (!(instruction.Operand is MethodReference mr) || mr.Name != "GetTypeFromHandle") + return null; + + var ldtoken = instructions [startIndex - 1]; + + if (ldtoken.OpCode != OpCodes.Ldtoken) + return null; + + return (ldtoken.Operand as TypeReference).Resolve (); + + // + // Patern #2 + // + // var temp = typeof (Foo); + // temp.ReflectionCall () + // + case Code.Ldloc_0: + storeIndex = GetIndexOfInstruction (instructions, OpCodes.Stloc_0, startIndex - 1); + startIndex = storeIndex - 1; + break; + case Code.Ldloc_1: + storeIndex = GetIndexOfInstruction (instructions, OpCodes.Stloc_1, startIndex - 1); + startIndex = storeIndex - 1; + break; + case Code.Ldloc_2: + storeIndex = GetIndexOfInstruction (instructions, OpCodes.Stloc_2, startIndex - 1); + startIndex = storeIndex - 1; + break; + case Code.Ldloc_3: + storeIndex = GetIndexOfInstruction (instructions, OpCodes.Stloc_3, startIndex - 1); + startIndex = storeIndex - 1; + break; + case Code.Ldloc_S: + storeIndex = GetIndexOfInstruction (instructions, OpCodes.Stloc_S, startIndex - 1, l => (VariableReference) l.Operand == (VariableReference) instruction.Operand); + startIndex = storeIndex - 1; + break; + case Code.Ldloc: + storeIndex = GetIndexOfInstruction (instructions, OpCodes.Stloc, startIndex - 1, l => (VariableReference) l.Operand == (VariableReference) instruction.Operand); + startIndex = storeIndex - 1; + break; + + case Code.Nop: + startIndex--; + break; + + default: + return null; + } + } + + return null; + } + + static int GetIndexOfInstruction (Collection instructions, OpCode opcode, int startIndex, Predicate comparer = null) + { + while (startIndex >= 0) { + var instr = instructions [startIndex]; + if (instr.OpCode == opcode && (comparer is null || comparer (instr))) + return startIndex; + + startIndex--; + } + + return -1; + } + + protected class AttributeProviderPair { + public AttributeProviderPair (CustomAttribute attribute, ICustomAttributeProvider provider) + { + Attribute = attribute; + Provider = provider; + } + + public CustomAttribute Attribute { get; private set; } + public ICustomAttributeProvider Provider { get; private set; } + } + } + + // Make our own copy of the BindingFlags enum, so that we don't depend on System.Reflection. + [Flags] + enum BindingFlags { + Default = 0, + IgnoreCase = 1, + DeclaredOnly = 2, + Instance = 4, + Static = 8, + Public = 16, + NonPublic = 32, + FlattenHierarchy = 64, + InvokeMethod = 256, + CreateInstance = 512, + GetField = 1024, + SetField = 2048, + GetProperty = 4096, + SetProperty = 8192, + PutDispProperty = 16384, + PutRefDispProperty = 32768, + ExactBinding = 65536, + SuppressChangeType = 131072, + OptionalParamBinding = 262144, + IgnoreReturn = 16777216 + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/OutputStep.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/OutputStep.cs new file mode 100644 index 000000000000..d4e52806c53d --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/OutputStep.cs @@ -0,0 +1,301 @@ +// +// OutputStep.cs +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// (C) 2006 Jb Evain +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using Mono.Cecil; +using Mono.Cecil.Cil; +using Mono.Cecil.PE; + +namespace Mono.Linker.Steps { + + public class OutputStep : BaseStep { + private static Dictionary architectureMap; + + private enum NativeOSOverride { + Apple = 0x4644, + FreeBSD = 0xadc4, + Linux = 0x7b79, + NetBSD = 0x1993, + Default = 0 + } + + List assembliesWritten; + + public OutputStep () + { + assembliesWritten = new List (); + } + + static TargetArchitecture CalculateArchitecture (TargetArchitecture readyToRunArch) + { + if (architectureMap is null) { + architectureMap = new Dictionary (); + foreach (var os in Enum.GetValues (typeof (NativeOSOverride))) { + ushort osVal = (ushort) (NativeOSOverride) os; + foreach (var arch in Enum.GetValues (typeof (TargetArchitecture))) { + ushort archVal = (ushort) (TargetArchitecture) arch; + architectureMap.Add ((ushort) (archVal ^ osVal), (TargetArchitecture) arch); + } + } + } + + TargetArchitecture pureILArch; + if (architectureMap.TryGetValue ((ushort) readyToRunArch, out pureILArch)) { + return pureILArch; + } + throw new BadImageFormatException ("unrecognized module attributes"); + } + + protected override void Process () + { + CheckOutputDirectory (); + Tracer.Finish (); + } + + protected override void EndProcess () + { + if (Context.AssemblyListFile is not null) { + using (var w = File.CreateText (Context.AssemblyListFile)) { + w.WriteLine ("[" + String.Join (", ", assembliesWritten.Select (a => "\"" + a + "\"").ToArray ()) + "]"); + } + } + } + + void CheckOutputDirectory () + { + if (Directory.Exists (Context.OutputDirectory)) + return; + + Directory.CreateDirectory (Context.OutputDirectory); + } + + protected override void ProcessAssembly (AssemblyDefinition assembly) + { + OutputAssembly (assembly); + } + + protected void WriteAssembly (AssemblyDefinition assembly, string directory) + { + WriteAssembly (assembly, directory, SaveSymbols (assembly)); + } + + protected virtual void WriteAssembly (AssemblyDefinition assembly, string directory, WriterParameters writerParameters) + { + foreach (var module in assembly.Modules) { + // Write back pure IL even for crossgen-ed assemblies + if (module.IsCrossgened ()) { + module.Attributes |= ModuleAttributes.ILOnly; + module.Attributes ^= ModuleAttributes.ILLibrary; + module.Architecture = CalculateArchitecture (module.Architecture); + } + } + + string outputName = GetAssemblyFileName (assembly, directory); + try { + assembly.Write (outputName, writerParameters); + } catch (Exception e) { + throw new OutputException ($"Failed to write '{outputName}", e); + } + } + + void OutputAssembly (AssemblyDefinition assembly) + { + string directory = Context.OutputDirectory; + + CopyConfigFileIfNeeded (assembly, directory); + + var action = Annotations.GetAction (assembly); + Context.LogMessage (MessageImportance.Low, $"Output action: {action,8} assembly: {assembly}"); + + switch (action) { + case AssemblyAction.Save: + case AssemblyAction.Link: + case AssemblyAction.AddBypassNGen: + Context.Tracer.AddDependency (assembly); + WriteAssembly (assembly, directory); + CopySatelliteAssembliesIfNeeded (assembly, directory); + assembliesWritten.Add (GetOriginalAssemblyFileInfo (assembly).Name); + break; + case AssemblyAction.Copy: + Context.Tracer.AddDependency (assembly); + CloseSymbols (assembly); + CopyAssembly (assembly, directory); + CopySatelliteAssembliesIfNeeded (assembly, directory); + assembliesWritten.Add (GetOriginalAssemblyFileInfo (assembly).Name); + break; + case AssemblyAction.Delete: + CloseSymbols (assembly); + DeleteAssembly (assembly, directory); + break; + default: + CloseSymbols (assembly); + break; + } + } + + protected virtual void DeleteAssembly (AssemblyDefinition assembly, string directory) + { + var target = GetAssemblyFileName (assembly, directory); + if (File.Exists (target)) { + File.Delete (target); + File.Delete (target + ".mdb"); + File.Delete (Path.ChangeExtension (target, "pdb")); + File.Delete (GetConfigFile (target)); + } + } + + void CloseSymbols (AssemblyDefinition assembly) + { + Annotations.CloseSymbolReader (assembly); + } + + WriterParameters SaveSymbols (AssemblyDefinition assembly) + { + var parameters = new WriterParameters { + DeterministicMvid = Context.DeterministicOutput + }; + + if (!Context.LinkSymbols) + return parameters; + + if (!assembly.MainModule.HasSymbols) + return parameters; + + // Use a string check to avoid a hard dependency on Mono.Cecil.Pdb + if (Environment.OSVersion.Platform != PlatformID.Win32NT && assembly.MainModule.SymbolReader.GetType ().FullName == "Mono.Cecil.Pdb.NativePdbReader") + return parameters; + + if (Context.SymbolWriterProvider is not null) + parameters.SymbolWriterProvider = Context.SymbolWriterProvider; + else + parameters.WriteSymbols = true; + return parameters; + } + + + void CopySatelliteAssembliesIfNeeded (AssemblyDefinition assembly, string directory) + { + if (!Annotations.ProcessSatelliteAssemblies) + return; + + FileInfo original = GetOriginalAssemblyFileInfo (assembly); + string resourceFile = GetAssemblyResourceFileName (original.FullName); + + foreach (var subDirectory in Directory.EnumerateDirectories (original.DirectoryName)) { + var satelliteAssembly = Path.Combine (subDirectory, resourceFile); + if (!File.Exists (satelliteAssembly)) + continue; + + string cultureName = subDirectory.Substring (subDirectory.LastIndexOf (Path.DirectorySeparatorChar) + 1); + string culturePath = Path.Combine (directory, cultureName); + + Directory.CreateDirectory (culturePath); + File.Copy (satelliteAssembly, Path.Combine (culturePath, resourceFile), true); + } + } + + void CopyConfigFileIfNeeded (AssemblyDefinition assembly, string directory) + { + string config = GetConfigFile (GetOriginalAssemblyFileInfo (assembly).FullName); + if (!File.Exists (config)) + return; + + string target = Path.GetFullPath (GetConfigFile (GetAssemblyFileName (assembly, directory))); + + if (config == target) + return; + + File.Copy (config, GetConfigFile (GetAssemblyFileName (assembly, directory)), true); + } + + static string GetAssemblyResourceFileName (string assembly) + { + return Path.GetFileNameWithoutExtension (assembly) + ".resources.dll"; + } + + static string GetConfigFile (string assembly) + { + return assembly + ".config"; + } + + static FileInfo GetOriginalAssemblyFileInfo (AssemblyDefinition assembly) + { + return new FileInfo (assembly.MainModule.FileName); + } + + protected virtual void CopyAssembly (AssemblyDefinition assembly, string directory) + { + // Special case. When an assembly has embedded pdbs, link symbols is not enabled, and the assembly's action is copy, + // we want to match the behavior of assemblies with the other symbol types and end up with an assembly that does not have symbols. + // In order to do that, we can't simply copy files. We need to write the assembly without symbols + if (assembly.MainModule.HasSymbols && !Context.LinkSymbols && assembly.MainModule.SymbolReader is EmbeddedPortablePdbReader) { + WriteAssembly (assembly, directory, new WriterParameters ()); + return; + } + + FileInfo fi = GetOriginalAssemblyFileInfo (assembly); + string target = Path.GetFullPath (Path.Combine (directory, fi.Name)); + string source = fi.FullName; + if (source == target) + return; + + CopyFileAndRemoveReadOnly (source, target); + + if (!Context.LinkSymbols) + return; + + var mdb = source + ".mdb"; + if (File.Exists (mdb)) + CopyFileAndRemoveReadOnly (mdb, target + ".mdb"); + + var pdb = Path.ChangeExtension (source, "pdb"); + if (File.Exists (pdb)) + CopyFileAndRemoveReadOnly (pdb, Path.ChangeExtension (target, "pdb")); + } + + static void CopyFileAndRemoveReadOnly (string src, string dest) + { + File.Copy (src, dest, true); + + System.IO.FileAttributes attrs = File.GetAttributes (dest); + + if ((attrs & System.IO.FileAttributes.ReadOnly) == System.IO.FileAttributes.ReadOnly) + File.SetAttributes (dest, attrs & ~System.IO.FileAttributes.ReadOnly); + } + + protected virtual string GetAssemblyFileName (AssemblyDefinition assembly, string directory) + { + string file = GetOriginalAssemblyFileInfo (assembly).Name; + return Path.Combine (directory, file); + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/PreserveDependencyLookupStep.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/PreserveDependencyLookupStep.cs new file mode 100644 index 000000000000..0ff917d71666 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/PreserveDependencyLookupStep.cs @@ -0,0 +1,99 @@ +// +// PreserveDependencyLookupStep.cs +// +// Author: +// Marek Safar (marek.safar@gmail.com) +// +// Copyright (C) 2018 Microsoft Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using Mono.Cecil; +using Mono.Collections.Generic; + +namespace Mono.Linker.Steps { + public class PreserveDependencyLookupStep : LoadReferencesStep { + protected override void ProcessAssembly (AssemblyDefinition assembly) + { + var module = assembly.MainModule; + + foreach (var type in module.Types) { + if (type.HasMethods) { + foreach (var method in type.GetMethods ()) { + var md = method.Resolve (); + if (md?.HasCustomAttributes != true) + continue; + + ProcessPreserveDependencyAttribute (md.CustomAttributes); + } + } + + if (type.HasFields) { + foreach (var field in type.Fields) { + var md = field.Resolve (); + if (md?.HasCustomAttributes != true) + continue; + + ProcessPreserveDependencyAttribute (md.CustomAttributes); + } + } + } + } + + public static bool IsPreserveDependencyAttribute (TypeReference tr) + { + return tr.Name == "PreserveDependencyAttribute" && tr.Namespace == "System.Runtime.CompilerServices"; + } + + public static bool HasPreserveDependencyAttribute (MethodDefinition method) + { + if (!method.HasCustomAttributes) + return false; + + foreach (var ca in method.CustomAttributes) { + if (IsPreserveDependencyAttribute (ca.AttributeType)) + return true; + } + + return false; + } + + void ProcessPreserveDependencyAttribute (Collection attributes) + { + foreach (var ca in attributes) { + if (!IsPreserveDependencyAttribute (ca.AttributeType)) + continue; + + if (ca.ConstructorArguments.Count != 3) + continue; + + var assemblyName = ca.ConstructorArguments [2].Value as string; + if (assemblyName is null) + continue; + + var newDependency = Context.Resolve (new AssemblyNameReference (assemblyName, new Version ())); + if (newDependency is not null) + ProcessReferences (newDependency); + } + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/RegenerateGuidStep.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/RegenerateGuidStep.cs new file mode 100644 index 000000000000..6b2558e8352d --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/RegenerateGuidStep.cs @@ -0,0 +1,48 @@ +// +// CleanStep.cs +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// (C) 2008 Novell, Inc. (http://www.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. +// + +using System; + +using Mono.Cecil; + +namespace Mono.Linker.Steps { + + public class RegenerateGuidStep : BaseStep { + + protected override void ProcessAssembly (AssemblyDefinition assembly) + { + if (Annotations.GetAction (assembly) == AssemblyAction.Link) + RegenerateGuid (assembly); + } + + static void RegenerateGuid (AssemblyDefinition asm) + { + asm.MainModule.Mvid = Guid.NewGuid (); + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/ResolveFromAssemblyStep.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/ResolveFromAssemblyStep.cs new file mode 100644 index 000000000000..94d317e9f153 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/ResolveFromAssemblyStep.cs @@ -0,0 +1,249 @@ +// +// ResolveFromAssemblyStep.cs +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// (C) 2006 Jb Evain +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using Mono.Cecil; +using Mono.Collections.Generic; + +namespace Mono.Linker.Steps { + + public class ResolveFromAssemblyStep : ResolveStep { + + AssemblyDefinition _assembly; + string _file; + RootVisibility _rootVisibility; + + public enum RootVisibility { + Any = 0, + PublicAndFamily = 1, + PublicAndFamilyAndAssembly = 2 + } + + + public ResolveFromAssemblyStep (string assembly, RootVisibility rootVisibility = RootVisibility.Any) + { + _file = assembly; + _rootVisibility = rootVisibility; + } + + public ResolveFromAssemblyStep (AssemblyDefinition assembly) + { + _assembly = assembly; + } + + protected override void Process () + { + if (_assembly is not null) + Context.Resolver.CacheAssembly (_assembly); + + var ignoreUnresolved = Context.Resolver.IgnoreUnresolved; + if (_rootVisibility == RootVisibility.PublicAndFamily) { + Context.Resolver.IgnoreUnresolved = false; + } + + AssemblyDefinition assembly = _assembly ?? Context.Resolve (_file); + Context.Resolver.IgnoreUnresolved = ignoreUnresolved; + if (_rootVisibility != RootVisibility.Any && HasInternalsVisibleTo (assembly)) { + _rootVisibility = RootVisibility.PublicAndFamilyAndAssembly; + } + + switch (assembly.MainModule.Kind) { + case ModuleKind.Dll: + ProcessLibrary (assembly); + break; + default: + ProcessExecutable (assembly); + break; + } + } + + protected virtual void ProcessLibrary (AssemblyDefinition assembly) + { + ProcessLibrary (Context, assembly, _rootVisibility); + } + + public static void ProcessLibrary (LinkContext context, AssemblyDefinition assembly, RootVisibility rootVisibility = RootVisibility.Any) + { + var action = rootVisibility == RootVisibility.Any ? AssemblyAction.Copy : AssemblyAction.Link; + context.SetAction (assembly, action); + + context.Tracer.Push (assembly); + + foreach (TypeDefinition type in assembly.MainModule.Types) + MarkType (context, type, rootVisibility); + + if (assembly.MainModule.HasExportedTypes) { + foreach (var exported in assembly.MainModule.ExportedTypes) { + bool isForwarder = exported.IsForwarder; + var declaringType = exported.DeclaringType; + while (!isForwarder && (declaringType is not null)) { + isForwarder = declaringType.IsForwarder; + declaringType = declaringType.DeclaringType; + } + + if (!isForwarder) + continue; + TypeDefinition resolvedExportedType = exported.Resolve (); + + if (resolvedExportedType is null) { + // + // It's quite common for assemblies to have broken exported types + // + // One source of them is from native csc which added all nested types of + // type-forwarded types automatically including private ones. + // + // Next source of broken type-forwarders is from custom metadata writers which + // simply write bogus information. + // + // Both cases are bugs not on our end but we still want to link all assemblies + // especially when such types cannot be used anyway + // + context.LogMessage ($"Cannot find declaration of exported type '{exported}' from the assembly '{assembly}'"); + + continue; + } + + context.Resolve (resolvedExportedType.Scope); + MarkType (context, resolvedExportedType, rootVisibility); + context.MarkingHelpers.MarkExportedType (exported, assembly.MainModule); + } + } + + context.Tracer.Pop (); + } + + static void MarkType (LinkContext context, TypeDefinition type, RootVisibility rootVisibility) + { + bool markType; + switch (rootVisibility) { + default: + markType = true; + break; + + case RootVisibility.PublicAndFamilyAndAssembly: + markType = !type.IsNestedPrivate; + break; + + case RootVisibility.PublicAndFamily: + markType = type.IsPublic || type.IsNestedPublic || type.IsNestedFamily || type.IsNestedFamilyOrAssembly; + break; + } + + if (!markType) { + return; + } + + context.Annotations.MarkAndPush (type); + + if (type.HasFields) + MarkFields (context, type.Fields, rootVisibility); + if (type.HasMethods) + MarkMethods (context, type.Methods, rootVisibility); + if (type.HasNestedTypes) + foreach (var nested in type.NestedTypes) + MarkType (context, nested, rootVisibility); + + context.Tracer.Pop (); + } + + void ProcessExecutable (AssemblyDefinition assembly) + { + Context.SetAction (assembly, AssemblyAction.Link); + + Tracer.Push (assembly); + + Annotations.Mark (assembly.EntryPoint.DeclaringType); + + MarkMethod (Context, assembly.EntryPoint, MethodAction.Parse, RootVisibility.Any); + + Tracer.Pop (); + } + + static void MarkFields (LinkContext context, Collection fields, RootVisibility rootVisibility) + { + foreach (FieldDefinition field in fields) { + bool markField; + switch (rootVisibility) { + default: + markField = true; + break; + + case RootVisibility.PublicAndFamily: + markField = field.IsPublic || field.IsFamily || field.IsFamilyOrAssembly; + break; + + case RootVisibility.PublicAndFamilyAndAssembly: + markField = field.IsPublic || field.IsFamily || field.IsFamilyOrAssembly || field.IsAssembly || field.IsFamilyAndAssembly; + break; + } + if (markField) { + context.Annotations.Mark (field); + } + } + } + + static void MarkMethods (LinkContext context, Collection methods, RootVisibility rootVisibility) + { + foreach (MethodDefinition method in methods) + MarkMethod (context, method, MethodAction.ForceParse, rootVisibility); + } + + static void MarkMethod (LinkContext context, MethodDefinition method, MethodAction action, RootVisibility rootVisibility) + { + bool markMethod; + switch (rootVisibility) { + default: + markMethod = true; + break; + + case RootVisibility.PublicAndFamily: + markMethod = method.IsPublic || method.IsFamily || method.IsFamilyOrAssembly; + break; + + case RootVisibility.PublicAndFamilyAndAssembly: + markMethod = method.IsPublic || method.IsFamily || method.IsFamilyOrAssembly || method.IsAssembly || method.IsFamilyAndAssembly; + break; + } + + if (markMethod) { + context.Annotations.Mark (method); + context.Annotations.SetAction (method, action); + } + } + + static bool HasInternalsVisibleTo (AssemblyDefinition assembly) + { + foreach (CustomAttribute attribute in assembly.CustomAttributes) { + if (attribute.Constructor.DeclaringType.FullName == + "System.Runtime.CompilerServices.InternalsVisibleToAttribute") + return true; + } + + return false; + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/ResolveFromXApiStep.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/ResolveFromXApiStep.cs new file mode 100644 index 000000000000..3d4f7099cea1 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/ResolveFromXApiStep.cs @@ -0,0 +1,138 @@ +// +// ResolveFromXApiStep.cs +// +// Author: +// Jb Evain (jbevain@novell.com) +// +// (C) 2007 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. +// + +using System.Xml.XPath; + +using Mono.Cecil; + +namespace Mono.Linker.Steps { + + public class ResolveFromXApiStep : ResolveStep, IXApiVisitor { + + static readonly string _name = "name"; + static readonly string _ns = string.Empty; + + XPathDocument _document; + + public ResolveFromXApiStep (XPathDocument document) + { + _document = document; + } + + protected override void Process () + { + XApiReader reader = new XApiReader (_document, this); + reader.Process (Context); + } + + public void OnAssembly (XPathNavigator nav, AssemblyDefinition assembly) + { + } + + public void OnAttribute (XPathNavigator nav) + { + string name = GetName (nav); + + TypeDefinition type = Context.GetType (name); + if (type is not null) + MarkType (type); + } + + public void OnClass (XPathNavigator nav, TypeDefinition type) + { + MarkType (type); + } + + public void OnInterface (XPathNavigator nav, TypeDefinition type) + { + MarkType (type); + } + + public void OnField (XPathNavigator nav, FieldDefinition field) + { + MarkField (field); + } + + public void OnMethod (XPathNavigator nav, MethodDefinition method) + { + MarkMethod (method); + } + + public void OnConstructor (XPathNavigator nav, MethodDefinition method) + { + MarkMethod (method); + } + + public void OnProperty (XPathNavigator nav, PropertyDefinition property) + { + } + + public void OnEvent (XPathNavigator nav, EventDefinition evt) + { + if (evt.AddMethod is not null) + MarkMethod (evt.AddMethod); + if (evt.InvokeMethod is not null) + MarkMethod (evt.InvokeMethod); + if (evt.RemoveMethod is not null) + MarkMethod (evt.RemoveMethod); + } + + static string GetName (XPathNavigator nav) + { + return GetAttribute (nav, _name); + } + + static string GetAttribute (XPathNavigator nav, string attribute) + { + return nav.GetAttribute (attribute, _ns); + } + + void MarkType (TypeDefinition type) + { + InternalMark (type); + } + + void MarkField (FieldDefinition field) + { + InternalMark (field); + } + + void InternalMark (IMetadataTokenProvider provider) + { + Annotations.Mark (provider); + Annotations.SetPublic (provider); + } + + void MarkMethod (MethodDefinition method) + { + InternalMark (method); + Annotations.MarkIndirectlyCalledMethod (method); + Annotations.SetAction (method, MethodAction.Parse); + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/ResolveFromXmlStep.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/ResolveFromXmlStep.cs new file mode 100644 index 000000000000..8f794173b839 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/ResolveFromXmlStep.cs @@ -0,0 +1,727 @@ +// +// ResolveFromXmlStep.cs +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// (C) 2006 Jb Evain +// (C) 2007 Novell, Inc. +// Copyright 2013 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. +// + +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml.XPath; + +using Mono.Cecil; + +namespace Mono.Linker.Steps { + + public class XmlResolutionException : Exception { + public XmlResolutionException (string message, Exception innerException) + : base (message, innerException) + { + } + } + + public class ResolveFromXmlStep : ResolveStep { + + static readonly string _signature = "signature"; + static readonly string _fullname = "fullname"; + static readonly string _required = "required"; + static readonly string _preserve = "preserve"; + static readonly string _accessors = "accessors"; + static readonly string _ns = string.Empty; + + static readonly string [] _accessorsAll = new string [] { "all" }; + static readonly char [] _accessorsSep = new char [] { ';' }; + + XPathDocument _document; + string _xmlDocumentLocation; + string _resourceName; + AssemblyDefinition _resourceAssembly; + + public ResolveFromXmlStep (XPathDocument document, string xmlDocumentLocation = "") + { + _document = document; + _xmlDocumentLocation = xmlDocumentLocation; + } + + public ResolveFromXmlStep (XPathDocument document, string resourceName, AssemblyDefinition resourceAssembly, string xmlDocumentLocation = "") + : this (document, xmlDocumentLocation) + { + if (string.IsNullOrEmpty (resourceName)) + throw new ArgumentNullException (nameof (resourceName)); + + if (resourceAssembly is null) + throw new ArgumentNullException (nameof (resourceAssembly)); + + _resourceName = resourceName; + _resourceAssembly = resourceAssembly; + } + + protected override void Process () + { + XPathNavigator nav = _document.CreateNavigator (); + + // This step can be created with XML files that aren't necessarily + // linker descriptor files. So bail if we don't have a element. + if (!nav.MoveToChild ("linker", _ns)) + return; + + try { + ProcessAssemblies (Context, nav.SelectChildren ("assembly", _ns)); + + if (!string.IsNullOrEmpty (_resourceName) && Context.StripResources) + Context.Annotations.AddResourceToRemove (_resourceAssembly, _resourceName); + } catch (Exception ex) when (!(ex is XmlResolutionException)) { + throw new XmlResolutionException (string.Format ("Failed to process XML description: {0}", _xmlDocumentLocation), ex); + } + } + + protected virtual void ProcessAssemblies (LinkContext context, XPathNodeIterator iterator) + { + while (iterator.MoveNext ()) { + AssemblyDefinition assembly = GetAssembly (context, GetAssemblyName (iterator.Current)); + if (assembly is not null) + ProcessAssembly (assembly, iterator); + } + } + + protected virtual void ProcessAssembly (AssemblyDefinition assembly, XPathNodeIterator iterator) + { + if (IsExcluded (iterator.Current)) + return; + + Tracer.Push (assembly); + if (GetTypePreserve (iterator.Current) == TypePreserve.All) { + foreach (var type in assembly.MainModule.Types) + MarkAndPreserveAll (type); + } else { + ProcessTypes (assembly, iterator.Current.SelectChildren ("type", _ns)); + ProcessNamespaces (assembly, iterator.Current.SelectChildren ("namespace", _ns)); + } + Tracer.Pop (); + } + + void ProcessNamespaces (AssemblyDefinition assembly, XPathNodeIterator iterator) + { + while (iterator.MoveNext ()) { + string fullname = GetFullName (iterator.Current); + foreach (TypeDefinition type in assembly.MainModule.Types) { + if (type.Namespace != fullname) + continue; + + MarkAndPreserveAll (type); + } + } + } + + void MarkAndPreserveAll (TypeDefinition type) + { + Annotations.MarkAndPush (type); + Annotations.SetPreserve (type, TypePreserve.All); + + if (!type.HasNestedTypes) { + Tracer.Pop (); + return; + } + + foreach (TypeDefinition nested in type.NestedTypes) + MarkAndPreserveAll (nested); + + Tracer.Pop (); + } + + void ProcessTypes (AssemblyDefinition assembly, XPathNodeIterator iterator) + { + while (iterator.MoveNext ()) { + XPathNavigator nav = iterator.Current; + + string fullname = GetFullName (nav); + + if (IsTypePattern (fullname)) { + ProcessTypePattern (fullname, assembly, nav); + continue; + } + + TypeDefinition type = assembly.MainModule.GetType (fullname); + + if (type is null) { + if (assembly.MainModule.HasExportedTypes) { + foreach (var exported in assembly.MainModule.ExportedTypes) { + if (fullname == exported.FullName) { + Tracer.Push (exported); + MarkingHelpers.MarkExportedType (exported, assembly.MainModule); + var resolvedExternal = exported.Resolve (); + Tracer.Pop (); + if (resolvedExternal is not null) { + type = resolvedExternal; + break; + } + } + } + } + } + + if (type is null) + continue; + + ProcessType (type, nav); + } + } + + static bool IsTypePattern (string fullname) + { + return fullname.IndexOf ("*") != -1; + } + + static Regex CreateRegexFromPattern (string pattern) + { + return new Regex (pattern.Replace (".", @"\.").Replace ("*", "(.*)")); + } + + void MatchType (TypeDefinition type, Regex regex, XPathNavigator nav) + { + if (regex.Match (type.FullName).Success) + ProcessType (type, nav); + + if (!type.HasNestedTypes) + return; + + foreach (var nt in type.NestedTypes) + MatchType (nt, regex, nav); + } + + void MatchExportedType (ExportedType exportedType, ModuleDefinition module, Regex regex, XPathNavigator nav) + { + if (regex.Match (exportedType.FullName).Success) { + MarkingHelpers.MarkExportedType (exportedType, module); + TypeDefinition type = exportedType.Resolve (); + if (type is not null) { + ProcessType (type, nav); + } + } + } + + + void ProcessTypePattern (string fullname, AssemblyDefinition assembly, XPathNavigator nav) + { + Regex regex = CreateRegexFromPattern (fullname); + + foreach (TypeDefinition type in assembly.MainModule.Types) { + MatchType (type, regex, nav); + } + + if (assembly.MainModule.HasExportedTypes) { + foreach (var exported in assembly.MainModule.ExportedTypes) { + MatchExportedType (exported, assembly.MainModule, regex, nav); + } + } + } + + protected virtual void ProcessType (TypeDefinition type, XPathNavigator nav) + { + if (IsExcluded (nav)) + return; + + TypePreserve preserve = GetTypePreserve (nav); + + if (!IsRequired (nav)) { + Annotations.SetPreserve (type, preserve); + return; + } + + if (Annotations.IsMarked (type)) { + var existingLevel = Annotations.TryGetPreserve (type, out TypePreserve existingPreserve) ? existingPreserve : TypePreserve.Nothing; + var duplicateLevel = preserve != TypePreserve.Nothing ? preserve : nav.HasChildren ? TypePreserve.Nothing : TypePreserve.All; + Context.LogMessage ($"Duplicate preserve in {_xmlDocumentLocation} of {type.FullName} ({existingLevel}). Duplicate uses ({duplicateLevel})"); + } + + Annotations.MarkAndPush (type); + Tracer.AddDirectDependency (this, type); + + if (type.IsNested) { + var parent = type; + while (parent.IsNested) { + parent = parent.DeclaringType; + Annotations.Mark (parent); + } + } + + if (preserve != TypePreserve.Nothing) + Annotations.SetPreserve (type, preserve); + + if (nav.HasChildren) { + MarkSelectedFields (nav, type); + MarkSelectedMethods (nav, type); + MarkSelectedEvents (nav, type); + MarkSelectedProperties (nav, type); + } + Tracer.Pop (); + } + + void MarkSelectedFields (XPathNavigator nav, TypeDefinition type) + { + XPathNodeIterator fields = nav.SelectChildren ("field", _ns); + if (fields.Count == 0) + return; + + ProcessFields (type, fields); + } + + void MarkSelectedMethods (XPathNavigator nav, TypeDefinition type) + { + XPathNodeIterator methods = nav.SelectChildren ("method", _ns); + if (methods.Count == 0) + return; + + ProcessMethods (type, methods); + } + + void MarkSelectedEvents (XPathNavigator nav, TypeDefinition type) + { + XPathNodeIterator events = nav.SelectChildren ("event", _ns); + if (events.Count == 0) + return; + + ProcessEvents (type, events); + } + + void MarkSelectedProperties (XPathNavigator nav, TypeDefinition type) + { + XPathNodeIterator properties = nav.SelectChildren ("property", _ns); + if (properties.Count == 0) + return; + + ProcessProperties (type, properties); + } + + static TypePreserve GetTypePreserve (XPathNavigator nav) + { + string attribute = GetAttribute (nav, _preserve); + if (string.IsNullOrEmpty (attribute)) + return nav.HasChildren ? TypePreserve.Nothing : TypePreserve.All; + + TypePreserve result; + if (Enum.TryParse (attribute, true, out result)) + return result; + return TypePreserve.Nothing; + } + + void ProcessFields (TypeDefinition type, XPathNodeIterator iterator) + { + while (iterator.MoveNext ()) + ProcessField (type, iterator); + } + + protected virtual void ProcessField (TypeDefinition type, XPathNodeIterator iterator) + { + if (IsExcluded (iterator.Current)) + return; + + string value = GetSignature (iterator.Current); + if (!String.IsNullOrEmpty (value)) + ProcessFieldSignature (type, value); + + value = GetAttribute (iterator.Current, "name"); + if (!String.IsNullOrEmpty (value)) + ProcessFieldName (type, value); + } + + void ProcessFieldSignature (TypeDefinition type, string signature) + { + FieldDefinition field = GetField (type, signature); + MarkField (type, field, signature); + } + + void MarkField (TypeDefinition type, FieldDefinition field, string signature) + { + if (field is not null) { + if (Annotations.IsMarked (field)) + Context.LogMessage ($"Duplicate preserve in {_xmlDocumentLocation} of {field.FullName}"); + + Annotations.Mark (field); + } else { + AddUnresolveMarker (string.Format ("T: {0}; F: {1}", type, signature)); + } + } + + void ProcessFieldName (TypeDefinition type, string name) + { + if (!type.HasFields) + return; + + foreach (FieldDefinition field in type.Fields) + if (field.Name == name) + MarkField (type, field, name); + } + + protected static FieldDefinition GetField (TypeDefinition type, string signature) + { + if (!type.HasFields) + return null; + + foreach (FieldDefinition field in type.Fields) + if (signature == GetFieldSignature (field)) + return field; + + return null; + } + + static string GetFieldSignature (FieldDefinition field) + { + return field.FieldType.FullName + " " + field.Name; + } + + void ProcessMethods (TypeDefinition type, XPathNodeIterator iterator) + { + while (iterator.MoveNext ()) + ProcessMethod (type, iterator); + } + + protected virtual void ProcessMethod (TypeDefinition type, XPathNodeIterator iterator) + { + if (IsExcluded (iterator.Current)) + return; + + string value = GetSignature (iterator.Current); + if (!String.IsNullOrEmpty (value)) + ProcessMethodSignature (type, value); + + value = GetAttribute (iterator.Current, "name"); + if (!String.IsNullOrEmpty (value)) + ProcessMethodName (type, value); + } + + void ProcessMethodSignature (TypeDefinition type, string signature) + { + MethodDefinition meth = GetMethod (type, signature); + MarkMethod (type, meth, signature); + } + + void MarkMethod (TypeDefinition type, MethodDefinition method, string signature) + { + if (method is not null) { + MarkMethod (method); + } else + AddUnresolveMarker (string.Format ("T: {0}; M: {1}", type, signature)); + } + + void MarkMethod (MethodDefinition method) + { + if (Annotations.IsMarked (method)) + Context.LogMessage ($"Duplicate preserve in {_xmlDocumentLocation} of {method.FullName}"); + + Annotations.Mark (method); + Annotations.MarkIndirectlyCalledMethod (method); + Tracer.AddDirectDependency (this, method); + Annotations.SetAction (method, MethodAction.Parse); + } + + void MarkMethodIfNotNull (MethodDefinition method) + { + if (method is null) + return; + + MarkMethod (method); + } + + void ProcessMethodName (TypeDefinition type, string name) + { + if (!type.HasMethods) + return; + + foreach (MethodDefinition method in type.Methods) + if (name == method.Name) + MarkMethod (type, method, name); + } + + protected static MethodDefinition GetMethod (TypeDefinition type, string signature) + { + if (type.HasMethods) + foreach (MethodDefinition meth in type.Methods) + if (signature == GetMethodSignature (meth, false)) + return meth; + + return null; + } + + public static string GetMethodSignature (MethodDefinition meth, bool includeGenericParameters) + { + StringBuilder sb = new StringBuilder (); + sb.Append (meth.ReturnType.FullName); + sb.Append (" "); + sb.Append (meth.Name); + if (includeGenericParameters && meth.HasGenericParameters) { + sb.Append ("`"); + sb.Append (meth.GenericParameters.Count); + } + + sb.Append ("("); + if (meth.HasParameters) { + for (int i = 0; i < meth.Parameters.Count; i++) { + if (i > 0) + sb.Append (","); + + sb.Append (meth.Parameters [i].ParameterType.FullName); + } + } + sb.Append (")"); + return sb.ToString (); + } + + void ProcessEvents (TypeDefinition type, XPathNodeIterator iterator) + { + while (iterator.MoveNext ()) + ProcessEvent (type, iterator); + } + + protected virtual void ProcessEvent (TypeDefinition type, XPathNodeIterator iterator) + { + if (IsExcluded (iterator.Current)) + return; + + string value = GetSignature (iterator.Current); + if (!String.IsNullOrEmpty (value)) + ProcessEventSignature (type, value); + + value = GetAttribute (iterator.Current, "name"); + if (!String.IsNullOrEmpty (value)) + ProcessEventName (type, value); + } + + void ProcessEventSignature (TypeDefinition type, string signature) + { + EventDefinition @event = GetEvent (type, signature); + MarkEvent (type, @event, signature); + } + + void MarkEvent (TypeDefinition type, EventDefinition @event, string signature) + { + if (@event is not null) { + if (Annotations.IsMarked (@event)) + Context.LogMessage ($"Duplicate preserve in {_xmlDocumentLocation} of {@event.FullName}"); + + Annotations.Mark (@event); + + MarkMethod (@event.AddMethod); + MarkMethod (@event.RemoveMethod); + MarkMethodIfNotNull (@event.InvokeMethod); + } else + AddUnresolveMarker (string.Format ("T: {0}; E: {1}", type, signature)); + } + + void ProcessEventName (TypeDefinition type, string name) + { + if (!type.HasEvents) + return; + + foreach (EventDefinition @event in type.Events) + if (@event.Name == name) + MarkEvent (type, @event, name); + } + + protected static EventDefinition GetEvent (TypeDefinition type, string signature) + { + if (!type.HasEvents) + return null; + + foreach (EventDefinition @event in type.Events) + if (signature == GetEventSignature (@event)) + return @event; + + return null; + } + + static string GetEventSignature (EventDefinition @event) + { + return @event.EventType.FullName + " " + @event.Name; + } + + void ProcessProperties (TypeDefinition type, XPathNodeIterator iterator) + { + while (iterator.MoveNext ()) + ProcessProperty (type, iterator); + } + + protected virtual void ProcessProperty (TypeDefinition type, XPathNodeIterator iterator) + { + if (IsExcluded (iterator.Current)) + return; + + string value = GetSignature (iterator.Current); + if (!String.IsNullOrEmpty (value)) + ProcessPropertySignature (type, value, GetAccessors (iterator.Current)); + + value = GetAttribute (iterator.Current, "name"); + if (!String.IsNullOrEmpty (value)) + ProcessPropertyName (type, value, _accessorsAll); + } + + void ProcessPropertySignature (TypeDefinition type, string signature, string [] accessors) + { + PropertyDefinition property = GetProperty (type, signature); + MarkProperty (type, property, signature, accessors); + } + + void MarkProperty (TypeDefinition type, PropertyDefinition property, string signature, string [] accessors) + { + if (property is not null) { + if (Annotations.IsMarked (property)) + Context.LogMessage ($"Duplicate preserve in {_xmlDocumentLocation} of {property.FullName}"); + + Annotations.Mark (property); + + MarkPropertyAccessors (type, property, accessors); + } else + AddUnresolveMarker (string.Format ("T: {0}; P: {1}", type, signature)); + } + + void MarkPropertyAccessors (TypeDefinition type, PropertyDefinition property, string [] accessors) + { + if (Array.IndexOf (accessors, "all") >= 0) { + MarkMethodIfNotNull (property.GetMethod); + MarkMethodIfNotNull (property.SetMethod); + + return; + } + if (property.GetMethod is not null + && Array.IndexOf (accessors, "get") >= 0) + MarkMethod (property.GetMethod); + else if (property.GetMethod is null) + AddUnresolveMarker (string.Format ("T: {0}' M: {1} get_{2}", type, property.PropertyType, property.Name)); + + if (property.SetMethod is not null + && Array.IndexOf (accessors, "set") >= 0) + MarkMethod (property.SetMethod); + else if (property.SetMethod is null) + AddUnresolveMarker (string.Format ("T: {0}' M: System.Void set_{2} ({1})", type, property.PropertyType, property.Name)); + } + + void ProcessPropertyName (TypeDefinition type, string name, string [] accessors) + { + if (!type.HasProperties) + return; + + foreach (PropertyDefinition property in type.Properties) + if (property.Name == name) + MarkProperty (type, property, name, accessors); + } + + protected static PropertyDefinition GetProperty (TypeDefinition type, string signature) + { + if (!type.HasProperties) + return null; + + foreach (PropertyDefinition property in type.Properties) + if (signature == GetPropertySignature (property)) + return property; + + return null; + } + + static string GetPropertySignature (PropertyDefinition property) + { + return property.PropertyType.FullName + " " + property.Name; + } + + protected AssemblyDefinition GetAssembly (LinkContext context, AssemblyNameReference assemblyName) + { + var assembly = context.Resolve (assemblyName); + ProcessReferences (assembly, context); + return assembly; + } + + protected virtual AssemblyNameReference GetAssemblyName (XPathNavigator nav) + { + return AssemblyNameReference.Parse (GetFullName (nav)); + } + + static void ProcessReferences (AssemblyDefinition assembly, LinkContext context) + { + context.ResolveReferences (assembly); + } + + static bool IsRequired (XPathNavigator nav) + { + string attribute = GetAttribute (nav, _required); + if (attribute is null || attribute.Length == 0) + return true; + + bool result; + if (bool.TryParse (attribute, out result)) + return result; + return false; + } + + protected static string GetSignature (XPathNavigator nav) + { + return GetAttribute (nav, _signature); + } + + static string GetFullName (XPathNavigator nav) + { + return GetAttribute (nav, _fullname); + } + + protected static string [] GetAccessors (XPathNavigator nav) + { + string accessorsValue = GetAttribute (nav, _accessors); + + if (accessorsValue is not null) { + string [] accessors = accessorsValue.Split ( + _accessorsSep, StringSplitOptions.RemoveEmptyEntries); + + if (accessors.Length > 0) { + for (int i = 0; i < accessors.Length; ++i) + accessors [i] = accessors [i].ToLower (); + + return accessors; + } + } + return _accessorsAll; + } + + protected static string GetAttribute (XPathNavigator nav, string attribute) + { + return nav.GetAttribute (attribute, _ns); + } + + protected virtual bool IsExcluded (XPathNavigator nav) + { + var value = GetAttribute (nav, "feature"); + if (string.IsNullOrEmpty (value)) + return false; + + return Context.IsFeatureExcluded (value); + } + + + public override string ToString () + { + return "ResolveFromXmlStep: " + _xmlDocumentLocation; + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/ResolveStep.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/ResolveStep.cs new file mode 100644 index 000000000000..604728bef247 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/ResolveStep.cs @@ -0,0 +1,56 @@ +// +// ResolveStep.cs +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// (C) 2006 Jb Evain +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System.Collections.Generic; + +namespace Mono.Linker.Steps { + + public abstract class ResolveStep : BaseStep { + + readonly List _unResolved; + + protected ResolveStep () + { + _unResolved = new List (); + } + + public bool AllMarkerResolved { + get { return _unResolved.Count == 0; } + } + + public string [] GetUnresolvedMarkers () + { + return _unResolved.ToArray (); + } + + protected void AddUnresolveMarker (string signature) + { + _unResolved.Add (signature); + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/SweepStep.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/SweepStep.cs new file mode 100644 index 000000000000..dd1e411be8c1 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/SweepStep.cs @@ -0,0 +1,714 @@ +// +// SweepStep.cs +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// (C) 2006 Jb Evain +// (C) 2007 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. +// + +using System.Collections.Generic; +using System.Linq; +using Mono.Cecil; +using Mono.Collections.Generic; +using Mono.Cecil.Cil; + +namespace Mono.Linker.Steps { + + public class SweepStep : BaseStep { + AssemblyDefinition [] assemblies; + readonly bool sweepSymbols; + readonly HashSet BypassNGenToSave = new HashSet (); + + public SweepStep (bool sweepSymbols = true) + { + this.sweepSymbols = sweepSymbols; + } + + protected override void Process () + { + assemblies = Context.Annotations.GetAssemblies ().ToArray (); + + foreach (var assembly in assemblies) { + RemoveUnusedAssembly (assembly); + } + + foreach (var assembly in assemblies) { + ProcessAssemblyAction (assembly); + } + } + + void RemoveUnusedAssembly (AssemblyDefinition assembly) + { + switch (Annotations.GetAction (assembly)) { + case AssemblyAction.AddBypassNGenUsed: + case AssemblyAction.CopyUsed: + case AssemblyAction.Link: + if (!IsUsedAssembly (assembly)) + RemoveAssembly (assembly); + + break; + } + } + + protected void ProcessAssemblyAction (AssemblyDefinition assembly) + { + switch (Annotations.GetAction (assembly)) { + case AssemblyAction.AddBypassNGenUsed: + Annotations.SetAction (assembly, AssemblyAction.AddBypassNGen); + goto case AssemblyAction.AddBypassNGen; + + case AssemblyAction.AddBypassNGen: + // FIXME: AddBypassNGen is just wrong, it should not be action as we need to + // turn it to Action.Save here to e.g. correctly update debug symbols + if (!Context.KeepTypeForwarderOnlyAssemblies || BypassNGenToSave.Contains (assembly)) { + goto case AssemblyAction.Save; + } + + break; + + case AssemblyAction.CopyUsed: + Annotations.SetAction (assembly, AssemblyAction.Copy); + goto case AssemblyAction.Copy; + + case AssemblyAction.Copy: + // + // Facade assemblies can have unused forwarders pointing to + // removed type (when facades are kept) + // + // main.exe -> facade.dll -> lib.dll + // link | copy | link + // + // when main.exe has unused reference to type in lib.dll + // + if (SweepTypeForwarders (assembly)) + Annotations.SetAction (assembly, AssemblyAction.Save); + + break; + + case AssemblyAction.Link: + SweepAssembly (assembly); + break; + + case AssemblyAction.Save: + // + // Save means we need to rewrite the assembly due to removed assembly + // reference. We do any additional removed assembly reference clean up here + // + UpdateForwardedTypesScope (assembly); + UpdateCustomAttributesTypesScopes (assembly); + SweepTypeForwarders (assembly); + break; + } + } + + protected virtual void SweepAssembly (AssemblyDefinition assembly) + { + var types = new List (); + + foreach (TypeDefinition type in assembly.MainModule.Types) { + if (Annotations.IsMarked (type)) { + SweepType (type); + types.Add (type); + continue; + } + + if (type.Name == "") + types.Add (type); + else + ElementRemoved (type); + } + + assembly.MainModule.Types.Clear (); + foreach (TypeDefinition type in types) + assembly.MainModule.Types.Add (type); + + SweepResources (assembly); + SweepCustomAttributes (assembly); + + foreach (var module in assembly.Modules) + SweepCustomAttributes (module); + + SweepTypeForwarders (assembly); + + UpdateForwardedTypesScope (assembly); + } + + bool IsUsedAssembly (AssemblyDefinition assembly) + { + if (IsMarkedAssembly (assembly)) + return true; + + if (assembly.MainModule.HasExportedTypes && Context.KeepTypeForwarderOnlyAssemblies) + return true; + + return false; + } + + bool IsMarkedAssembly (AssemblyDefinition assembly) + { + return Annotations.IsMarked (assembly.MainModule); + } + + protected virtual void RemoveAssembly (AssemblyDefinition assembly) + { + Annotations.SetAction (assembly, AssemblyAction.Delete); + + foreach (var a in assemblies) { + switch (Annotations.GetAction (a)) { + case AssemblyAction.Skip: + case AssemblyAction.Delete: + continue; + } + + SweepReferences (a, assembly); + } + } + + void SweepResources (AssemblyDefinition assembly) + { + var resourcesToRemove = Annotations.GetResourcesToRemove (assembly); + if (resourcesToRemove is not null) { + var resources = assembly.MainModule.Resources; + + for (int i = 0; i < resources.Count; i++) { + var resource = resources [i] as EmbeddedResource; + if (resource is null) + continue; + + if (resourcesToRemove.Contains (resource.Name)) + resources.RemoveAt (i--); + } + } + } + + void SweepReferences (AssemblyDefinition assembly, AssemblyDefinition referenceToRemove) + { + if (assembly == referenceToRemove) + return; + + bool reference_removed = false; + + var references = assembly.MainModule.AssemblyReferences; + for (int i = 0; i < references.Count; i++) { + var reference = references [i]; + + AssemblyDefinition ad = Context.Resolver.Resolve (reference); + if (ad is null || !AreSameReference (ad.Name, referenceToRemove.Name)) + continue; + + ReferenceRemoved (assembly, reference); + references.RemoveAt (i--); + reference_removed = true; + } + + if (reference_removed) { + switch (Annotations.GetAction (assembly)) { + case AssemblyAction.CopyUsed: + if (IsUsedAssembly (assembly)) { + goto case AssemblyAction.Copy; + } + break; + + case AssemblyAction.Copy: + // + // Assembly has a reference to another assembly which has been fully removed. This can + // happen when for example the reference assembly is 'copy-used' and it's not needed. + // + // or + // + // Assembly can contain type references with + // type forwarders to deleted assembly (facade) when + // facade assemblies are not kept. For that reason we need to + // rewrite the copy to save to update the scopes not to point + // forwarding assembly (facade). + // + // foo.dll -> facade.dll -> lib.dll + // copy | copy (delete) | link + // + Annotations.SetAction (assembly, AssemblyAction.Save); + break; + + case AssemblyAction.AddBypassNGenUsed: + if (IsUsedAssembly (assembly)) { + Annotations.SetAction (assembly, AssemblyAction.AddBypassNGen); + goto case AssemblyAction.AddBypassNGen; + } + break; + + case AssemblyAction.AddBypassNGen: + BypassNGenToSave.Add (assembly); + break; + } + } + } + + bool SweepTypeForwarders (AssemblyDefinition assembly) + { + if (assembly.MainModule.HasExportedTypes) { + return SweepCollectionMetadata (assembly.MainModule.ExportedTypes); + } + + return false; + } + + void UpdateForwardedTypesScope (AssemblyDefinition assembly) + { + var changed_types = new Dictionary (); + + foreach (TypeReference tr in assembly.MainModule.GetTypeReferences ()) { + if (tr.IsWindowsRuntimeProjection) + continue; + + TypeDefinition td; + try { + td = tr.Resolve (); + } catch (AssemblyResolutionException) { + // Don't crash on unresolved assembly + continue; + } + + // at this stage reference might include things that can't be resolved + // and if it is (resolved) it needs to be kept only if marked (#16213) + if (td is null || !Annotations.IsMarked (td)) + continue; + + IMetadataScope scope = assembly.MainModule.ImportReference (td).Scope; + if (tr.Scope != scope) + changed_types.Add (tr, scope); + } + + // + // Resolved everything first before updating scopes. + // If we set the scope to null, then calling Resolve() on any of its + // nested types would crash. + // + foreach (var e in changed_types) { + e.Key.Scope = e.Value; + } + + if (assembly.MainModule.HasExportedTypes) { + foreach (var et in assembly.MainModule.ExportedTypes) { + var td = et.Resolve (); + if (td is null) + continue; + + et.Scope = assembly.MainModule.ImportReference (td).Scope; + } + } + } + + static void UpdateCustomAttributesTypesScopes (AssemblyDefinition assembly) + { + UpdateCustomAttributesTypesScopes ((ICustomAttributeProvider) assembly); + + foreach (var module in assembly.Modules) + UpdateCustomAttributesTypesScopes (module); + + foreach (var type in assembly.MainModule.Types) + UpdateCustomAttributesTypesScopes (type); + } + + static void UpdateCustomAttributesTypesScopes (TypeDefinition typeDefinition) + { + UpdateCustomAttributesTypesScopes ((ICustomAttributeProvider) typeDefinition); + + if (typeDefinition.HasEvents) + UpdateCustomAttributesTypesScopes (typeDefinition.Events); + + if (typeDefinition.HasFields) + UpdateCustomAttributesTypesScopes (typeDefinition.Fields); + + if (typeDefinition.HasMethods) + UpdateCustomAttributesTypesScopes (typeDefinition.Methods); + + if (typeDefinition.HasProperties) + UpdateCustomAttributesTypesScopes (typeDefinition.Properties); + + if (typeDefinition.HasGenericParameters) + UpdateCustomAttributesTypesScopes (typeDefinition.GenericParameters); + + if (typeDefinition.HasNestedTypes) { + foreach (var nestedType in typeDefinition.NestedTypes) { + UpdateCustomAttributesTypesScopes (nestedType); + } + } + } + + static void UpdateCustomAttributesTypesScopes (Collection providers) where T : ICustomAttributeProvider + { + foreach (var provider in providers) + UpdateCustomAttributesTypesScopes (provider); + } + + static void UpdateCustomAttributesTypesScopes (Collection genericParameters) + { + foreach (var gp in genericParameters) { + UpdateCustomAttributesTypesScopes (gp); + + if (gp.HasConstraints) + UpdateCustomAttributesTypesScopes (gp.Constraints); + } + } + + static void UpdateCustomAttributesTypesScopes (ICustomAttributeProvider customAttributeProvider) + { + if (!customAttributeProvider.HasCustomAttributes) + return; + + foreach (var ca in customAttributeProvider.CustomAttributes) + UpdateForwardedTypesScope (ca); + } + + static void UpdateForwardedTypesScope (CustomAttribute attribute) + { + AssemblyDefinition assembly = attribute.Constructor.Module.Assembly; + + if (attribute.HasConstructorArguments) { + foreach (var ca in attribute.ConstructorArguments) + UpdateForwardedTypesScope (ca, assembly); + } + + if (attribute.HasFields) { + foreach (var field in attribute.Fields) + UpdateForwardedTypesScope (field.Argument, assembly); + } + + if (attribute.HasProperties) { + foreach (var property in attribute.Properties) + UpdateForwardedTypesScope (property.Argument, assembly); + } + } + + static void UpdateForwardedTypesScope (CustomAttributeArgument attributeArgument, AssemblyDefinition assembly) + { + UpdateTypeScope (attributeArgument.Type, assembly); + + switch (attributeArgument.Value) { + case TypeReference tr: + UpdateTypeScope (tr, assembly); + break; + case CustomAttributeArgument caa: + UpdateForwardedTypesScope (caa, assembly); + break; + case CustomAttributeArgument [] array: + foreach (var item in array) + UpdateForwardedTypesScope (item, assembly); + break; + } + } + + static void UpdateTypeScope (TypeReference type, AssemblyDefinition assembly) + { + // Can't update the scope of windows runtime projections + if (type.IsWindowsRuntimeProjection) + return; + + if (type is GenericInstanceType git && git.HasGenericArguments) { + UpdateTypeScope (git.ElementType, assembly); + foreach (var ga in git.GenericArguments) + UpdateTypeScope (ga, assembly); + return; + } + + if (type is ArrayType at) { + UpdateTypeScope (at.ElementType, assembly); + return; + } + + TypeDefinition td = type.Resolve (); + if (td is null) + return; + + IMetadataScope scope = assembly.MainModule.ImportReference (td).Scope; + if (type.Scope != scope) + type.Scope = td.Scope; + } + + protected virtual void SweepType (TypeDefinition type) + { + if (type.HasFields) + SweepCollectionWithCustomAttributes (type.Fields); + + if (type.HasMethods) + SweepMethods (type.Methods); + + if (type.HasNestedTypes) + SweepNestedTypes (type); + + if (type.HasInterfaces) + SweepInterfaces (type); + + if (type.HasCustomAttributes) + SweepCustomAttributes (type); + + if (type.HasGenericParameters) + SweepGenericParameters (type.GenericParameters); + + if (type.HasProperties) + SweepCustomAttributeCollection (type.Properties); + + if (type.HasEvents) + SweepCustomAttributeCollection (type.Events); + + if (type.HasFields && !type.IsBeforeFieldInit && !Annotations.HasPreservedStaticCtor (type) && !type.IsEnum) + type.IsBeforeFieldInit = true; + } + + protected void SweepNestedTypes (TypeDefinition type) + { + for (int i = 0; i < type.NestedTypes.Count; i++) { + var nested = type.NestedTypes [i]; + if (Annotations.IsMarked (nested)) { + SweepType (nested); + } else { + ElementRemoved (type.NestedTypes [i]); + type.NestedTypes.RemoveAt (i--); + } + } + } + + protected void SweepInterfaces (TypeDefinition type) + { + for (int i = type.Interfaces.Count - 1; i >= 0; i--) { + var iface = type.Interfaces [i]; + if (Annotations.IsMarked (iface)) { + SweepCustomAttributes (iface); + continue; + } + InterfaceRemoved (type, iface); + type.Interfaces.RemoveAt (i); + } + } + + protected void SweepGenericParameters (Collection genericParameters) + { + foreach (var gp in genericParameters) { + SweepCustomAttributes (gp); + + if (gp.HasConstraints) + SweepCustomAttributeCollection (gp.Constraints); + } + } + + protected void SweepCustomAttributes (TypeDefinition type) + { + var removed = SweepCustomAttributes (type as ICustomAttributeProvider); + + if (ShouldSetHasSecurityToFalse (type, type, type.HasSecurity, removed)) + type.HasSecurity = false; + } + + protected void SweepCustomAttributes (MethodDefinition method) + { + var removed = SweepCustomAttributes (method as ICustomAttributeProvider); + + if (ShouldSetHasSecurityToFalse (method, method, method.HasSecurity, removed)) + method.HasSecurity = false; + } + + bool ShouldSetHasSecurityToFalse (ISecurityDeclarationProvider providerAsSecurity, ICustomAttributeProvider provider, bool existingHasSecurity, IList removedAttributes) + { + if (existingHasSecurity && removedAttributes.Count > 0 && !providerAsSecurity.HasSecurityDeclarations) { + // If the method or type had security before and all attributes were removed, or no remaining attributes are security attributes, + // then we need to set HasSecurity to false + if (provider.CustomAttributes.Count == 0 || provider.CustomAttributes.All (attr => !IsSecurityAttributeType (attr.AttributeType.Resolve ()))) + return true; + } + + return false; + } + + static bool IsSecurityAttributeType (TypeDefinition definition) + { + if (definition is null) + return false; + + if (definition.Namespace == "System.Security") { + switch (definition.FullName) { + // This seems to be one attribute in the System.Security namespace that doesn't count + // as an attribute that requires HasSecurity to be true + case "System.Security.SecurityCriticalAttribute": + return false; + } + + return true; + } + + if (definition.BaseType is null) + return false; + + return IsSecurityAttributeType (definition.BaseType.Resolve ()); + } + + protected IList SweepCustomAttributes (ICustomAttributeProvider provider) + { + var removed = new List (); + + for (int i = provider.CustomAttributes.Count - 1; i >= 0; i--) { + var attribute = provider.CustomAttributes [i]; + if (Annotations.IsMarked (attribute)) { + UpdateForwardedTypesScope (attribute); + } else { + CustomAttributeUsageRemoved (provider, attribute); + removed.Add (provider.CustomAttributes [i]); + provider.CustomAttributes.RemoveAt (i); + } + } + + return removed; + } + + protected void SweepCustomAttributeCollection (Collection providers) where T : ICustomAttributeProvider + { + foreach (var provider in providers) + SweepCustomAttributes (provider); + } + + protected virtual void SweepMethods (Collection methods) + { + SweepCollectionWithCustomAttributes (methods); + if (sweepSymbols) + SweepDebugInfo (methods); + + foreach (var method in methods) { + if (method.HasGenericParameters) + SweepGenericParameters (method.GenericParameters); + + SweepCustomAttributes (method.MethodReturnType); + + if (!method.HasParameters) + continue; + + foreach (var parameter in method.Parameters) + SweepCustomAttributes (parameter); + } + } + + void SweepDebugInfo (Collection methods) + { + List sweptScopes = null; + foreach (var m in methods) { + if (m.DebugInformation is null) + continue; + + var scope = m.DebugInformation.Scope; + if (scope is null) + continue; + + if (sweptScopes is null) { + sweptScopes = new List (); + } else if (sweptScopes.Contains (scope)) { + continue; + } + + sweptScopes.Add (scope); + + if (scope.HasConstants) { + var constants = scope.Constants; + for (int i = 0; i < constants.Count; ++i) { + if (!Annotations.IsMarked (constants [i].ConstantType)) + constants.RemoveAt (i--); + } + } + + var import = scope.Import; + while (import is not null) { + if (import.HasTargets) { + var targets = import.Targets; + for (int i = 0; i < targets.Count; ++i) { + var ttype = targets [i].Type; + if (ttype is not null && !Annotations.IsMarked (ttype)) + targets.RemoveAt (i--); + + // TODO: Clear also AssemblyReference and Namespace when not marked + } + } + + import = import.Parent; + } + } + } + + protected void SweepCollectionWithCustomAttributes (IList list) where T : ICustomAttributeProvider + { + for (int i = 0; i < list.Count; i++) + if (ShouldRemove (list [i])) { + ElementRemoved (list [i]); + list.RemoveAt (i--); + } else { + SweepCustomAttributes (list [i]); + } + } + + protected bool SweepCollectionMetadata (IList list) where T : IMetadataTokenProvider + { + bool removed = false; + + for (int i = 0; i < list.Count; i++) { + if (ShouldRemove (list [i])) { + ElementRemoved (list [i]); + list.RemoveAt (i--); + removed = true; + } + } + + return removed; + } + + protected virtual bool ShouldRemove (T element) where T : IMetadataTokenProvider + { + return !Annotations.IsMarked (element); + } + + static bool AreSameReference (AssemblyNameReference a, AssemblyNameReference b) + { + if (a == b) + return true; + + if (a.Name != b.Name) + return false; + + if (a.Version > b.Version) + return false; + + return true; + } + + protected virtual void ElementRemoved (IMetadataTokenProvider element) + { + } + + protected virtual void ReferenceRemoved (AssemblyDefinition assembly, AssemblyNameReference reference) + { + } + + protected virtual void InterfaceRemoved (TypeDefinition type, InterfaceImplementation iface) + { + } + + protected virtual void CustomAttributeUsageRemoved (ICustomAttributeProvider provider, CustomAttribute attribute) + { + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/TypeMapStep.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/TypeMapStep.cs new file mode 100644 index 000000000000..053ad77373d9 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker.Steps/TypeMapStep.cs @@ -0,0 +1,371 @@ +// +// TypeMapStep.cs +// +// Author: +// Jb Evain (jbevain@novell.com) +// +// (C) 2009 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. +// + +using System.Collections.Generic; +using Mono.Cecil; + +namespace Mono.Linker.Steps { + + public class TypeMapStep : BaseStep { + + protected override void ProcessAssembly (AssemblyDefinition assembly) + { + foreach (TypeDefinition type in assembly.MainModule.Types) + MapType (type); + } + + protected virtual void MapType (TypeDefinition type) + { + MapVirtualMethods (type); + MapInterfaceMethodsInTypeHierarchy (type); + MapInterfaceHierarchy (type); + MapBaseTypeHierarchy (type); + + if (!type.HasNestedTypes) + return; + + foreach (var nested in type.NestedTypes) + MapType (nested); + } + + void MapInterfaceHierarchy (TypeDefinition type) + { + if (!type.IsInterface || !type.HasInterfaces) + return; + + foreach (var iface in type.Interfaces) { + var resolved = iface.InterfaceType.Resolve (); + if (resolved is null) + continue; + + Annotations.AddDerivedInterfaceForInterface (resolved, type); + } + } + + void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) + { + if (!type.HasInterfaces) + return; + + foreach (var @interface in type.Interfaces) { + var interfaceType = @interface.InterfaceType; + var iface = interfaceType.Resolve (); + if (iface is null || !iface.HasMethods) + continue; + + foreach (MethodDefinition interfaceMethod in iface.Methods) { + if (TryMatchMethod (type, interfaceMethod) is not null) + continue; + + var @base = GetBaseMethodInTypeHierarchy (type, interfaceMethod); + if (@base is not null) + AnnotateMethods (interfaceMethod, @base, @interface); + + if (interfaceType is GenericInstanceType genericInterfaceInstance) { + var genericContext = new Inflater.GenericContext (genericInterfaceInstance, null); + var baseInflated = GetBaseInflatedInterfaceMethodInTypeHierarchy (genericContext, type, interfaceMethod); + if (baseInflated is not null) + Annotations.AddOverride (interfaceMethod, baseInflated, @interface); + } + } + } + } + + static MethodReference CreateGenericInstanceCandidate (Inflater.GenericContext context, TypeDefinition candidateType, MethodDefinition interfaceMethod) + { + var methodReference = new MethodReference (interfaceMethod.Name, interfaceMethod.ReturnType, candidateType) { HasThis = interfaceMethod.HasThis }; + + foreach (var genericMethodParameter in interfaceMethod.GenericParameters) + methodReference.GenericParameters.Add (new GenericParameter (genericMethodParameter.Name, methodReference)); + + if (interfaceMethod.ReturnType.IsGenericParameter || interfaceMethod.ReturnType.IsGenericInstance) + methodReference.ReturnType = Inflater.InflateType (context, interfaceMethod.ReturnType); + + foreach (var p in interfaceMethod.Parameters) { + var parameterType = p.ParameterType; + if (parameterType.IsGenericParameter || parameterType.IsGenericInstance) + parameterType = Inflater.InflateType (context, parameterType); + + methodReference.Parameters.Add (new ParameterDefinition (p.Name, p.Attributes, parameterType)); + } + + return methodReference; + } + + void MapVirtualMethods (TypeDefinition type) + { + if (!type.HasMethods) + return; + + foreach (MethodDefinition method in type.Methods) { + if (!method.IsVirtual) + continue; + + MapVirtualMethod (method); + + if (method.HasOverrides) + MapOverrides (method); + } + } + + void MapVirtualMethod (MethodDefinition method) + { + MapVirtualBaseMethod (method); + MapVirtualInterfaceMethod (method); + } + + void MapVirtualBaseMethod (MethodDefinition method) + { + MethodDefinition @base = GetBaseMethodInTypeHierarchy (method); + if (@base is null) + return; + + AnnotateMethods (@base, method); + } + + void MapVirtualInterfaceMethod (MethodDefinition method) + { + foreach (MethodDefinition @base in GetBaseMethodsInInterfaceHierarchy (method)) + AnnotateMethods (@base, method); + } + + void MapOverrides (MethodDefinition method) + { + foreach (MethodReference override_ref in method.Overrides) { + MethodDefinition @override = override_ref.Resolve (); + if (@override is null) + continue; + + AnnotateMethods (@override, method); + } + } + + void MapBaseTypeHierarchy (TypeDefinition type) + { + if (!type.IsClass) + return; + + var bases = new List (); + var current = type.BaseType; + + while (current is not null) { + var resolved = current.Resolve (); + if (resolved is null) + break; + + // Exclude Object. That's implied and adding it to the list will just lead to lots of extra unnecessary processing + if (resolved.BaseType is null) + break; + + bases.Add (resolved); + current = resolved.BaseType; + } + + Annotations.SetClassHierarchy (type, bases); + } + + void AnnotateMethods (MethodDefinition @base, MethodDefinition @override, InterfaceImplementation matchingInterfaceImplementation = null) + { + Annotations.AddBaseMethod (@override, @base); + Annotations.AddOverride (@base, @override, matchingInterfaceImplementation); + } + + static MethodDefinition GetBaseMethodInTypeHierarchy (MethodDefinition method) + { + return GetBaseMethodInTypeHierarchy (method.DeclaringType, method); + } + + static MethodDefinition GetBaseMethodInTypeHierarchy (TypeDefinition type, MethodDefinition method) + { + TypeReference @base = type.GetInflatedBaseType (); + while (@base is not null) { + MethodDefinition base_method = TryMatchMethod (@base, method); + if (base_method is not null) + return base_method; + + @base = @base.GetInflatedBaseType (); + } + + return null; + } + + static MethodDefinition GetBaseInflatedInterfaceMethodInTypeHierarchy (Inflater.GenericContext context, TypeDefinition type, MethodDefinition interfaceMethod) + { + TypeReference @base = type.GetInflatedBaseType (); + while (@base is not null) { + var candidate = CreateGenericInstanceCandidate (context, @base.Resolve (), interfaceMethod); + + MethodDefinition base_method = TryMatchMethod (@base, candidate); + if (base_method is not null) + return base_method; + + @base = @base.GetInflatedBaseType (); + } + + return null; + } + + static IEnumerable GetBaseMethodsInInterfaceHierarchy (MethodDefinition method) + { + return GetBaseMethodsInInterfaceHierarchy (method.DeclaringType, method); + } + + static IEnumerable GetBaseMethodsInInterfaceHierarchy (TypeReference type, MethodDefinition method) + { + foreach (TypeReference @interface in type.GetInflatedInterfaces ()) { + MethodDefinition base_method = TryMatchMethod (@interface, method); + if (base_method is not null) + yield return base_method; + + foreach (MethodDefinition @base in GetBaseMethodsInInterfaceHierarchy (@interface, method)) + yield return @base; + } + } + + static MethodDefinition TryMatchMethod (TypeReference type, MethodReference method) + { + foreach (var candidate in type.GetMethods ()) { + if (MethodMatch (candidate, method)) + return candidate.Resolve (); + } + + return null; + } + + static bool MethodMatch (MethodReference candidate, MethodReference method) + { + var candidateDef = candidate.Resolve (); + + if (!candidateDef.IsVirtual) + return false; + + if (candidate.HasParameters != method.HasParameters) + return false; + + if (candidate.Name != method.Name) + return false; + + if (candidate.HasGenericParameters != method.HasGenericParameters) + return false; + + // we need to track what the generic parameter represent - as we cannot allow it to + // differ between the return type or any parameter + if (!TypeMatch (candidate.GetReturnType (), method.GetReturnType ())) + return false; + + if (!candidate.HasParameters) + return true; + + var cp = candidate.Parameters; + var mp = method.Parameters; + if (cp.Count != mp.Count) + return false; + + if (candidate.GenericParameters.Count != method.GenericParameters.Count) + return false; + + for (int i = 0; i < cp.Count; i++) { + if (!TypeMatch (candidate.GetParameterType (i), method.GetParameterType (i))) + return false; + } + + return true; + } + + static bool TypeMatch (IModifierType a, IModifierType b) + { + if (!TypeMatch (a.ModifierType, b.ModifierType)) + return false; + + return TypeMatch (a.ElementType, b.ElementType); + } + + static bool TypeMatch (TypeSpecification a, TypeSpecification b) + { + var gita = a as GenericInstanceType; + if (gita is not null) + return TypeMatch (gita, (GenericInstanceType) b); + + var mta = a as IModifierType; + if (mta is not null) + return TypeMatch (mta, (IModifierType) b); + + return TypeMatch (a.ElementType, b.ElementType); + } + + static bool TypeMatch (GenericInstanceType a, GenericInstanceType b) + { + if (!TypeMatch (a.ElementType, b.ElementType)) + return false; + + if (a.HasGenericArguments != b.HasGenericArguments) + return false; + + if (!a.HasGenericArguments) + return true; + + var gaa = a.GenericArguments; + var gab = b.GenericArguments; + if (gaa.Count != gab.Count) + return false; + + for (int i = 0; i < gaa.Count; i++) { + if (!TypeMatch (gaa [i], gab [i])) + return false; + } + + return true; + } + + static bool TypeMatch (GenericParameter a, GenericParameter b) + { + if (a.Position != b.Position) + return false; + + if (a.Type != b.Type) + return false; + + return true; + } + + static bool TypeMatch (TypeReference a, TypeReference b) + { + if (a is TypeSpecification || b is TypeSpecification) { + if (a.GetType () != b.GetType ()) + return false; + + return TypeMatch ((TypeSpecification) a, (TypeSpecification) b); + } + + if (a is GenericParameter && b is GenericParameter) + return TypeMatch ((GenericParameter) a, (GenericParameter) b); + + return a.FullName == b.FullName; + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/Annotations.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/Annotations.cs new file mode 100644 index 000000000000..c060892ba545 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/Annotations.cs @@ -0,0 +1,464 @@ +// +// Annotations.cs +// +// Author: +// Jb Evain (jbevain@novell.com) +// +// (C) 2007 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. +// + +using System; +using System.Collections.Generic; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace Mono.Linker { + + public partial class AnnotationStore { + + protected readonly LinkContext context; + + protected readonly Dictionary assembly_actions = new Dictionary (); + protected readonly Dictionary method_actions = new Dictionary (); + protected readonly Dictionary method_stub_values = new Dictionary (); + protected readonly Dictionary field_values = new Dictionary (); + protected readonly HashSet field_init = new HashSet (); + protected readonly HashSet fieldType_init = new HashSet (); + protected readonly HashSet marked = new HashSet (); + protected readonly HashSet processed = new HashSet (); + protected readonly Dictionary preserved_types = new Dictionary (); + protected readonly Dictionary> preserved_methods = new Dictionary> (); + protected readonly HashSet public_api = new HashSet (); + protected readonly Dictionary> override_methods = new Dictionary> (); + protected readonly Dictionary> base_methods = new Dictionary> (); + protected readonly Dictionary symbol_readers = new Dictionary (); + protected readonly Dictionary> class_type_base_hierarchy = new Dictionary> (); + protected readonly Dictionary> derived_interfaces = new Dictionary> (); + + protected readonly Dictionary> custom_annotations = new Dictionary> (); + protected readonly Dictionary> resources_to_remove = new Dictionary> (); + protected readonly HashSet marked_attributes = new HashSet (); + readonly HashSet marked_types_with_cctor = new HashSet (); + protected readonly HashSet marked_instantiated = new HashSet (); + protected readonly HashSet indirectly_called = new HashSet (); + + + public AnnotationStore (LinkContext context) => this.context = context; + + public bool ProcessSatelliteAssemblies { get; set; } + + protected Tracer Tracer { + get { + return context.Tracer; + } + } + + [Obsolete ("Use Tracer in LinkContext directly")] + public void PrepareDependenciesDump () + { + Tracer.AddRecorder (new XmlDependencyRecorder (context)); + } + + [Obsolete ("Use Tracer in LinkContext directly")] + public void PrepareDependenciesDump (string filename) + { + Tracer.AddRecorder (new XmlDependencyRecorder (context, filename)); + } + + public ICollection GetAssemblies () + { + return assembly_actions.Keys; + } + + public AssemblyAction GetAction (AssemblyDefinition assembly) + { + AssemblyAction action; + if (assembly_actions.TryGetValue (assembly, out action)) + return action; + + throw new InvalidOperationException ($"No action for the assembly {assembly.Name} defined"); + } + + public MethodAction GetAction (MethodDefinition method) + { + MethodAction action; + if (method_actions.TryGetValue (method, out action)) + return action; + + return MethodAction.Nothing; + } + + public void SetAction (AssemblyDefinition assembly, AssemblyAction action) + { + assembly_actions [assembly] = action; + } + + public bool HasAction (AssemblyDefinition assembly) + { + return assembly_actions.ContainsKey (assembly); + } + + public void SetAction (MethodDefinition method, MethodAction action) + { + method_actions [method] = action; + } + + public void SetMethodStubValue (MethodDefinition method, object value) + { + method_stub_values [method] = value; + } + + public void SetFieldValue (FieldDefinition field, object value) + { + field_values [field] = value; + } + + public void SetSubstitutedInit (FieldDefinition field) + { + field_init.Add (field); + } + + public bool HasSubstitutedInit (FieldDefinition field) + { + return field_init.Contains (field); + } + + public void SetSubstitutedInit (TypeDefinition type) + { + fieldType_init.Add (type); + } + + public bool HasSubstitutedInit (TypeDefinition type) + { + return fieldType_init.Contains (type); + } + + public void Mark (IMetadataTokenProvider provider) + { + marked.Add (provider); + Tracer.AddDependency (provider, true); + } + + public void Mark (CustomAttribute attribute) + { + marked_attributes.Add (attribute); + } + + public void MarkAndPush (IMetadataTokenProvider provider) + { + Mark (provider); + Tracer.Push (provider, false); + } + + public bool IsMarked (IMetadataTokenProvider provider) + { + return marked.Contains (provider); + } + + public bool IsMarked (CustomAttribute attribute) + { + return marked_attributes.Contains (attribute); + } + + public void MarkIndirectlyCalledMethod (MethodDefinition method) + { + if (!context.AddReflectionAnnotations) + return; + + indirectly_called.Add (method); + } + + public bool HasMarkedAnyIndirectlyCalledMethods () + { + return indirectly_called.Count != 0; + } + + public bool IsIndirectlyCalled (MethodDefinition method) + { + return indirectly_called.Contains (method); + } + + public void MarkInstantiated (TypeDefinition type) + { + marked_instantiated.Add (type); + } + + public bool IsInstantiated (TypeDefinition type) + { + return marked_instantiated.Contains (type); + } + + public void Processed (IMetadataTokenProvider provider) + { + processed.Add (provider); + } + + public bool IsProcessed (IMetadataTokenProvider provider) + { + return processed.Contains (provider); + } + + public bool IsPreserved (TypeDefinition type) + { + return preserved_types.ContainsKey (type); + } + + public void SetPreserve (TypeDefinition type, TypePreserve preserve) + { + TypePreserve existing; + if (preserved_types.TryGetValue (type, out existing)) + preserved_types [type] = ChoosePreserveActionWhichPreservesTheMost (existing, preserve); + else + preserved_types.Add (type, preserve); + } + + public static TypePreserve ChoosePreserveActionWhichPreservesTheMost (TypePreserve leftPreserveAction, TypePreserve rightPreserveAction) + { + if (leftPreserveAction == rightPreserveAction) + return leftPreserveAction; + + if (leftPreserveAction == TypePreserve.All || rightPreserveAction == TypePreserve.All) + return TypePreserve.All; + + if (leftPreserveAction == TypePreserve.Nothing) + return rightPreserveAction; + + if (rightPreserveAction == TypePreserve.Nothing) + return leftPreserveAction; + + if ((leftPreserveAction == TypePreserve.Methods && rightPreserveAction == TypePreserve.Fields) || + (leftPreserveAction == TypePreserve.Fields && rightPreserveAction == TypePreserve.Methods)) + return TypePreserve.All; + + return rightPreserveAction; + } + + public TypePreserve GetPreserve (TypeDefinition type) + { + TypePreserve preserve; + if (preserved_types.TryGetValue (type, out preserve)) + return preserve; + + throw new NotSupportedException ($"No type preserve information for `{type}`"); + } + + public bool TryGetPreserve (TypeDefinition type, out TypePreserve preserve) + { + return preserved_types.TryGetValue (type, out preserve); + } + + public bool TryGetMethodStubValue (MethodDefinition method, out object value) + { + return method_stub_values.TryGetValue (method, out value); + } + + public bool TryGetFieldUserValue (FieldDefinition field, out object value) + { + return field_values.TryGetValue (field, out value); + } + + public HashSet GetResourcesToRemove (AssemblyDefinition assembly) + { + HashSet resources; + if (resources_to_remove.TryGetValue (assembly, out resources)) + return resources; + + return null; + } + + public void AddResourceToRemove (AssemblyDefinition assembly, string name) + { + HashSet resources; + if (!resources_to_remove.TryGetValue (assembly, out resources)) { + resources = resources_to_remove [assembly] = new HashSet (); + } + + resources.Add (name); + } + + public void SetPublic (IMetadataTokenProvider provider) + { + public_api.Add (provider); + } + + public bool IsPublic (IMetadataTokenProvider provider) + { + return public_api.Contains (provider); + } + + public void AddOverride (MethodDefinition @base, MethodDefinition @override, InterfaceImplementation matchingInterfaceImplementation = null) + { + var methods = GetOverrides (@base); + if (methods is null) { + methods = new List (); + override_methods [@base] = methods; + } + + methods.Add (new OverrideInformation (@base, @override, matchingInterfaceImplementation)); + } + + public List GetOverrides (MethodDefinition method) + { + List overrides; + if (override_methods.TryGetValue (method, out overrides)) + return overrides; + + return null; + } + + public void AddBaseMethod (MethodDefinition method, MethodDefinition @base) + { + var methods = GetBaseMethods (method); + if (methods is null) { + methods = new List (); + base_methods [method] = methods; + } + + methods.Add (@base); + } + + public List GetBaseMethods (MethodDefinition method) + { + List bases; + if (base_methods.TryGetValue (method, out bases)) + return bases; + + return null; + } + + public List GetPreservedMethods (TypeDefinition type) + { + return GetPreservedMethods (type as IMemberDefinition); + } + + public void AddPreservedMethod (TypeDefinition type, MethodDefinition method) + { + AddPreservedMethod (type as IMemberDefinition, method); + } + + public List GetPreservedMethods (MethodDefinition method) + { + return GetPreservedMethods (method as IMemberDefinition); + } + + public void AddPreservedMethod (MethodDefinition key, MethodDefinition method) + { + AddPreservedMethod (key as IMemberDefinition, method); + } + + List GetPreservedMethods (IMemberDefinition definition) + { + List preserved; + if (preserved_methods.TryGetValue (definition, out preserved)) + return preserved; + + return null; + } + + void AddPreservedMethod (IMemberDefinition definition, MethodDefinition method) + { + var methods = GetPreservedMethods (definition); + if (methods is null) { + methods = new List (); + preserved_methods [definition] = methods; + } + + methods.Add (method); + } + + public void AddSymbolReader (AssemblyDefinition assembly, ISymbolReader symbolReader) + { + symbol_readers [assembly] = symbolReader; + } + + public void CloseSymbolReader (AssemblyDefinition assembly) + { + ISymbolReader symbolReader; + if (!symbol_readers.TryGetValue (assembly, out symbolReader)) + return; + + symbol_readers.Remove (assembly); + symbolReader.Dispose (); + } + + public Dictionary GetCustomAnnotations (object key) + { + Dictionary slots; + if (custom_annotations.TryGetValue (key, out slots)) + return slots; + + slots = new Dictionary (); + custom_annotations.Add (key, slots); + return slots; + } + + public bool HasPreservedStaticCtor (TypeDefinition type) + { + return marked_types_with_cctor.Contains (type); + } + + public bool SetPreservedStaticCtor (TypeDefinition type) + { + return marked_types_with_cctor.Add (type); + } + + public void SetClassHierarchy (TypeDefinition type, List bases) + { + class_type_base_hierarchy [type] = bases; + } + + public List GetClassHierarchy (TypeDefinition type) + { + if (class_type_base_hierarchy.TryGetValue (type, out List bases)) + return bases; + + return null; + } + + public void AddDerivedInterfaceForInterface (TypeDefinition @base, TypeDefinition derived) + { + if (!@base.IsInterface) + throw new ArgumentException ($"{nameof (@base)} must be an interface"); + + if (!derived.IsInterface) + throw new ArgumentException ($"{nameof (derived)} must be an interface"); + + List derivedInterfaces; + if (!derived_interfaces.TryGetValue (@base, out derivedInterfaces)) + derived_interfaces [@base] = derivedInterfaces = new List (); + + derivedInterfaces.Add (derived); + } + + public List GetDerivedInterfacesForInterface (TypeDefinition @interface) + { + if (!@interface.IsInterface) + throw new ArgumentException ($"{nameof (@interface)} must be an interface"); + + List derivedInterfaces; + if (derived_interfaces.TryGetValue (@interface, out derivedInterfaces)) + return derivedInterfaces; + + return null; + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/AssemblyAction.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/AssemblyAction.cs new file mode 100644 index 000000000000..341339cd1597 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/AssemblyAction.cs @@ -0,0 +1,55 @@ +// +// AssemblyAction.cs +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// (C) 2006 Jb Evain +// +// 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. +// + +namespace Mono.Linker { + + public enum AssemblyAction { + // Ignore the assembly + Skip, + // Copy the existing files, assembly and symbols, into the output destination. E.g. .dll and .mdb + // The linker still analyzes the assemblies (to know what they require) but does not modify them. + Copy, + // Copy the existing files, assembly and symbols, into the output destination if and only if + // anything from the assembly is used. + // The linker still analyzes the assemblies (to know what they require) but does not modify them. + CopyUsed, + // Link the assembly + Link, + // Remove the assembly from the output + Delete, + // Save the assembly/symbols in memory without linking it. + // E.g. useful to remove unneeded assembly references (as done in SweepStep), + // resolving [TypeForwardedTo] attributes (like PCL) to their final location + Save, + // Keep all types, methods, and fields but add System.Runtime.BypassNGenAttribute to unmarked methods. + AddBypassNGen, + // Keep all types, methods, and fields in marked assemblies but add System.Runtime.BypassNGenAttribute to unmarked methods. + // Delete unmarked assemblies. + AddBypassNGenUsed + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/AssemblyResolver.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/AssemblyResolver.cs new file mode 100644 index 000000000000..0b7367539c00 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/AssemblyResolver.cs @@ -0,0 +1,157 @@ +// +// AssemblyResolver.cs +// +// Author: +// Jb Evain (jbevain@novell.com) +// +// (C) 2007 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. +// + +using System; +using System.Collections.Generic; +using System.IO; +using Mono.Cecil; +using Mono.Collections.Generic; + +namespace Mono.Linker { + +#if FEATURE_ILLINK + public class AssemblyResolver : DirectoryAssemblyResolver { +#else + public class AssemblyResolver : BaseAssemblyResolver { +#endif + + readonly Dictionary _assemblies; + HashSet _unresolvedAssemblies; + bool _ignoreUnresolved; + LinkContext _context; + readonly Collection _references; + + + public IDictionary AssemblyCache { + get { return _assemblies; } + } + + public AssemblyResolver () + : this (new Dictionary (StringComparer.OrdinalIgnoreCase)) + { + } + + public AssemblyResolver (Dictionary assembly_cache) + { + _assemblies = assembly_cache; + _references = new Collection () { }; + } + + public bool IgnoreUnresolved { + get { return _ignoreUnresolved; } + set { _ignoreUnresolved = value; } + } + + public LinkContext Context { + get { return _context; } + set { _context = value; } + } + +#if !FEATURE_ILLINK + // The base class's definition of GetAssembly is visible when using DirectoryAssemblyResolver. + AssemblyDefinition GetAssembly (string file, ReaderParameters parameters) + { + if (parameters.AssemblyResolver is null) + parameters.AssemblyResolver = this; + + return ModuleDefinition.ReadModule (file, parameters).Assembly; + } +#endif + + AssemblyDefinition ResolveFromReferences (AssemblyNameReference name, Collection references, ReaderParameters parameters) + { + var fileName = name.Name + ".dll"; + foreach (var reference in references) { + if (Path.GetFileName (reference) != fileName) + continue; + try { + return GetAssembly (reference, parameters); + } catch (BadImageFormatException) { + continue; + } + } + + return null; + } + + public override AssemblyDefinition Resolve (AssemblyNameReference name, ReaderParameters parameters) + { + // Validate arguments, similarly to how the base class does it. + if (name is null) + throw new ArgumentNullException ("name"); + if (parameters is null) + throw new ArgumentNullException ("parameters"); + + AssemblyDefinition asm = null; + if (!_assemblies.TryGetValue (name.Name, out asm) && (_unresolvedAssemblies is null || !_unresolvedAssemblies.Contains (name.Name))) { + try { + // Any full path explicit reference takes precedence over other look up logic + asm = ResolveFromReferences (name, _references, parameters); + + // Fall back to the base class resolution logic + if (asm is null) + asm = base.Resolve (name, parameters); + + _assemblies [name.Name] = asm; + } catch (AssemblyResolutionException) { + if (!_ignoreUnresolved) + throw; + _context.LogMessage ($"warning: Ignoring unresolved assembly '{name.Name}'."); + if (_unresolvedAssemblies is null) + _unresolvedAssemblies = new HashSet (); + _unresolvedAssemblies.Add (name.Name); + } + } + + return asm; + } + + public virtual AssemblyDefinition CacheAssembly (AssemblyDefinition assembly) + { + _assemblies [assembly.Name.Name] = assembly; + base.AddSearchDirectory (Path.GetDirectoryName (assembly.MainModule.FileName)); + return assembly; + } + + public void AddReferenceAssembly (string referencePath) + { + _references.Add (referencePath); + } + + protected override void Dispose (bool disposing) + { + foreach (var asm in _assemblies.Values) { + asm.Dispose (); + } + + _assemblies.Clear (); + if (_unresolvedAssemblies is not null) + _unresolvedAssemblies.Clear (); + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/AssemblyUtilities.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/AssemblyUtilities.cs new file mode 100644 index 000000000000..a0da2a92fb7a --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/AssemblyUtilities.cs @@ -0,0 +1,16 @@ +using System; +using Mono.Cecil; + +namespace Mono.Linker { + + public static class AssemblyUtilities { + + public static bool IsCrossgened (this ModuleDefinition module) + { + return (module.Attributes & ModuleAttributes.ILOnly) == 0 && + (module.Attributes & ModuleAttributes.ILLibrary) != 0; + } + + } + +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/BCL.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/BCL.cs new file mode 100644 index 000000000000..c0f504e587dc --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/BCL.cs @@ -0,0 +1,84 @@ +using Mono.Cecil; + +namespace Mono.Linker { + public static class BCL { + public static class EventTracingForWindows { + public static bool IsEventSourceImplementation (TypeDefinition type, LinkContext context = null) + { + if (!type.IsClass) + return false; + + while (type.BaseType is not null) { + var bt = type.BaseType.Resolve (); + + if (bt is null) { + if (context is not null && !context.IgnoreUnresolved) + throw new ResolutionException (type.BaseType); + + break; + } + + if (IsEventSourceType (bt)) + return true; + + type = bt; + } + + return false; + } + + public static bool IsEventSourceType (TypeReference type) + { + return type.Namespace == "System.Diagnostics.Tracing" && type.Name == "EventSource"; + } + + public static bool IsNonEventAtribute (TypeReference type) + { + return type.Namespace == "System.Diagnostics.Tracing" && type.Name == "NonEventAttribute"; + } + + public static bool IsProviderName (string name) + { + return name == "Keywords" || name == "Tasks" || name == "Opcodes"; + } + } + + public static bool IsIDisposableImplementation (MethodDefinition method) + { + if (method.Name != "Dispose" || method.ReturnType.MetadataType != MetadataType.Void) + return false; + + if (method.HasParameters || method.HasGenericParameters || method.IsStatic) + return false; + + if (!method.IsFinal) + return false; + + return true; + } + + static readonly string [] corlibNames = new [] { + "mscorlib", + "System.Runtime", + "System.Private.CoreLib", + "netstandard" + }; + + public static TypeDefinition FindPredefinedType (string ns, string name, LinkContext context) + { + var cache = context.Resolver.AssemblyCache; + + foreach (var corlibName in corlibNames) { + if (!cache.TryGetValue (corlibName, out AssemblyDefinition corlib)) + continue; + + TypeDefinition type = corlib.MainModule.GetType (ns, name); + // The assembly could be a facade with type forwarders, in which case we don't find the type in this assembly. + if (type is not null) + return type; + } + + return null; + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/ConsoleLogger.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/ConsoleLogger.cs new file mode 100644 index 000000000000..49305240d619 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/ConsoleLogger.cs @@ -0,0 +1,9 @@ +using System; +namespace Mono.Linker { + public class ConsoleLogger : ILogger { + public void LogMessage (MessageImportance importance, string message, params object [] values) + { + Console.WriteLine (message, values); + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/I18nAssemblies.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/I18nAssemblies.cs new file mode 100644 index 000000000000..a14c58fe2aed --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/I18nAssemblies.cs @@ -0,0 +1,47 @@ +// +// I18nAssemblies.cs +// +// Author: +// Jb Evain (jbevain@novell.com) +// +// (C) 2007 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. +// + +using System; + +namespace Mono.Linker { + + [Flags] + public enum I18nAssemblies { + + None = 0, + + CJK = 1, + MidEast = 2, + Other = 4, + Rare = 8, + West = 16, + + All = CJK | MidEast | Other | Rare | West, + Base + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/IDependencyRecorder.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/IDependencyRecorder.cs new file mode 100644 index 000000000000..7ad5b6b8c553 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/IDependencyRecorder.cs @@ -0,0 +1,42 @@ +// +// IDependencyRecorder.cs +// +// Copyright (C) 2017 Microsoft Corporation (http://www.microsoft.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. +// + +namespace Mono.Linker { + /// + /// Abstraction exposed by the linker (mostly MarkStep, but not only) - it will call this interface + /// every time it finds a dependency between two parts of the dependency graph. + /// + public interface IDependencyRecorder { + /// + /// Reports a dependency detected by the linker. + /// + /// The source of the dependency (for example the caller method). + /// The target of the dependency (for example the callee method). + /// true if the target is also marked by the MarkStep. + /// The source and target are typically Cecil metadata objects (MethodDefinition, TypeDefinition, ...) + /// but they can also be the linker steps or really any other object. + void RecordDependency (object source, object target, bool marked); + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/ILogger.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/ILogger.cs new file mode 100644 index 000000000000..6aa49124b883 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/ILogger.cs @@ -0,0 +1,12 @@ +using System; +namespace Mono.Linker { + public enum MessageImportance { + High, + Low, + Normal, + } + + public interface ILogger { + void LogMessage (MessageImportance importance, string message, params object [] values); + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/IReflectionPatternRecorder.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/IReflectionPatternRecorder.cs new file mode 100644 index 000000000000..a3b236a86dea --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/IReflectionPatternRecorder.cs @@ -0,0 +1,60 @@ +// +// IReflectionPatternRecorder.cs +// +// Copyright (C) 2017 Microsoft Corporation (http://www.microsoft.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using Mono.Cecil; + +namespace Mono.Linker { + /// + /// Interface which is called every time the linker inspects a pattern of code involving reflection to determine a more complex + /// dependency. + /// + /// + /// The rules are such that if a given callsite of a "reflectionMethod" gets examined + /// linker will always report it one way or another: + /// - it will either call RecognizedReflectionAccessPattern method when it can figure out exactly the dependency. + /// - or it will call UnrecognizedReflectionAccessPattern with an optional message describing why it could not recognize + /// the pattern. + /// + public interface IReflectionPatternRecorder { + /// + /// Called when the linker recognized a reflection access pattern (and thus was able to correctly apply marking to the accessed item). + /// + /// The method which contains the reflection access pattern. + /// The reflection method which is at the heart of the access pattern. + /// The item accessed through reflection. This can be one of: + /// TypeDefinition, MethodDefinition, PropertyDefinition, FieldDefinition, EventDefinition. + void RecognizedReflectionAccessPattern (MethodDefinition sourceMethod, MethodDefinition reflectionMethod, IMemberDefinition accessedItem); + + /// + /// Called when the linker detected a reflection access but was not able to recognize the entire pattern. + /// + /// The method which contains the reflection access code. + /// The reflection method which is at the heart of the access code. + /// Humanly readable message describing what failed during the pattern recognition. + /// This effectively means that there's a potential hole in the linker marking - some items which are accessed only through + /// reflection may not be marked correctly and thus may fail at runtime. + void UnrecognizedReflectionAccessPattern (MethodDefinition sourceMethod, MethodDefinition reflectionMethod, string message); + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/IXApiVisitor.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/IXApiVisitor.cs new file mode 100644 index 000000000000..204d699ee14e --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/IXApiVisitor.cs @@ -0,0 +1,47 @@ +// +// XApiVisitor.cs +// +// Author: +// Jb Evain (jbevain@novell.com) +// +// (C) 2007 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. +// + +using System.Xml.XPath; + +using Mono.Cecil; + +namespace Mono.Linker { + + public interface IXApiVisitor { + + void OnAssembly (XPathNavigator nav, AssemblyDefinition assembly); + void OnAttribute (XPathNavigator nav); + void OnClass (XPathNavigator nav, TypeDefinition type); + void OnInterface (XPathNavigator nav, TypeDefinition type); + void OnField (XPathNavigator nav, FieldDefinition field); + void OnMethod (XPathNavigator nav, MethodDefinition method); + void OnConstructor (XPathNavigator nav, MethodDefinition method); + void OnProperty (XPathNavigator nav, PropertyDefinition property); + void OnEvent (XPathNavigator nav, EventDefinition evt); + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/Inflater.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/Inflater.cs new file mode 100644 index 000000000000..54c710cf93a4 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/Inflater.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Mono.Cecil; + +namespace Mono.Linker { + class Inflater { + public static TypeReference InflateType (GenericContext context, TypeReference typeReference) + { + var typeDefinition = InflateTypeWithoutException (context, typeReference); + if (typeDefinition is null) + throw new InvalidOperationException ($"Unable to resolve a reference to the type '{typeReference.FullName}' in the assembly '{typeReference.Module.Assembly.FullName}'. Does this type exist in a different assembly in the project?"); + + return typeDefinition; + } + + public static GenericInstanceType InflateType (GenericContext context, TypeDefinition typeDefinition) + { + return ConstructGenericType (context, typeDefinition, typeDefinition.GenericParameters); + } + + public static GenericInstanceType InflateType (GenericContext context, GenericInstanceType genericInstanceType) + { + var inflatedType = ConstructGenericType (context, genericInstanceType.Resolve (), genericInstanceType.GenericArguments); + inflatedType.MetadataToken = genericInstanceType.MetadataToken; + return inflatedType; + } + + public static TypeReference InflateTypeWithoutException (GenericContext context, TypeReference typeReference) + { + var genericParameter = typeReference as GenericParameter; + if (genericParameter is not null) { + if (context.Method is null && genericParameter.Type != GenericParameterType.Type) { + // If no method is specified assume only partial inflation is desired. + return typeReference; + } + + var genericArgumentType = genericParameter.Type == GenericParameterType.Type + ? context.Type.GenericArguments [genericParameter.Position] + : context.Method.GenericArguments [genericParameter.Position]; + + var inflatedType = genericArgumentType; + return inflatedType; + } + var genericInstanceType = typeReference as GenericInstanceType; + if (genericInstanceType is not null) + return InflateType (context, genericInstanceType); + + var arrayType = typeReference as ArrayType; + if (arrayType is not null) + return new ArrayType (InflateType (context, arrayType.ElementType), arrayType.Rank); + + var byReferenceType = typeReference as ByReferenceType; + if (byReferenceType is not null) + return new ByReferenceType (InflateType (context, byReferenceType.ElementType)); + + var pointerType = typeReference as PointerType; + if (pointerType is not null) + return new PointerType (InflateType (context, pointerType.ElementType)); + + var reqModType = typeReference as RequiredModifierType; + if (reqModType is not null) + return InflateTypeWithoutException (context, reqModType.ElementType); + + var optModType = typeReference as OptionalModifierType; + if (optModType is not null) + return InflateTypeWithoutException (context, optModType.ElementType); + + return typeReference.Resolve (); + } + + static GenericInstanceType ConstructGenericType (GenericContext context, TypeDefinition typeDefinition, IEnumerable genericArguments) + { + var inflatedType = new GenericInstanceType (typeDefinition); + + foreach (var genericArgument in genericArguments) + inflatedType.GenericArguments.Add (InflateType (context, genericArgument)); + + return inflatedType; + } + + public class GenericContext { + private readonly GenericInstanceType _type; + private readonly GenericInstanceMethod _method; + + public GenericContext (GenericInstanceType type, GenericInstanceMethod method) + { + _type = type; + _method = method; + } + + public GenericInstanceType Type { + get { return _type; } + } + + public GenericInstanceMethod Method { + get { return _method; } + } + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/KnownMembers.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/KnownMembers.cs new file mode 100644 index 000000000000..7fc1023de6c2 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/KnownMembers.cs @@ -0,0 +1,29 @@ +using Mono.Cecil; + +namespace Mono.Linker { + public class KnownMembers { + public MethodDefinition NotSupportedExceptionCtorString { get; set; } + public MethodDefinition DisablePrivateReflectionAttributeCtor { get; set; } + public MethodDefinition ObjectCtor { get; set; } + + public static bool IsNotSupportedExceptionCtorString (MethodDefinition method) + { + if (!method.IsConstructor || method.IsStatic || !method.HasParameters) + return false; + + if (method.Parameters.Count != 1 || method.Parameters [0].ParameterType.MetadataType != MetadataType.String) + return false; + + return true; + } + + public static bool IsSatelliteAssemblyMarker (MethodDefinition method) + { + if (!method.IsConstructor || method.IsStatic) + return false; + + var declaringType = method.DeclaringType; + return declaringType.Name == "ResourceManager" && declaringType.Namespace == "System.Resources"; + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/LinkContext.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/LinkContext.cs new file mode 100644 index 000000000000..d03dacb8aa25 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/LinkContext.cs @@ -0,0 +1,461 @@ +// +// LinkContext.cs +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// (C) 2006 Jb Evain +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections.Generic; +using System.IO; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace Mono.Linker { + + public class UnintializedContextFactory { + virtual public AnnotationStore CreateAnnotationStore (LinkContext context) => new AnnotationStore (context); + virtual public MarkingHelpers CreateMarkingHelpers (LinkContext context) => new MarkingHelpers (context); + virtual public Tracer CreateTracer (LinkContext context) => new Tracer (context); + } + + public class LinkContext : IDisposable { + + Pipeline _pipeline; + AssemblyAction _coreAction; + AssemblyAction _userAction; + Dictionary _actions; + string _outputDirectory; + readonly Dictionary _parameters; + bool _linkSymbols; + bool _keepTypeForwarderOnlyAssemblies; + bool _keepMembersForDebugger; + bool _ignoreUnresolved; + + AssemblyResolver _resolver; + + ReaderParameters _readerParameters; + ISymbolReaderProvider _symbolReaderProvider; + ISymbolWriterProvider _symbolWriterProvider; + + AnnotationStore _annotations; + + public Pipeline Pipeline { + get { return _pipeline; } + } + + public AnnotationStore Annotations { + get { return _annotations; } + } + + public bool DeterministicOutput { get; set; } + + public string OutputDirectory { + get { return _outputDirectory; } + set { _outputDirectory = value; } + } + + public AssemblyAction CoreAction { + get { return _coreAction; } + set { _coreAction = value; } + } + + public AssemblyAction UserAction { + get { return _userAction; } + set { _userAction = value; } + } + + public bool LinkSymbols { + get { return _linkSymbols; } + set { _linkSymbols = value; } + } + + public bool KeepTypeForwarderOnlyAssemblies { + get { return _keepTypeForwarderOnlyAssemblies; } + set { _keepTypeForwarderOnlyAssemblies = value; } + } + + public bool KeepMembersForDebugger { + get { return _keepMembersForDebugger; } + set { _keepMembersForDebugger = value; } + } + + public bool IgnoreUnresolved { + get { return _ignoreUnresolved; } + set { _ignoreUnresolved = value; } + } + + public bool EnableReducedTracing { get; set; } + + public bool KeepUsedAttributeTypesOnly { get; set; } + + public bool KeepDependencyAttributes { get; set; } + + public bool StripResources { get; set; } + + public List Substitutions { get; private set; } + + public System.Collections.IDictionary Actions { + get { return _actions; } + } + + public AssemblyResolver Resolver { + get { return _resolver; } + } + + public ReaderParameters ReaderParameters { + get { return _readerParameters; } + } + + public ISymbolReaderProvider SymbolReaderProvider { + get { return _symbolReaderProvider; } + set { _symbolReaderProvider = value; } + } + + public ISymbolWriterProvider SymbolWriterProvider { + get { return _symbolWriterProvider; } + set { _symbolWriterProvider = value; } + } + + public bool LogMessages { get; set; } + + public ILogger Logger { private get; set; } = new ConsoleLogger (); + + public MarkingHelpers MarkingHelpers { get; private set; } + + public KnownMembers MarkedKnownMembers { get; private set; } + + public Tracer Tracer { get; private set; } + + public IReflectionPatternRecorder ReflectionPatternRecorder { get; set; } + + public string [] ExcludedFeatures { get; set; } + + public CodeOptimizations DisabledOptimizations { get; set; } + + public bool AddReflectionAnnotations { get; set; } + + public string AssemblyListFile { get; set; } + + public LinkContext (Pipeline pipeline) + : this (pipeline, new AssemblyResolver ()) + { + } + + public LinkContext (Pipeline pipeline, AssemblyResolver resolver) + : this (pipeline, resolver, new ReaderParameters { + AssemblyResolver = resolver + }, new UnintializedContextFactory ()) + { + } + + public LinkContext (Pipeline pipeline, AssemblyResolver resolver, ReaderParameters readerParameters, UnintializedContextFactory factory) + { + _pipeline = pipeline; + _resolver = resolver; + _resolver.Context = this; + _actions = new Dictionary (); + _parameters = new Dictionary (); + _readerParameters = readerParameters; + + SymbolReaderProvider = new DefaultSymbolReaderProvider (false); + + if (factory is null) + throw new ArgumentNullException (nameof (factory)); + + _annotations = factory.CreateAnnotationStore (this); + MarkingHelpers = factory.CreateMarkingHelpers (this); + Tracer = factory.CreateTracer (this); + ReflectionPatternRecorder = new LoggingReflectionPatternRecorder (this); + MarkedKnownMembers = new KnownMembers (); + StripResources = true; + + // See https://github.com/mono/linker/issues/612 + DisabledOptimizations |= CodeOptimizations.UnreachableBodies; + DisabledOptimizations |= CodeOptimizations.ClearInitLocals; + DisabledOptimizations |= CodeOptimizations.IPConstantPropagation; + } + + public void AddSubstitutionFile (string file) + { + if (Substitutions is null) { + Substitutions = new List (); + Substitutions.Add (file); + return; + } + + if (Substitutions.Contains (file)) + return; + + Substitutions.Add (file); + } + + public TypeDefinition GetType (string fullName) + { + int pos = fullName.IndexOf (","); + fullName = TypeReferenceExtensions.ToCecilName (fullName); + if (pos == -1) { + foreach (AssemblyDefinition asm in GetAssemblies ()) { + var type = asm.MainModule.GetType (fullName); + if (type is not null) + return type; + } + + return null; + } + + string asmname = fullName.Substring (pos + 1); + fullName = fullName.Substring (0, pos); + AssemblyDefinition assembly = Resolve (AssemblyNameReference.Parse (asmname)); + return assembly.MainModule.GetType (fullName); + } + + public AssemblyDefinition Resolve (string name) + { + if (File.Exists (name)) { + try { + AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly (name, _readerParameters); + return _resolver.CacheAssembly (assembly); + } catch (Exception e) { + throw new AssemblyResolutionException (new AssemblyNameReference (name, new Version ()), e); + } + } + + return Resolve (new AssemblyNameReference (name, new Version ())); + } + + public AssemblyDefinition Resolve (IMetadataScope scope) + { + AssemblyNameReference reference = GetReference (scope); + try { + AssemblyDefinition assembly = _resolver.Resolve (reference, _readerParameters); + + if (assembly is not null) + RegisterAssembly (assembly); + + return assembly; + } catch (Exception e) { + throw new AssemblyResolutionException (reference, e); + } + } + + public void RegisterAssembly (AssemblyDefinition assembly) + { + if (SeenFirstTime (assembly)) { + SafeReadSymbols (assembly); + SetDefaultAction (assembly); + } + } + + protected bool SeenFirstTime (AssemblyDefinition assembly) + { + return !_annotations.HasAction (assembly); + } + + public virtual void SafeReadSymbols (AssemblyDefinition assembly) + { + if (assembly.MainModule.HasSymbols) + return; + + if (_symbolReaderProvider is null) + throw new ArgumentNullException (nameof (_symbolReaderProvider)); + + try { + var symbolReader = _symbolReaderProvider.GetSymbolReader ( + assembly.MainModule, + assembly.MainModule.FileName); + + if (symbolReader is null) + return; + + try { + assembly.MainModule.ReadSymbols (symbolReader); + } catch { + symbolReader.Dispose (); + return; + } + + // Add symbol reader to annotations only if we have successfully read it + _annotations.AddSymbolReader (assembly, symbolReader); + } catch { } + } + + public virtual ICollection ResolveReferences (AssemblyDefinition assembly) + { + List references = new List (); + if (assembly is null) + return references; + foreach (AssemblyNameReference reference in assembly.MainModule.AssemblyReferences) { + AssemblyDefinition definition = Resolve (reference); + if (definition is not null) + references.Add (definition); + } + return references; + } + + static AssemblyNameReference GetReference (IMetadataScope scope) + { + AssemblyNameReference reference; + if (scope is ModuleDefinition) { + AssemblyDefinition asm = ((ModuleDefinition) scope).Assembly; + reference = asm.Name; + } else + reference = (AssemblyNameReference) scope; + + return reference; + } + + public void SetAction (AssemblyDefinition assembly, AssemblyAction defaultAction) + { + RegisterAssembly (assembly); + + if (!_actions.TryGetValue (assembly.Name.Name, out AssemblyAction action)) + action = defaultAction; + + Annotations.SetAction (assembly, action); + } + + protected void SetDefaultAction (AssemblyDefinition assembly) + { + AssemblyAction action; + + AssemblyNameDefinition name = assembly.Name; + + if (_actions.TryGetValue (name.Name, out action)) { + } else if (IsCore (name)) { + action = _coreAction; + } else { + action = _userAction; + } + + _annotations.SetAction (assembly, action); + } + + public static bool IsCore (AssemblyNameReference name) + { + switch (name.Name) { + case "mscorlib": + case "Accessibility": + case "Mono.Security": + // WPF + case "PresentationFramework": + case "PresentationCore": + case "WindowsBase": + case "UIAutomationProvider": + case "UIAutomationTypes": + case "PresentationUI": + case "ReachFramework": + case "netstandard": + return true; + default: + return name.Name.StartsWith ("System") + || name.Name.StartsWith ("Microsoft"); + } + } + + public virtual AssemblyDefinition [] GetAssemblies () + { + var cache = _resolver.AssemblyCache; + AssemblyDefinition [] asms = new AssemblyDefinition [cache.Count]; + cache.Values.CopyTo (asms, 0); + return asms; + } + + public void SetParameter (string key, string value) + { + _parameters [key] = value; + } + + public bool HasParameter (string key) + { + return _parameters.ContainsKey (key); + } + + public string GetParameter (string key) + { + string val = null; + _parameters.TryGetValue (key, out val); + return val; + } + + public void Dispose () + { + _resolver.Dispose (); + } + + public bool IsFeatureExcluded (string featureName) + { + return ExcludedFeatures is not null && Array.IndexOf (ExcludedFeatures, featureName) >= 0; + } + + public bool IsOptimizationEnabled (CodeOptimizations optimization) + { + return (DisabledOptimizations & optimization) == 0; + } + + public void LogMessage (string message) + { + LogMessage (MessageImportance.Normal, message); + } + + public void LogMessage (MessageImportance importance, string message) + { + if (LogMessages && Logger is not null) + Logger.LogMessage (importance, "{0}", message); + } + } + + [Flags] + public enum CodeOptimizations { + BeforeFieldInit = 1 << 0, + + /// + /// Option to disable removal of overrides of virtual methods when a type is never instantiated + /// + /// Being able to disable this optimization is helpful when trying to troubleshoot problems caused by types created via reflection or from native + /// that do not get an instance constructor marked. + /// + OverrideRemoval = 1 << 1, + + /// + /// Option to disable delaying marking of instance methods until an instance of that type could exist + /// + UnreachableBodies = 1 << 2, + + /// + /// Option to clear the initlocals flag on methods + /// + ClearInitLocals = 1 << 3, + + /// + /// Option to remove .interfaceimpl for interface types that are not used + /// + UnusedInterfaces = 1 << 4, + + /// + /// Option to do interprocedural constant propagation on return values + /// + IPConstantPropagation = 1 << 5 + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/LoadException.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/LoadException.cs new file mode 100644 index 000000000000..2b11298d3a21 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/LoadException.cs @@ -0,0 +1,15 @@ +using System; + +namespace Mono.Linker { + public class LoadException : Exception { + public LoadException (string message) + : base (message) + { + } + + public LoadException (string message, Exception innerException) + : base (message, innerException) + { + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/LoggingReflectionPatternRecorder.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/LoggingReflectionPatternRecorder.cs new file mode 100644 index 000000000000..b64b1a792089 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/LoggingReflectionPatternRecorder.cs @@ -0,0 +1,47 @@ +// +// LoggingReflectionPatternRecorder.cs +// +// Copyright (C) 2017 Microsoft Corporation (http://www.microsoft.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using Mono.Cecil; + +namespace Mono.Linker { + class LoggingReflectionPatternRecorder : IReflectionPatternRecorder { + private readonly LinkContext _context; + + public LoggingReflectionPatternRecorder (LinkContext context) + { + _context = context; + } + + public void RecognizedReflectionAccessPattern (MethodDefinition sourceMethod, MethodDefinition reflectionMethod, IMemberDefinition accessedItem) + { + // Do nothing - there's no logging for successfully recognized patterns + } + + public void UnrecognizedReflectionAccessPattern (MethodDefinition sourceMethod, MethodDefinition reflectionMethod, string message) + { + _context.LogMessage (MessageImportance.Low, message); + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/MarkException.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/MarkException.cs new file mode 100644 index 000000000000..7c84e5646386 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/MarkException.cs @@ -0,0 +1,25 @@ +using System; + +using Mono.Cecil; + +namespace Mono.Linker { + public class MarkException : Exception { + public MethodDefinition Method { get; private set; } + + public MarkException (string message) + : base (message) + { + } + + public MarkException (string message, Exception innerException) + : base (message, innerException) + { + } + + public MarkException (string message, Exception innerException, MethodDefinition method) + : base (message, innerException) + { + Method = method; + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/MarkingHelpers.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/MarkingHelpers.cs new file mode 100644 index 000000000000..4fa7ac0325a7 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/MarkingHelpers.cs @@ -0,0 +1,20 @@ +using System; +using Mono.Cecil; + +namespace Mono.Linker { + public class MarkingHelpers { + protected readonly LinkContext _context; + + public MarkingHelpers (LinkContext context) + { + _context = context; + } + + public void MarkExportedType (ExportedType type, ModuleDefinition module) + { + _context.Annotations.Mark (type); + if (_context.KeepTypeForwarderOnlyAssemblies) + _context.Annotations.Mark (module); + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/MethodAction.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/MethodAction.cs new file mode 100644 index 000000000000..a3550e78396b --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/MethodAction.cs @@ -0,0 +1,38 @@ +// +// MethodAction.cs +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// (C) 2006 Jb Evain +// +// 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. +// + +namespace Mono.Linker { + + public enum MethodAction { + Nothing, + Parse, + ForceParse, + ConvertToStub, + ConvertToThrow, + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/MethodBodyScanner.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/MethodBodyScanner.cs new file mode 100644 index 000000000000..ca726c63260f --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/MethodBodyScanner.cs @@ -0,0 +1,165 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace Mono.Linker { + public static class MethodBodyScanner { + public static bool IsWorthConvertingToThrow (MethodBody body) + { + // Some bodies are cheaper size wise to leave alone than to convert to a throw + Instruction previousMeaningful = null; + int meaningfulCount = 0; + foreach (var ins in body.Instructions) { + // Handle ignoring noops because because (1) it's a valid case to ignore + // and (2) When running the tests on .net core roslyn tosses in no ops + // and that leads to a difference in test results between mcs and .net framework csc. + if (ins.OpCode.Code == Code.Nop) + continue; + + meaningfulCount++; + + if (meaningfulCount == 1 && ins.OpCode.Code == Code.Ret) + return false; + + if (meaningfulCount == 2 && ins.OpCode.Code == Code.Ret && previousMeaningful is not null) { + if (previousMeaningful.OpCode.StackBehaviourPop == StackBehaviour.Pop0) { + switch (previousMeaningful.OpCode.StackBehaviourPush) { + case StackBehaviour.Pushi: + case StackBehaviour.Pushi8: + case StackBehaviour.Pushr4: + case StackBehaviour.Pushr8: + return false; + } + + switch (previousMeaningful.OpCode.Code) { + case Code.Ldnull: + return false; + } + } + } + + if (meaningfulCount >= 2) + return true; + + previousMeaningful = ins; + } + + return true; + } + + public static IEnumerable GetReferencedInterfaces (AnnotationStore annotations, MethodBody body) + { + var possibleStackTypes = AllPossibleStackTypes (body.Method); + if (possibleStackTypes.Count == 0) + return null; + + var interfaceTypes = possibleStackTypes.Where (t => t.IsInterface).ToArray (); + if (interfaceTypes.Length == 0) + return null; + + var interfaceImplementations = new HashSet (); + + // If a type could be on the stack in the body and an interface it implements could be on the stack on the body + // then we need to mark that interface implementation. When this occurs it is not safe to remove the interface implementation from the type + // even if the type is never instantiated + foreach (var type in possibleStackTypes) { + // We only sweep interfaces on classes so that's why we only care about classes + if (!type.IsClass) + continue; + + AddMatchingInterfaces (interfaceImplementations, type, interfaceTypes); + var bases = annotations.GetClassHierarchy (type); + foreach (var @base in bases) { + AddMatchingInterfaces (interfaceImplementations, @base, interfaceTypes); + } + } + + return interfaceImplementations; + } + + static HashSet AllPossibleStackTypes (MethodDefinition method) + { + if (!method.HasBody) + throw new ArgumentException (); + + var body = method.Body; + var types = new HashSet (); + + foreach (VariableDefinition var in body.Variables) + AddIfResolved (types, var.VariableType); + + foreach (var parameter in body.Method.Parameters) + AddIfResolved (types, parameter.ParameterType); + + foreach (ExceptionHandler eh in body.ExceptionHandlers) { + if (eh.HandlerType == ExceptionHandlerType.Catch) { + AddIfResolved (types, eh.CatchType); + } + } + + foreach (Instruction instruction in body.Instructions) { + if (instruction.Operand is FieldReference fieldReference) { + AddIfResolved (types, fieldReference.Resolve ()?.FieldType); + } else if (instruction.Operand is MethodReference methodReference) { + if (methodReference is GenericInstanceMethod genericInstanceMethod) + AddFromGenericInstance (types, genericInstanceMethod); + + if (methodReference.DeclaringType is GenericInstanceType genericInstanceType) + AddFromGenericInstance (types, genericInstanceType); + + var resolvedMethod = methodReference.Resolve (); + if (resolvedMethod is not null) { + if (resolvedMethod.HasParameters) { + foreach (var param in resolvedMethod.Parameters) + AddIfResolved (types, param.ParameterType); + } + + AddFromGenericParameterProvider (types, resolvedMethod); + AddFromGenericParameterProvider (types, resolvedMethod.DeclaringType); + AddIfResolved (types, resolvedMethod.ReturnType); + } + } + } + + return types; + } + + static void AddMatchingInterfaces (HashSet results, TypeDefinition type, TypeDefinition [] interfaceTypes) + { + foreach (var interfaceType in interfaceTypes) { + if (type.HasInterface (interfaceType, out InterfaceImplementation implementation)) + results.Add (implementation); + } + } + + static void AddFromGenericInstance (HashSet set, IGenericInstance instance) + { + if (!instance.HasGenericArguments) + return; + + foreach (var genericArgument in instance.GenericArguments) + AddIfResolved (set, genericArgument); + } + + static void AddFromGenericParameterProvider (HashSet set, IGenericParameterProvider provider) + { + if (!provider.HasGenericParameters) + return; + + foreach (var genericParameter in provider.GenericParameters) { + foreach (var constraint in genericParameter.Constraints) + AddIfResolved (set, constraint.ConstraintType); + } + } + + static void AddIfResolved (HashSet set, TypeReference item) + { + var resolved = item?.Resolve (); + if (resolved is null) + return; + set.Add (resolved); + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/MethodDefinitionExtensions.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/MethodDefinitionExtensions.cs new file mode 100644 index 000000000000..cc1d5b6896ee --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/MethodDefinitionExtensions.cs @@ -0,0 +1,95 @@ +using Mono.Cecil; + +namespace Mono.Linker { + public static class MethodDefinitionExtensions { + public static bool IsDefaultConstructor (this MethodDefinition method) + { + return IsInstanceConstructor (method) && !method.HasParameters; + } + + public static bool IsInstanceConstructor (this MethodDefinition method) + { + return method.IsConstructor && !method.IsStatic; + } + + public static bool IsIntrinsic (this MethodDefinition method) + { + if (!method.HasCustomAttributes) + return false; + + foreach (var ca in method.CustomAttributes) { + var caType = ca.AttributeType; + if (caType.Name == "IntrinsicAttribute" && caType.Namespace == "System.Runtime.CompilerServices") + return true; + } + + return false; + } + + public static bool IsPropertyMethod (this MethodDefinition md) + { + return (md.SemanticsAttributes & MethodSemanticsAttributes.Getter) != 0 || + (md.SemanticsAttributes & MethodSemanticsAttributes.Setter) != 0; + } + + public static bool IsPublicInstancePropertyMethod (this MethodDefinition md) + { + return md.IsPublic && !md.IsStatic && IsPropertyMethod (md); + } + + public static bool IsEventMethod (this MethodDefinition md) + { + return (md.SemanticsAttributes & MethodSemanticsAttributes.AddOn) != 0 || + (md.SemanticsAttributes & MethodSemanticsAttributes.Fire) != 0 || + (md.SemanticsAttributes & MethodSemanticsAttributes.RemoveOn) != 0; + } + + public static PropertyDefinition GetProperty (this MethodDefinition md) + { + TypeDefinition declaringType = md.DeclaringType; + foreach (PropertyDefinition prop in declaringType.Properties) + if (prop.GetMethod == md || prop.SetMethod == md) + return prop; + + return null; + } + + public static EventDefinition GetEvent (this MethodDefinition md) + { + TypeDefinition declaringType = md.DeclaringType; + foreach (EventDefinition evt in declaringType.Events) + if (evt.AddMethod == md || evt.InvokeMethod == md || evt.RemoveMethod == md) + return evt; + + return null; + } + + public static bool IsStaticConstructor (this MethodDefinition method) + { + return method.IsConstructor && method.IsStatic; + } + + public static bool IsFinalizer (this MethodDefinition method) + { + if (method.Name != "Finalize" || method.ReturnType.MetadataType != MetadataType.Void) + return false; + + if (method.HasParameters || method.HasGenericParameters || method.IsStatic) + return false; + + return true; + } + + public static void ClearDebugInformation (this MethodDefinition method) + { + // TODO: This always allocates, update when Cecil catches up + var di = method.DebugInformation; + di.SequencePoints.Clear (); + if (di.Scope is not null) { + di.Scope.Variables.Clear (); + di.Scope.Constants.Clear (); + di.Scope = null; + } + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/MethodReferenceExtensions.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/MethodReferenceExtensions.cs new file mode 100644 index 000000000000..4b9c599fc3d4 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/MethodReferenceExtensions.cs @@ -0,0 +1,26 @@ +using System; +using Mono.Cecil; + +namespace Mono.Linker { + public static class MethodReferenceExtensions { + public static TypeReference GetReturnType (this MethodReference method) + { + var genericInstance = method.DeclaringType as GenericInstanceType; + + if (genericInstance is not null) + return TypeReferenceExtensions.InflateGenericType (genericInstance, method.ReturnType); + + return method.ReturnType; + } + + public static TypeReference GetParameterType (this MethodReference method, int parameterIndex) + { + var genericInstance = method.DeclaringType as GenericInstanceType; + + if (genericInstance is not null) + return TypeReferenceExtensions.InflateGenericType (genericInstance, method.Parameters [parameterIndex].ParameterType); + + return method.Parameters [parameterIndex].ParameterType; + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/OutputException.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/OutputException.cs new file mode 100644 index 000000000000..d6c25ff3f7ef --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/OutputException.cs @@ -0,0 +1,15 @@ +using System; + +namespace Mono.Linker { + public class OutputException : Exception { + public OutputException (string message) + : base (message) + { + } + + public OutputException (string message, Exception innerException) + : base (message, innerException) + { + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/OverrideInformation.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/OverrideInformation.cs new file mode 100644 index 000000000000..b9a5448b37c5 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/OverrideInformation.cs @@ -0,0 +1,39 @@ +using System.Diagnostics; +using Mono.Cecil; + +namespace Mono.Linker { + [DebuggerDisplay ("{Override}")] + public class OverrideInformation { + public readonly MethodDefinition Base; + public readonly MethodDefinition Override; + public readonly InterfaceImplementation MatchingInterfaceImplementation; + + public OverrideInformation (MethodDefinition @base, MethodDefinition @override, InterfaceImplementation matchingInterfaceImplementation = null) + { + Base = @base; + Override = @override; + MatchingInterfaceImplementation = matchingInterfaceImplementation; + } + + public bool IsOverrideOfInterfaceMember { + get { + if (MatchingInterfaceImplementation is not null) + return true; + + return Base.DeclaringType.IsInterface; + } + } + + public TypeDefinition InterfaceType { + get { + if (!IsOverrideOfInterfaceMember) + return null; + + if (MatchingInterfaceImplementation is not null) + return MatchingInterfaceImplementation.InterfaceType.Resolve (); + + return Base.DeclaringType; + } + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/Pipeline.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/Pipeline.cs new file mode 100644 index 000000000000..e9334be89fbb --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/Pipeline.cs @@ -0,0 +1,152 @@ +// +// Pipeline.cs +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// (C) 2006 Jb Evain +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections.Generic; + +using Mono.Linker.Steps; + +namespace Mono.Linker { + + public class Pipeline { + + readonly List _steps; + + public Pipeline () + { + _steps = new List (); + } + + public void PrependStep (IStep step) + { + _steps.Insert (0, step); + } + + public void AppendStep (IStep step) + { + _steps.Add (step); + } + + public void AddStepBefore (Type target, IStep step) + { + for (int i = 0; i < _steps.Count; i++) { + if (target.IsInstanceOfType (_steps [i])) { + _steps.Insert (i, step); + return; + } + } + string msg = String.Format ("Step {0} could not be inserted before (not found) {1}", step, target); + throw new InvalidOperationException (msg); + } + + public void AddStepBefore (IStep target, IStep step) + { + for (int i = 0; i < _steps.Count; i++) { + if (_steps [i] == target) { + _steps.Insert (i, step); + return; + } + } + } + + public void ReplaceStep (Type target, IStep step) + { + AddStepBefore (target, step); + RemoveStep (target); + } + + public void AddStepAfter (Type target, IStep step) + { + for (int i = 0; i < _steps.Count; i++) { + if (target.IsInstanceOfType (_steps [i])) { + if (i == _steps.Count - 1) + _steps.Add (step); + else + _steps.Insert (i + 1, step); + return; + } + } + string msg = String.Format ("Step {0} could not be inserted after (not found) {1}", step, target); + throw new InvalidOperationException (msg); + } + + public void AddStepAfter (IStep target, IStep step) + { + for (int i = 0; i < _steps.Count; i++) { + if (_steps [i] == target) { + if (i == _steps.Count - 1) + _steps.Add (step); + else + _steps.Insert (i + 1, step); + return; + } + } + } + + public void RemoveStep (Type target) + { + for (int i = 0; i < _steps.Count; i++) { + if (_steps [i].GetType () != target) + continue; + + _steps.RemoveAt (i); + break; + } + } + + public void Process (LinkContext context) + { + while (_steps.Count > 0) { + IStep step = _steps [0]; + ProcessStep (context, step); + _steps.Remove (step); + } + } + + protected virtual void ProcessStep (LinkContext context, IStep step) + { + context.Tracer.Push (step); + step.Process (context); + context.Tracer.Pop (); + } + + public IStep [] GetSteps () + { + return _steps.ToArray (); + } + + public bool ContainsStep (Type type) + { + foreach (IStep step in _steps) + if (step.GetType () == type) + return true; + + return false; + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/Tracer.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/Tracer.cs new file mode 100644 index 000000000000..cadbc27aaeae --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/Tracer.cs @@ -0,0 +1,113 @@ +// +// Tracer.cs +// +// Author: +// Radek Doulik +// +// Copyright (C) 2017 Microsoft Corporation (http://www.microsoft.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections.Generic; + +namespace Mono.Linker { + public class Tracer { + protected readonly LinkContext context; + + Stack dependency_stack; + List recorders; + + public Tracer (LinkContext context) + { + this.context = context; + dependency_stack = new Stack (); + } + + public void Finish () + { + dependency_stack = null; + if (recorders is not null) { + foreach (var recorder in recorders) { + if (recorder is IDisposable disposableRecorder) + disposableRecorder.Dispose (); + } + } + + recorders = null; + } + + public void AddRecorder (IDependencyRecorder recorder) + { + if (recorders is null) { + recorders = new List (); + } + + recorders.Add (recorder); + } + + public void Push (object o, bool addDependency = true) + { + if (!IsRecordingEnabled ()) + return; + + if (addDependency && dependency_stack.Count > 0) + AddDependency (o); + + dependency_stack.Push (o); + } + + public void Pop () + { + if (!IsRecordingEnabled ()) + return; + + dependency_stack.Pop (); + } + + bool IsRecordingEnabled () + { + return recorders is not null; + } + + public void AddDirectDependency (object b, object e) + { + ReportDependency (b, e, false); + } + + public void AddDependency (object o, bool marked = false) + { + if (!IsRecordingEnabled ()) + return; + + ReportDependency (dependency_stack.Count > 0 ? dependency_stack.Peek () : null, o, marked); + } + + private void ReportDependency (object source, object target, bool marked) + { + if (IsRecordingEnabled ()) { + foreach (IDependencyRecorder recorder in recorders) { + recorder.RecordDependency (source, target, marked); + } + } + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/TypeDefinitionExtensions.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/TypeDefinitionExtensions.cs new file mode 100644 index 000000000000..40c94f88a7de --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/TypeDefinitionExtensions.cs @@ -0,0 +1,44 @@ + +using System; +using Mono.Cecil; + +namespace Mono.Linker { + public static class TypeDefinitionExtensions { + public static bool HasInterface (this TypeDefinition type, TypeDefinition interfaceType, out InterfaceImplementation implementation) + { + implementation = null; + if (!type.HasInterfaces) + return false; + + foreach (var iface in type.Interfaces) { + if (iface.InterfaceType.Resolve () == interfaceType) { + implementation = iface; + return true; + } + } + + return false; + } + + public static TypeReference GetEnumUnderlyingType (this TypeDefinition enumType) + { + foreach (var field in enumType.Fields) { + if (!field.IsStatic && field.Name == "value__") { + return field.FieldType; + } + } + + throw new MissingFieldException ($"Enum type '{enumType.FullName}' is missing 'value__' field"); + } + + public static bool IsMulticastDelegate (this TypeDefinition td) + { + return td.BaseType?.Name == "MulticastDelegate" && td.BaseType.Namespace == "System"; + } + + public static bool IsSerializable (this TypeDefinition td) + { + return (td.Attributes & TypeAttributes.Serializable) != 0; + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/TypeNameParser.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/TypeNameParser.cs new file mode 100644 index 000000000000..1a86b9b4144e --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/TypeNameParser.cs @@ -0,0 +1,49 @@ +using System; + +namespace Mono.Linker { + public static class TypeNameParser { + public static bool TryParseTypeAssemblyQualifiedName (string value, out string typeName, out string assemblyName) + { + if (string.IsNullOrEmpty (value)) { + typeName = null; + assemblyName = null; + return false; + } + + //Filter the assembly qualified name down to the basic type by removing pointer, reference, and array markers on the type + //We must also convert nested types from + to / to match cecil's formatting + value = value + .Replace ('+', '/') + .Replace ("*", string.Empty) + .Replace ("&", string.Empty); + + while (value.IndexOf ('[') > 0) { + var openidx = value.IndexOf ('['); + var closeidx = value.IndexOf (']'); + + // No matching close ] or out of order + if (closeidx < 0 || closeidx < openidx) { + typeName = null; + assemblyName = null; + return false; + } + + value = value.Remove (openidx, closeidx + 1 - openidx); + } + + var tokens = value.Split (','); + typeName = tokens [0].Trim (); + assemblyName = null; + if (tokens.Length > 1) + assemblyName = tokens [1].Trim (); + + if (string.IsNullOrWhiteSpace (typeName)) { + typeName = null; + assemblyName = null; + return false; + } + + return true; + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/TypePreserve.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/TypePreserve.cs new file mode 100644 index 000000000000..83c0635fc6e7 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/TypePreserve.cs @@ -0,0 +1,37 @@ +// +// TypePreserve.cs +// +// Author: +// Jb Evain (jb@nurv.fr) +// +// (C) 2007 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. +// + +namespace Mono.Linker { + + public enum TypePreserve { + Nothing, // This is actually Declaration + All, + Fields, + Methods + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/TypeReferenceExtensions.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/TypeReferenceExtensions.cs new file mode 100644 index 000000000000..262faa106421 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/TypeReferenceExtensions.cs @@ -0,0 +1,239 @@ +using System; +using Mono.Cecil; +using System.Collections.Generic; +using System.Linq; + +namespace Mono.Linker { + public static class TypeReferenceExtensions { + public static TypeReference GetInflatedBaseType (this TypeReference type) + { + if (type is null) + return null; + + if (type.IsGenericParameter || type.IsByReference || type.IsPointer) + return null; + + var sentinelType = type as SentinelType; + if (sentinelType is not null) + return sentinelType.ElementType.GetInflatedBaseType (); + + var pinnedType = type as PinnedType; + if (pinnedType is not null) + return pinnedType.ElementType.GetInflatedBaseType (); + + var requiredModifierType = type as RequiredModifierType; + if (requiredModifierType is not null) + return requiredModifierType.ElementType.GetInflatedBaseType (); + + var genericInstance = type as GenericInstanceType; + if (genericInstance is not null) { + var baseType = type.Resolve ()?.BaseType; + var baseTypeGenericInstance = baseType as GenericInstanceType; + + if (baseTypeGenericInstance is not null) + return InflateGenericType (genericInstance, baseType); + + return baseType; + } + + return type.Resolve ()?.BaseType; + } + + public static IEnumerable GetInflatedInterfaces (this TypeReference typeRef) + { + var typeDef = typeRef.Resolve (); + + if (typeDef?.HasInterfaces != true) + yield break; + + var genericInstance = typeRef as GenericInstanceType; + if (genericInstance is not null) { + foreach (var interfaceImpl in typeDef.Interfaces) + yield return InflateGenericType (genericInstance, interfaceImpl.InterfaceType); + } else { + foreach (var interfaceImpl in typeDef.Interfaces) + yield return interfaceImpl.InterfaceType; + } + } + + public static TypeReference InflateGenericType (GenericInstanceType genericInstanceProvider, TypeReference typeToInflate) + { + var arrayType = typeToInflate as ArrayType; + if (arrayType is not null) { + var inflatedElementType = InflateGenericType (genericInstanceProvider, arrayType.ElementType); + + if (inflatedElementType != arrayType.ElementType) + return new ArrayType (inflatedElementType, arrayType.Rank); + + return arrayType; + } + + var genericInst = typeToInflate as GenericInstanceType; + if (genericInst is not null) + return MakeGenericType (genericInstanceProvider, genericInst); + + var genericParameter = typeToInflate as GenericParameter; + if (genericParameter is not null) { + if (genericParameter.Owner is MethodReference) + return genericParameter; + + var elementType = genericInstanceProvider.ElementType.Resolve (); + var parameter = elementType.GenericParameters.Single (p => p == genericParameter); + return genericInstanceProvider.GenericArguments [parameter.Position]; + } + + var functionPointerType = typeToInflate as FunctionPointerType; + if (functionPointerType is not null) { + var result = new FunctionPointerType (); + result.ReturnType = InflateGenericType (genericInstanceProvider, functionPointerType.ReturnType); + + for (int i = 0; i < functionPointerType.Parameters.Count; i++) { + var inflatedParameterType = InflateGenericType (genericInstanceProvider, functionPointerType.Parameters [i].ParameterType); + result.Parameters.Add (new ParameterDefinition (inflatedParameterType)); + } + + return result; + } + + var modifierType = typeToInflate as IModifierType; + if (modifierType is not null) { + var modifier = InflateGenericType (genericInstanceProvider, modifierType.ModifierType); + var elementType = InflateGenericType (genericInstanceProvider, modifierType.ElementType); + + if (modifierType is OptionalModifierType) { + return new OptionalModifierType (modifier, elementType); + } + + return new RequiredModifierType (modifier, elementType); + } + + var pinnedType = typeToInflate as PinnedType; + if (pinnedType is not null) { + var elementType = InflateGenericType (genericInstanceProvider, pinnedType.ElementType); + + if (elementType != pinnedType.ElementType) + return new PinnedType (elementType); + + return pinnedType; + } + + var pointerType = typeToInflate as PointerType; + if (pointerType is not null) { + var elementType = InflateGenericType (genericInstanceProvider, pointerType.ElementType); + + if (elementType != pointerType.ElementType) + return new PointerType (elementType); + + return pointerType; + } + + var byReferenceType = typeToInflate as ByReferenceType; + if (byReferenceType is not null) { + var elementType = InflateGenericType (genericInstanceProvider, byReferenceType.ElementType); + + if (elementType != byReferenceType.ElementType) + return new ByReferenceType (elementType); + + return byReferenceType; + } + + var sentinelType = typeToInflate as SentinelType; + if (sentinelType is not null) { + var elementType = InflateGenericType (genericInstanceProvider, sentinelType.ElementType); + + if (elementType != sentinelType.ElementType) + return new SentinelType (elementType); + + return sentinelType; + } + + return typeToInflate; + } + + private static GenericInstanceType MakeGenericType (GenericInstanceType genericInstanceProvider, GenericInstanceType type) + { + var result = new GenericInstanceType (type.ElementType); + + for (var i = 0; i < type.GenericArguments.Count; ++i) { + result.GenericArguments.Add (InflateGenericType (genericInstanceProvider, type.GenericArguments [i])); + } + + return result; + } + + public static IEnumerable GetMethods (this TypeReference type) + { + var typeDef = type.Resolve (); + + if (typeDef?.HasMethods != true) + yield break; + + var genericInstanceType = type as GenericInstanceType; + if (genericInstanceType is not null) { + foreach (var methodDef in typeDef.Methods) + yield return MakeMethodReferenceForGenericInstanceType (genericInstanceType, methodDef); + } else { + foreach (var method in typeDef.Methods) + yield return method; + } + } + + private static MethodReference MakeMethodReferenceForGenericInstanceType (GenericInstanceType genericInstanceType, MethodDefinition methodDef) + { + var method = new MethodReference (methodDef.Name, methodDef.ReturnType, genericInstanceType) { + HasThis = methodDef.HasThis, + ExplicitThis = methodDef.ExplicitThis, + CallingConvention = methodDef.CallingConvention + }; + + foreach (var parameter in methodDef.Parameters) + method.Parameters.Add (new ParameterDefinition (parameter.Name, parameter.Attributes, parameter.ParameterType)); + + foreach (var gp in methodDef.GenericParameters) + method.GenericParameters.Add (new GenericParameter (gp.Name, method)); + + return method; + } + + public static string ToCecilName (this string fullTypeName) + { + return fullTypeName.Replace ('+', '/'); + } + + public static bool HasDefaultConstructor (this TypeReference type) + { + foreach (var m in type.GetMethods ()) { + if (m.HasParameters) + continue; + + var definition = m.Resolve (); + if (definition?.IsDefaultConstructor () == true) + return true; + } + + return false; + } + + public static MethodReference GetDefaultInstanceConstructor (this TypeReference type) + { + foreach (var m in type.GetMethods ()) { + if (m.HasParameters) + continue; + + var definition = m.Resolve (); + if (!definition.IsDefaultConstructor ()) + continue; + + return m; + } + + throw new NotImplementedException (); + } + + public static bool IsTypeOf (this TypeReference type, string ns, string name) + { + return type.Name == name + && type.Namespace == ns; + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/XApiReader.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/XApiReader.cs new file mode 100644 index 000000000000..95a076389429 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/XApiReader.cs @@ -0,0 +1,384 @@ +// +// XApiReader.cs +// +// Author: +// Jb Evain (jbevain@novell.com) +// +// (C) 2007 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. +// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.XPath; + +using Mono.Cecil; +using Mono.Collections.Generic; + +namespace Mono.Linker { + + public class XApiReader { + + static readonly string _name = "name"; + static readonly string _ns = string.Empty; + + LinkContext _context; + XPathDocument _document; + IXApiVisitor _visitor; + + AssemblyDefinition _assembly; + string _namespace; + Stack _types = new Stack (); + StringBuilder _signature; + + public XApiReader (XPathDocument document, IXApiVisitor visitor) + { + _document = document; + _visitor = visitor; + } + + public void Process (LinkContext context) + { + _context = context; + ProcessAssemblies (_document.CreateNavigator ()); + } + + void OnAssembly (XPathNavigator nav) + { + _assembly = GetAssembly (nav); + + _visitor.OnAssembly (nav, _assembly); + + ProcessAttributes (nav); + ProcessNamespaces (nav); + } + + AssemblyDefinition GetAssembly (XPathNavigator nav) + { + AssemblyNameReference name = new AssemblyNameReference ( + GetName (nav), + new Version (GetAttribute (nav, "version"))); + + AssemblyDefinition assembly = _context.Resolve (name); + ProcessReferences (assembly); + return assembly; + } + + void ProcessReferences (AssemblyDefinition assembly) + { + foreach (AssemblyNameReference name in assembly.MainModule.AssemblyReferences) + _context.Resolve (name); + } + + void OnAttribute (XPathNavigator nav) + { + _visitor.OnAttribute (nav); + } + + void PushType (TypeDefinition type) + { + _types.Push (type); + } + + TypeDefinition PeekType () + { + return _types.Peek (); + } + + TypeDefinition PopType () + { + return _types.Pop (); + } + + void OnNamespace (XPathNavigator nav) + { + _namespace = GetName (nav); + + ProcessClasses (nav); + } + + void OnClass (XPathNavigator nav) + { + string name = GetClassName (nav); + + TypeDefinition type = _assembly.MainModule.GetType (name); + if (type is null) + return; + + _visitor.OnClass (nav, type); + + PushType (type); + + ProcessAttributes (nav); + ProcessInterfaces (nav); + ProcessFields (nav); + ProcessMethods (nav); + ProcessConstructors (nav); + ProcessProperties (nav); + ProcessEvents (nav); + ProcessClasses (nav); + + PopType (); + } + + string GetClassName (XPathNavigator nav) + { + if (IsNestedClass ()) + return PeekType ().FullName + "/" + GetName (nav); + + return _namespace + "." + GetName (nav); + } + + bool IsNestedClass () + { + return _types.Count > 0; + } + + void OnField (XPathNavigator nav) + { + TypeDefinition declaring = PeekType (); + + FieldDefinition field = declaring.Fields.FirstOrDefault (f => f.Name == GetName (nav)); + if (field is not null) + _visitor.OnField (nav, field); + + ProcessAttributes (nav); + } + + void OnInterface (XPathNavigator nav) + { + string name = GetName (nav); + + TypeDefinition type = _context.GetType (GetTypeName (name)); + if (type is not null) + _visitor.OnInterface (nav, type); + } + + void OnMethod (XPathNavigator nav) + { + InitMethodSignature (nav); + + ProcessParameters (nav); + + string signature = GetMethodSignature (); + + MethodDefinition method = GetMethod (signature); + if (method is not null) + _visitor.OnMethod (nav, method); + + ProcessAttributes (nav); + } + + MethodDefinition GetMethod (string signature) + { + return GetMethod (PeekType ().Methods, signature); + } + + static MethodDefinition GetMethod (Collection methods, string signature) + { + foreach (MethodDefinition method in methods) + if (signature == GetSignature (method)) + return method; + + return null; + } + + static string GetSignature (MethodDefinition method) + { + return method.ToString ().Replace ("<", "[").Replace (">", "]"); + } + + string GetMethodSignature () + { + _signature.Append (")"); + return _signature.ToString (); + } + + void InitMethodSignature (XPathNavigator nav) + { + _signature = new StringBuilder (); + + string returntype = GetAttribute (nav, "returntype"); + if (returntype is null || returntype.Length == 0) + returntype = "System.Void"; + + _signature.Append (NormalizeTypeName (returntype)); + _signature.Append (" "); + _signature.Append (PeekType ().FullName); + _signature.Append ("::"); + + string name = GetName (nav); + _signature.Append (GetMethodName (name)); + + _signature.Append ("("); + } + + static string GetMethodName (string name) + { + return GetStringBefore (name, "("); + } + + static string NormalizeTypeName (string name) + { + return name.Replace ("+", "/").Replace ("<", "[").Replace (">", "]"); + } + + static string GetTypeName (string name) + { + return GetStringBefore (NormalizeTypeName (name), "["); + } + + static string GetStringBefore (string str, string marker) + { + int pos = str.IndexOf (marker); + if (pos == -1) + return str; + + return str.Substring (0, pos); + } + + void OnParameter (XPathNavigator nav) + { + string type = GetAttribute (nav, "type"); + int pos = int.Parse (GetAttribute (nav, "position")); + + if (pos > 0) + _signature.Append (","); + _signature.Append (NormalizeTypeName (type)); + } + + void OnConstructor (XPathNavigator nav) + { + InitMethodSignature (nav); + + ProcessParameters (nav); + + string signature = GetMethodSignature (); + + MethodDefinition ctor = GetMethod (signature); + if (ctor is not null) + _visitor.OnConstructor (nav, ctor); + + ProcessAttributes (nav); + } + + void OnProperty (XPathNavigator nav) + { + string name = GetName (nav); + TypeDefinition type = PeekType (); + + var property = type.Properties.FirstOrDefault (p => p.Name == name); + if (property is not null) + _visitor.OnProperty (nav, property); + + ProcessAttributes (nav); + ProcessMethods (nav); + } + + void OnEvent (XPathNavigator nav) + { + string name = GetName (nav); + TypeDefinition type = PeekType (); + + EventDefinition evt = type.Events.FirstOrDefault (e => e.Name == name); + if (evt is not null) + _visitor.OnEvent (nav, evt); + + ProcessAttributes (nav); + } + + void ProcessAssemblies (XPathNavigator nav) + { + ProcessChildren (nav, "assemblies//assembly", new OnChildren (OnAssembly)); + } + + void ProcessAttributes (XPathNavigator nav) + { + ProcessChildren (nav, "attributes//attribute", new OnChildren (OnAttribute)); + } + + void ProcessNamespaces (XPathNavigator nav) + { + ProcessChildren (nav, "namespaces//namespace", new OnChildren (OnNamespace)); + } + + void ProcessClasses (XPathNavigator nav) + { + ProcessChildren (nav, "classes//class", new OnChildren (OnClass)); + } + + void ProcessInterfaces (XPathNavigator nav) + { + ProcessChildren (nav, "interfaces//interface", new OnChildren (OnInterface)); + } + + void ProcessFields (XPathNavigator nav) + { + ProcessChildren (nav, "fields//field", new OnChildren (OnField)); + } + + void ProcessMethods (XPathNavigator nav) + { + ProcessChildren (nav, "methods//method", new OnChildren (OnMethod)); + } + + void ProcessConstructors (XPathNavigator nav) + { + ProcessChildren (nav, "constructors//constructor", new OnChildren (OnConstructor)); + } + + void ProcessParameters (XPathNavigator nav) + { + ProcessChildren (nav, "parameters//parameter", new OnChildren (OnParameter)); + } + + void ProcessProperties (XPathNavigator nav) + { + ProcessChildren (nav, "properties//property", new OnChildren (OnProperty)); + } + + void ProcessEvents (XPathNavigator nav) + { + ProcessChildren (nav, "events//event", new OnChildren (OnEvent)); + } + + static void ProcessChildren (XPathNavigator nav, string children, OnChildren action) + { + XPathNodeIterator iterator = nav.Select (children); + while (iterator.MoveNext ()) + action (iterator.Current); + } + + delegate void OnChildren (XPathNavigator nav); + + static string GetName (XPathNavigator nav) + { + return GetAttribute (nav, _name); + } + + static string GetAttribute (XPathNavigator nav, string attribute) + { + return nav.GetAttribute (attribute, _ns); + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/XmlDependencyRecorder.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/XmlDependencyRecorder.cs new file mode 100644 index 000000000000..87d32b8d8bda --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/linker/Linker/XmlDependencyRecorder.cs @@ -0,0 +1,213 @@ +// +// Tracer.cs +// +// Copyright (C) 2017 Microsoft Corporation (http://www.microsoft.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using Mono.Cecil; +using Mono.Linker.Steps; +using System; +using System.IO; +using System.IO.Compression; +using System.Xml; + +namespace Mono.Linker { + /// + /// Class which implements IDependencyRecorder and writes the dependencies into an XML file. + /// + public class XmlDependencyRecorder : IDependencyRecorder, IDisposable { + public const string DefaultDependenciesFileName = "linker-dependencies.xml.gz"; + + private readonly LinkContext context; + private XmlWriter writer; + private Stream stream; + + public XmlDependencyRecorder (LinkContext context, string fileName = null) + { + this.context = context; + + XmlWriterSettings settings = new XmlWriterSettings { + Indent = true, + IndentChars = "\t" + }; + + if (fileName is null) + fileName = DefaultDependenciesFileName; + + if (string.IsNullOrEmpty (Path.GetDirectoryName (fileName)) && !string.IsNullOrEmpty (context.OutputDirectory)) { + fileName = Path.Combine (context.OutputDirectory, fileName); + Directory.CreateDirectory (context.OutputDirectory); + } + + var depsFile = File.OpenWrite (fileName); + + if (Path.GetExtension (fileName) == ".xml") + stream = depsFile; + else + stream = new GZipStream (depsFile, CompressionMode.Compress); + + writer = XmlWriter.Create (stream, settings); + writer.WriteStartDocument (); + writer.WriteStartElement ("dependencies"); + writer.WriteStartAttribute ("version"); + writer.WriteString ("1.2"); + writer.WriteEndAttribute (); + } + + public void Dispose () + { + if (writer is null) + return; + + writer.WriteEndElement (); + writer.WriteEndDocument (); + writer.Flush (); + writer.Dispose (); + stream.Dispose (); + writer = null; + stream = null; + } + + public void RecordDependency (object source, object target, bool marked) + { + if (!ShouldRecord (source) && !ShouldRecord (target)) + return; + + // This is a hack to work around a quirk of MarkStep that results in outputting ~6k edges even with the above ShouldRecord checks. + // What happens is that due to the method queueing in MarkStep, the dependency chain is broken in many cases. And in these cases + // we end up adding an edge for MarkStep -> + // This isn't particularly useful information since it's incomplete, but it's especially not useful in ReducedTracing mode when there is one of these for + // every class library method that was queued. + if (context.EnableReducedTracing && source is MarkStep && !ShouldRecord (target)) + return; + + // This is another hack to prevent useless information from being logged. With the introduction of interface sweeping there are a lot of edges such as + // `e="InterfaceImpl:Mono.Cecil.InterfaceImplementation"` which are useless information. Ideally we would format the interface implementation into a meaningful format + // however I don't think that is worth the effort at the moment. + if (target is InterfaceImplementation) + return; + + if (source != target) { + writer.WriteStartElement ("edge"); + if (marked) + writer.WriteAttributeString ("mark", "1"); + writer.WriteAttributeString ("b", TokenString (source)); + writer.WriteAttributeString ("e", TokenString (target)); + writer.WriteEndElement (); + } + } + + static bool IsAssemblyBound (TypeDefinition td) + { + do { + if (td.IsNestedPrivate || td.IsNestedAssembly || td.IsNestedFamilyAndAssembly) + return true; + + td = td.DeclaringType; + } while (td is not null); + + return false; + } + + string TokenString (object o) + { + if (o is null) + return "N:null"; + + if (o is TypeReference t) { + bool addAssembly = true; + var td = t as TypeDefinition ?? t.Resolve (); + + if (td is not null) { + addAssembly = td.IsNotPublic || IsAssemblyBound (td); + t = td; + } + + var addition = addAssembly ? $":{t.Module}" : ""; + + return $"{(o as IMetadataTokenProvider).MetadataToken.TokenType}:{o}{addition}"; + } + + if (o is IMetadataTokenProvider) + return (o as IMetadataTokenProvider).MetadataToken.TokenType + ":" + o; + + return "Other:" + o; + } + + bool WillAssemblyBeModified (AssemblyDefinition assembly) + { + switch (context.Annotations.GetAction (assembly)) { + case AssemblyAction.Link: + case AssemblyAction.AddBypassNGen: + case AssemblyAction.AddBypassNGenUsed: + return true; + default: + return false; + } + } + + bool ShouldRecord (object o) + { + if (!context.EnableReducedTracing) + return true; + + if (o is TypeDefinition t) + return WillAssemblyBeModified (t.Module.Assembly); + + if (o is IMemberDefinition m) + return WillAssemblyBeModified (m.DeclaringType.Module.Assembly); + + if (o is TypeReference typeRef) { + var resolved = typeRef.Resolve (); + + // Err on the side of caution if we can't resolve + if (resolved is null) + return true; + + return WillAssemblyBeModified (resolved.Module.Assembly); + } + + if (o is MemberReference mRef) { + var resolved = mRef.Resolve (); + + // Err on the side of caution if we can't resolve + if (resolved is null) + return true; + + return WillAssemblyBeModified (resolved.DeclaringType.Module.Assembly); + } + + if (o is ModuleDefinition module) + return WillAssemblyBeModified (module.Assembly); + + if (o is AssemblyDefinition assembly) + return WillAssemblyBeModified (assembly); + + if (o is ParameterDefinition parameter) { + if (parameter.Method is MethodDefinition parameterMethodDefinition) + return WillAssemblyBeModified (parameterMethodDefinition.DeclaringType.Module.Assembly); + } + + return true; + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/ApplyPreserveAttributeBase.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/ApplyPreserveAttributeBase.cs new file mode 100644 index 000000000000..942907d00fde --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/ApplyPreserveAttributeBase.cs @@ -0,0 +1,164 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +using Mono.Linker; +using Mono.Linker.Steps; + +using Mono.Cecil; + +namespace Mono.Tuner { + + public abstract class ApplyPreserveAttributeBase : BaseSubStep { + + // set 'removeAttribute' to true if you want the preserved attribute to be removed from the final assembly + protected abstract bool IsPreservedAttribute (ICustomAttributeProvider provider, CustomAttribute attribute, out bool removeAttribute); + + public override SubStepTargets Targets { + get { + return SubStepTargets.Type + | SubStepTargets.Field + | SubStepTargets.Method + | SubStepTargets.Property + | SubStepTargets.Event; + } + } + + public override bool IsActiveFor (AssemblyDefinition assembly) + { + return !Profile.IsSdkAssembly (assembly) && Annotations.GetAction (assembly) == AssemblyAction.Link; + } + + public override void ProcessType (TypeDefinition type) + { + TryApplyPreserveAttribute (type); + } + + public override void ProcessField (FieldDefinition field) + { + foreach (var attribute in GetPreserveAttributes (field)) + Mark (field, attribute); + } + + public override void ProcessMethod (MethodDefinition method) + { + MarkMethodIfPreserved (method); + } + + public override void ProcessProperty (PropertyDefinition property) + { + foreach (var attribute in GetPreserveAttributes (property)) { + MarkMethod (property.GetMethod, attribute); + MarkMethod (property.SetMethod, attribute); + } + } + + public override void ProcessEvent (EventDefinition @event) + { + foreach (var attribute in GetPreserveAttributes (@event)) { + MarkMethod (@event.AddMethod, attribute); + MarkMethod (@event.InvokeMethod, attribute); + MarkMethod (@event.RemoveMethod, attribute); + } + } + + void MarkMethodIfPreserved (MethodDefinition method) + { + foreach (var attribute in GetPreserveAttributes (method)) + MarkMethod (method, attribute); + } + + void MarkMethod (MethodDefinition method, CustomAttribute preserve_attribute) + { + if (method is null) + return; + + Mark (method, preserve_attribute); + Annotations.SetAction (method, MethodAction.Parse); + } + + void Mark (IMetadataTokenProvider provider, CustomAttribute preserve_attribute) + { + if (IsConditionalAttribute (preserve_attribute)) { + PreserveConditional (provider); + return; + } + + PreserveUnconditional (provider); + } + + void PreserveConditional (IMetadataTokenProvider provider) + { + var method = provider as MethodDefinition; + if (method is null) { + // workaround to support (uncommon but valid) conditional fields form [Preserve] + PreserveUnconditional (provider); + return; + } + + Annotations.AddPreservedMethod (method.DeclaringType, method); + } + + static bool IsConditionalAttribute (CustomAttribute attribute) + { + if (attribute is null) + return false; + + foreach (var named_argument in attribute.Fields) + if (named_argument.Name == "Conditional") + return (bool) named_argument.Argument.Value; + + return false; + } + + void PreserveUnconditional (IMetadataTokenProvider provider) + { + Annotations.Mark (provider); + + var member = provider as IMemberDefinition; + if (member is null || member.DeclaringType is null) + return; + + Mark (member.DeclaringType, null); + } + + void TryApplyPreserveAttribute (TypeDefinition type) + { + foreach (var attribute in GetPreserveAttributes (type)) { + Annotations.Mark (type); + + if (!attribute.HasFields) + continue; + + foreach (var named_argument in attribute.Fields) + if (named_argument.Name == "AllMembers" && (bool) named_argument.Argument.Value) + Annotations.SetPreserve (type, TypePreserve.All); + } + } + + List GetPreserveAttributes (ICustomAttributeProvider provider) + { + List attrs = new List (); + + if (!provider.HasCustomAttributes) + return attrs; + + var attributes = provider.CustomAttributes; + + for (int i = attributes.Count - 1; i >= 0; i--) { + var attribute = attributes [i]; + + bool remote_attribute; + if (!IsPreservedAttribute (provider, attribute, out remote_attribute)) + continue; + + attrs.Add (attribute); + if (remote_attribute) + attributes.RemoveAt (i); + } + + return attrs; + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/CecilRocks.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/CecilRocks.cs new file mode 100644 index 000000000000..6d2abeef6e04 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/CecilRocks.cs @@ -0,0 +1,519 @@ +// +// MethodBodyRocks.cs +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2011 Jb Evain +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections.Generic; +using System.Linq; + +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace Mono.Tuner { + + public static class MethodBodyRocks { + + public static IEnumerable GetAllTypes (this ModuleDefinition self) + { + return self.Types.SelectMany (t => t.GetAllTypes ()); + } + + static IEnumerable GetAllTypes (this TypeDefinition self) + { + yield return self; + + if (!self.HasNestedTypes) + yield break; + + foreach (var type in self.NestedTypes.SelectMany (t => t.GetAllTypes ())) + yield return type; + } + + public static IEnumerable GetMethods (this TypeDefinition self) + { + return self.Methods.Where (m => !m.IsConstructor); + } + + public static IEnumerable GetConstructors (this TypeDefinition self) + { + return self.Methods.Where (m => m.IsConstructor); + } + + public static MethodDefinition GetTypeConstructor (this TypeDefinition self) + { + return self.GetConstructors ().FirstOrDefault (c => c.IsStatic); + } + + public static void SimplifyMacros (this MethodBody self) + { + if (self is null) + throw new ArgumentNullException ("self"); + + foreach (var instruction in self.Instructions) { + if (instruction.OpCode.OpCodeType != OpCodeType.Macro) + continue; + + switch (instruction.OpCode.Code) { + case Code.Ldarg_0: + ExpandMacro (instruction, OpCodes.Ldarg, self.GetParameter (0)); + break; + case Code.Ldarg_1: + ExpandMacro (instruction, OpCodes.Ldarg, self.GetParameter (1)); + break; + case Code.Ldarg_2: + ExpandMacro (instruction, OpCodes.Ldarg, self.GetParameter (2)); + break; + case Code.Ldarg_3: + ExpandMacro (instruction, OpCodes.Ldarg, self.GetParameter (3)); + break; + case Code.Ldloc_0: + ExpandMacro (instruction, OpCodes.Ldloc, self.Variables [0]); + break; + case Code.Ldloc_1: + ExpandMacro (instruction, OpCodes.Ldloc, self.Variables [1]); + break; + case Code.Ldloc_2: + ExpandMacro (instruction, OpCodes.Ldloc, self.Variables [2]); + break; + case Code.Ldloc_3: + ExpandMacro (instruction, OpCodes.Ldloc, self.Variables [3]); + break; + case Code.Stloc_0: + ExpandMacro (instruction, OpCodes.Stloc, self.Variables [0]); + break; + case Code.Stloc_1: + ExpandMacro (instruction, OpCodes.Stloc, self.Variables [1]); + break; + case Code.Stloc_2: + ExpandMacro (instruction, OpCodes.Stloc, self.Variables [2]); + break; + case Code.Stloc_3: + ExpandMacro (instruction, OpCodes.Stloc, self.Variables [3]); + break; + case Code.Ldarg_S: + instruction.OpCode = OpCodes.Ldarg; + break; + case Code.Ldarga_S: + instruction.OpCode = OpCodes.Ldarga; + break; + case Code.Starg_S: + instruction.OpCode = OpCodes.Starg; + break; + case Code.Ldloc_S: + instruction.OpCode = OpCodes.Ldloc; + break; + case Code.Ldloca_S: + instruction.OpCode = OpCodes.Ldloca; + break; + case Code.Stloc_S: + instruction.OpCode = OpCodes.Stloc; + break; + case Code.Ldc_I4_M1: + ExpandMacro (instruction, OpCodes.Ldc_I4, -1); + break; + case Code.Ldc_I4_0: + ExpandMacro (instruction, OpCodes.Ldc_I4, 0); + break; + case Code.Ldc_I4_1: + ExpandMacro (instruction, OpCodes.Ldc_I4, 1); + break; + case Code.Ldc_I4_2: + ExpandMacro (instruction, OpCodes.Ldc_I4, 2); + break; + case Code.Ldc_I4_3: + ExpandMacro (instruction, OpCodes.Ldc_I4, 3); + break; + case Code.Ldc_I4_4: + ExpandMacro (instruction, OpCodes.Ldc_I4, 4); + break; + case Code.Ldc_I4_5: + ExpandMacro (instruction, OpCodes.Ldc_I4, 5); + break; + case Code.Ldc_I4_6: + ExpandMacro (instruction, OpCodes.Ldc_I4, 6); + break; + case Code.Ldc_I4_7: + ExpandMacro (instruction, OpCodes.Ldc_I4, 7); + break; + case Code.Ldc_I4_8: + ExpandMacro (instruction, OpCodes.Ldc_I4, 8); + break; + case Code.Ldc_I4_S: + ExpandMacro (instruction, OpCodes.Ldc_I4, (int) (sbyte) instruction.Operand); + break; + case Code.Br_S: + instruction.OpCode = OpCodes.Br; + break; + case Code.Brfalse_S: + instruction.OpCode = OpCodes.Brfalse; + break; + case Code.Brtrue_S: + instruction.OpCode = OpCodes.Brtrue; + break; + case Code.Beq_S: + instruction.OpCode = OpCodes.Beq; + break; + case Code.Bge_S: + instruction.OpCode = OpCodes.Bge; + break; + case Code.Bgt_S: + instruction.OpCode = OpCodes.Bgt; + break; + case Code.Ble_S: + instruction.OpCode = OpCodes.Ble; + break; + case Code.Blt_S: + instruction.OpCode = OpCodes.Blt; + break; + case Code.Bne_Un_S: + instruction.OpCode = OpCodes.Bne_Un; + break; + case Code.Bge_Un_S: + instruction.OpCode = OpCodes.Bge_Un; + break; + case Code.Bgt_Un_S: + instruction.OpCode = OpCodes.Bgt_Un; + break; + case Code.Ble_Un_S: + instruction.OpCode = OpCodes.Ble_Un; + break; + case Code.Blt_Un_S: + instruction.OpCode = OpCodes.Blt_Un; + break; + case Code.Leave_S: + instruction.OpCode = OpCodes.Leave; + break; + } + } + } + + static void ExpandMacro (Instruction instruction, OpCode opcode, object operand) + { + instruction.OpCode = opcode; + instruction.Operand = operand; + } + + static void MakeMacro (Instruction instruction, OpCode opcode) + { + instruction.OpCode = opcode; + instruction.Operand = null; + } + + public static void OptimizeMacros (this MethodBody self) + { + if (self is null) + throw new ArgumentNullException ("self"); + + var method = self.Method; + + foreach (var instruction in self.Instructions) { + int index; + switch (instruction.OpCode.Code) { + case Code.Ldarg: + index = ((ParameterDefinition) instruction.Operand).Index; + if (index == -1 && instruction.Operand == self.ThisParameter) + index = 0; + else if (method.HasThis) + index++; + + switch (index) { + case 0: + MakeMacro (instruction, OpCodes.Ldarg_0); + break; + case 1: + MakeMacro (instruction, OpCodes.Ldarg_1); + break; + case 2: + MakeMacro (instruction, OpCodes.Ldarg_2); + break; + case 3: + MakeMacro (instruction, OpCodes.Ldarg_3); + break; + default: + if (index < 256) + ExpandMacro (instruction, OpCodes.Ldarg_S, instruction.Operand); + break; + } + break; + case Code.Ldloc: + index = ((VariableDefinition) instruction.Operand).Index; + switch (index) { + case 0: + MakeMacro (instruction, OpCodes.Ldloc_0); + break; + case 1: + MakeMacro (instruction, OpCodes.Ldloc_1); + break; + case 2: + MakeMacro (instruction, OpCodes.Ldloc_2); + break; + case 3: + MakeMacro (instruction, OpCodes.Ldloc_3); + break; + default: + if (index < 256) + ExpandMacro (instruction, OpCodes.Ldloc_S, instruction.Operand); + break; + } + break; + case Code.Stloc: + index = ((VariableDefinition) instruction.Operand).Index; + switch (index) { + case 0: + MakeMacro (instruction, OpCodes.Stloc_0); + break; + case 1: + MakeMacro (instruction, OpCodes.Stloc_1); + break; + case 2: + MakeMacro (instruction, OpCodes.Stloc_2); + break; + case 3: + MakeMacro (instruction, OpCodes.Stloc_3); + break; + default: + if (index < 256) + ExpandMacro (instruction, OpCodes.Stloc_S, instruction.Operand); + break; + } + break; + case Code.Ldarga: + index = ((ParameterDefinition) instruction.Operand).Index; + if (index == -1 && instruction.Operand == self.ThisParameter) + index = 0; + else if (method.HasThis) + index++; + if (index < 256) + ExpandMacro (instruction, OpCodes.Ldarga_S, instruction.Operand); + break; + case Code.Ldloca: + if (((VariableDefinition) instruction.Operand).Index < 256) + ExpandMacro (instruction, OpCodes.Ldloca_S, instruction.Operand); + break; + case Code.Ldc_I4: + int i = (int) instruction.Operand; + switch (i) { + case -1: + MakeMacro (instruction, OpCodes.Ldc_I4_M1); + break; + case 0: + MakeMacro (instruction, OpCodes.Ldc_I4_0); + break; + case 1: + MakeMacro (instruction, OpCodes.Ldc_I4_1); + break; + case 2: + MakeMacro (instruction, OpCodes.Ldc_I4_2); + break; + case 3: + MakeMacro (instruction, OpCodes.Ldc_I4_3); + break; + case 4: + MakeMacro (instruction, OpCodes.Ldc_I4_4); + break; + case 5: + MakeMacro (instruction, OpCodes.Ldc_I4_5); + break; + case 6: + MakeMacro (instruction, OpCodes.Ldc_I4_6); + break; + case 7: + MakeMacro (instruction, OpCodes.Ldc_I4_7); + break; + case 8: + MakeMacro (instruction, OpCodes.Ldc_I4_8); + break; + default: + if (i >= -128 && i < 128) + ExpandMacro (instruction, OpCodes.Ldc_I4_S, (sbyte) i); + break; + } + break; + } + } + + OptimizeBranches (self); + } + + static void OptimizeBranches (MethodBody body) + { + ComputeOffsets (body); + + foreach (var instruction in body.Instructions) { + if (instruction.OpCode.OperandType != OperandType.InlineBrTarget) + continue; + + if (OptimizeBranch (instruction)) + ComputeOffsets (body); + } + } + + static bool OptimizeBranch (Instruction instruction) + { + var offset = ((Instruction) instruction.Operand).Offset - (instruction.Offset + instruction.OpCode.Size + 4); + if (!(offset >= -128 && offset <= 127)) + return false; + + switch (instruction.OpCode.Code) { + case Code.Br: + instruction.OpCode = OpCodes.Br_S; + break; + case Code.Brfalse: + instruction.OpCode = OpCodes.Brfalse_S; + break; + case Code.Brtrue: + instruction.OpCode = OpCodes.Brtrue_S; + break; + case Code.Beq: + instruction.OpCode = OpCodes.Beq_S; + break; + case Code.Bge: + instruction.OpCode = OpCodes.Bge_S; + break; + case Code.Bgt: + instruction.OpCode = OpCodes.Bgt_S; + break; + case Code.Ble: + instruction.OpCode = OpCodes.Ble_S; + break; + case Code.Blt: + instruction.OpCode = OpCodes.Blt_S; + break; + case Code.Bne_Un: + instruction.OpCode = OpCodes.Bne_Un_S; + break; + case Code.Bge_Un: + instruction.OpCode = OpCodes.Bge_Un_S; + break; + case Code.Bgt_Un: + instruction.OpCode = OpCodes.Bgt_Un_S; + break; + case Code.Ble_Un: + instruction.OpCode = OpCodes.Ble_Un_S; + break; + case Code.Blt_Un: + instruction.OpCode = OpCodes.Blt_Un_S; + break; + case Code.Leave: + instruction.OpCode = OpCodes.Leave_S; + break; + } + + return true; + } + + static void ComputeOffsets (MethodBody body) + { + var offset = 0; + foreach (var instruction in body.Instructions) { + instruction.Offset = offset; + offset += instruction.GetSize (); + } + } + + public static ParameterDefinition GetParameter (this MethodBody self, int index) + { + var method = self.Method; + + if (method.HasThis) { + if (index == 0) + return self.ThisParameter; + + index--; + } + + var parameters = method.Parameters; + + if (index < 0 || index >= parameters.Count) + return null; + + return parameters [index]; + } + + public static bool Implements (this TypeReference self, string interfaceName) + { + if (interfaceName is null) + throw new ArgumentNullException ("interfaceName"); + if (self is null) + return false; + + TypeDefinition type = self.Resolve (); + if (type is null) + return false; // not enough information available + + // special case, check if we implement ourselves + if (type.IsInterface && (type.FullName == interfaceName)) + return true; + + return Implements (type, interfaceName, (interfaceName.IndexOf ('`') >= 0)); + } + + public static bool Implements (TypeDefinition type, string interfaceName, bool generic) + { + while (type is not null) { + // does the type implements it itself + if (type.HasInterfaces) { + foreach (var iface in type.Interfaces) { + string fullname = (generic) ? iface.InterfaceType.GetElementType ().FullName : iface.InterfaceType.FullName; + if (fullname == interfaceName) + return true; + //if not, then maybe one of its parent interfaces does + if (Implements (iface.InterfaceType.Resolve (), interfaceName, generic)) + return true; + } + } + + type = type.BaseType is not null ? type.BaseType.Resolve () : null; + } + return false; + } + + public static bool Inherits (this TypeReference self, string @namespace, string name) + { + if (@namespace is null) + throw new ArgumentNullException ("namespace"); + if (name is null) + throw new ArgumentNullException ("name"); + if (self is null) + return false; + + TypeReference current = self.Resolve (); + while (current is not null) { + if (current.Is (@namespace, name)) + return true; + if (current.Is ("System", "Object")) + return false; + + TypeDefinition td = current.Resolve (); + if (td is null) + return false; // could not resolve type + current = td.BaseType; + } + return false; + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/CustomizeActions.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/CustomizeActions.cs new file mode 100644 index 000000000000..38e2ad97fbf4 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/CustomizeActions.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using Mono.Linker; +using Mono.Linker.Steps; + +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace Mono.Tuner { + + public class CustomizeActions : BaseStep { + + readonly bool link_sdk_only; + readonly HashSet skipped_assemblies; + + public CustomizeActions (bool link_sdk_only, IEnumerable skipped_assemblies) + { + this.link_sdk_only = link_sdk_only; + this.skipped_assemblies = new HashSet (skipped_assemblies); + } + + protected override void ProcessAssembly (AssemblyDefinition assembly) + { + if (!IsSkipped (assembly) && IsLinked (assembly)) { + if (!Annotations.HasAction (assembly)) // stray assembly not picked up when resolving references + Annotations.SetAction (assembly, AssemblyAction.Link); + return; + } + ProcessUserAssembly (assembly); + } + + protected virtual bool IsPreservedAttribute (CustomAttribute attribute) + { + // [assembly: Preserve (type)] does not preserve all the code in the assembly, in fact it might + // not preserve anything in _this_ assembly, but something in a separate assembly (reference) + if (attribute.HasConstructorArguments) + return false; + return (attribute.AttributeType.Name == "PreserveAttribute"); + } + + protected virtual bool IsLinkerSafeAttribute (CustomAttribute attribute) + { + return (attribute.AttributeType.Name == "LinkerSafeAttribute"); + } + + const ModuleAttributes Supported = ModuleAttributes.ILOnly | ModuleAttributes.Required32Bit | + ModuleAttributes.Preferred32Bit | ModuleAttributes.StrongNameSigned; + + protected virtual bool IsSkipped (AssemblyDefinition assembly) + { + // Cecil can't save back mixed-mode assemblies - so we can't link them + if ((assembly.MainModule.Attributes & ~Supported) != 0) + return true; + + if (assembly.HasCustomAttributes) { + foreach (var ca in assembly.CustomAttributes) { + if (IsPreservedAttribute (ca)) + return true; + } + } + return skipped_assemblies.Contains (assembly.Name.Name); + } + + protected virtual bool IsLinked (AssemblyDefinition assembly) + { + // LinkAll + if (!link_sdk_only) + return true; + // Link SDK : applies to BCL/SDK and product assembly (e.g. monotouch.dll) + if (Profile.IsSdkAssembly (assembly)) + return true; + if (Profile.IsProductAssembly (assembly)) + return true; + // the assembly can be marked with [LinkAssembly] + if (assembly.HasCustomAttributes) { + foreach (var ca in assembly.CustomAttributes) { + if (IsLinkerSafeAttribute (ca)) + return true; + } + } + return false; + } + + protected void ProcessUserAssembly (AssemblyDefinition assembly) + { + ResolveFromAssemblyStep.ProcessLibrary (Context, assembly); + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/Dispatcher.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/Dispatcher.cs new file mode 100644 index 000000000000..e10c7b6c6f91 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/Dispatcher.cs @@ -0,0 +1,310 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +using Mono.Linker; +using Mono.Linker.Steps; + +using Mono.Cecil; + +namespace Mono.Tuner { + + [Flags] + public enum SubStepTargets { + None = 0, + + Assembly = 1, + Type = 2, + Field = 4, + Method = 8, + Property = 16, + Event = 32, + } + + public interface ISubStep { + + SubStepTargets Targets { get; } + + void Initialize (LinkContext context); + bool IsActiveFor (AssemblyDefinition assembly); + + void ProcessAssembly (AssemblyDefinition assembly); + void ProcessType (TypeDefinition type); + void ProcessField (FieldDefinition field); + void ProcessMethod (MethodDefinition method); + void ProcessProperty (PropertyDefinition property); + void ProcessEvent (EventDefinition @event); + } + + public abstract class BaseSubStep : ISubStep { + + protected LinkContext context; + + public AnnotationStore Annotations { + get { return context.Annotations; } + } + + public abstract SubStepTargets Targets { get; } + + public virtual void Initialize (LinkContext context) + { + this.context = context; + } + + public virtual bool IsActiveFor (AssemblyDefinition assembly) + { + return true; + } + + public virtual void ProcessAssembly (AssemblyDefinition assembly) + { + } + + public virtual void ProcessType (TypeDefinition type) + { + } + + public virtual void ProcessField (FieldDefinition field) + { + } + + public virtual void ProcessMethod (MethodDefinition method) + { + } + + public virtual void ProcessProperty (PropertyDefinition property) + { + } + + public virtual void ProcessEvent (EventDefinition @event) + { + } + } + + public class SubStepDispatcher : IStep, IEnumerable { + + List substeps = new List (); + + List on_assemblies; + List on_types; + List on_fields; + List on_methods; + List on_properties; + List on_events; + + Tracer tracer; + + public void Add (ISubStep substep) + { + substeps.Add (substep); + } + + public void Process (LinkContext context) + { + tracer = context.Tracer; + + InitializeSubSteps (context); + + BrowseAssemblies (context.GetAssemblies ()); + } + + void Push (ISubStep subStep) + { + if (tracer is not null) + tracer.Push (subStep); + } + + void Pop () + { + if (tracer is not null) + tracer.Pop (); + } + + static bool HasSubSteps (List substeps) + { + return substeps is not null && substeps.Count > 0; + } + + void BrowseAssemblies (IEnumerable assemblies) + { + foreach (var assembly in assemblies) { + CategorizeSubSteps (assembly); + + if (HasSubSteps (on_assemblies)) + DispatchAssembly (assembly); + + if (!ShouldDispatchTypes ()) + continue; + + BrowseTypes (assembly.MainModule.Types); + } + } + + bool ShouldDispatchTypes () + { + return HasSubSteps (on_types) + || HasSubSteps (on_fields) + || HasSubSteps (on_methods) + || HasSubSteps (on_properties) + || HasSubSteps (on_events); + } + + void BrowseTypes (ICollection types) + { + foreach (TypeDefinition type in types) { + DispatchType (type); + + if (type.HasFields && HasSubSteps (on_fields)) + BrowseFields (type.Fields); + + if (type.HasMethods && HasSubSteps (on_methods)) + BrowseMethods (type.Methods); + + if (type.HasProperties && HasSubSteps (on_properties)) + BrowseProperties (type.Properties); + + if (type.HasEvents && HasSubSteps (on_events)) + BrowseEvents (type.Events); + + if (type.HasNestedTypes) + BrowseTypes (type.NestedTypes); + } + } + + void BrowseFields (ICollection fields) + { + foreach (FieldDefinition field in fields) + DispatchField (field); + } + + void BrowseMethods (ICollection methods) + { + foreach (MethodDefinition method in methods) + DispatchMethod (method); + } + + void BrowseProperties (ICollection properties) + { + foreach (PropertyDefinition property in properties) + DispatchProperty (property); + } + + void BrowseEvents (ICollection events) + { + foreach (EventDefinition @event in events) + DispatchEvent (@event); + } + + void DispatchAssembly (AssemblyDefinition assembly) + { + foreach (var substep in on_assemblies) { + Push (substep); + substep.ProcessAssembly (assembly); + Pop (); + } + } + + void DispatchType (TypeDefinition type) + { + foreach (var substep in on_types) { + Push (substep); + substep.ProcessType (type); + Pop (); + } + } + + void DispatchField (FieldDefinition field) + { + foreach (var substep in on_fields) { + Push (substep); + substep.ProcessField (field); + Pop (); + } + } + + void DispatchMethod (MethodDefinition method) + { + foreach (var substep in on_methods) { + Push (substep); + substep.ProcessMethod (method); + Pop (); + } + } + + void DispatchProperty (PropertyDefinition property) + { + foreach (var substep in on_properties) { + Push (substep); + substep.ProcessProperty (property); + Pop (); + } + } + + void DispatchEvent (EventDefinition @event) + { + foreach (var substep in on_events) { + Push (substep); + substep.ProcessEvent (@event); + Pop (); + } + } + + void InitializeSubSteps (LinkContext context) + { + foreach (var substep in substeps) + substep.Initialize (context); + } + + void CategorizeSubSteps (AssemblyDefinition assembly) + { + on_assemblies = null; + on_types = null; + on_fields = null; + on_methods = null; + on_properties = null; + on_events = null; + + foreach (var substep in substeps) + CategorizeSubStep (substep, assembly); + } + + void CategorizeSubStep (ISubStep substep, AssemblyDefinition assembly) + { + if (!substep.IsActiveFor (assembly)) + return; + + CategorizeTarget (substep, SubStepTargets.Assembly, ref on_assemblies); + CategorizeTarget (substep, SubStepTargets.Type, ref on_types); + CategorizeTarget (substep, SubStepTargets.Field, ref on_fields); + CategorizeTarget (substep, SubStepTargets.Method, ref on_methods); + CategorizeTarget (substep, SubStepTargets.Property, ref on_properties); + CategorizeTarget (substep, SubStepTargets.Event, ref on_events); + } + + static void CategorizeTarget (ISubStep substep, SubStepTargets target, ref List list) + { + if (!Targets (substep, target)) + return; + + if (list is null) + list = new List (); + + list.Add (substep); + } + + static bool Targets (ISubStep substep, SubStepTargets target) + { + return (substep.Targets & target) == target; + } + + IEnumerator IEnumerable.GetEnumerator () + { + return GetEnumerator (); + } + + public IEnumerator GetEnumerator () + { + return substeps.GetEnumerator (); + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/Extensions.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/Extensions.cs new file mode 100644 index 000000000000..23ba6dc0ac87 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/Extensions.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; + +using Mono.Cecil; + +using Mono.Linker; + +namespace Mono.Tuner { + + public static partial class Extensions { + + public static bool TryGetLinkedAssembly (this LinkContext context, string name, out AssemblyDefinition assembly) + { + assembly = GetAssembly (context, name); + if (assembly is null) + return false; + + return context.Annotations.GetAction (assembly) == AssemblyAction.Link; + } + + public static AssemblyDefinition GetAssembly (this LinkContext context, string assembly_name) + { + foreach (var assembly in context.GetAssemblies ()) + if (assembly.Name.Name == assembly_name) + return assembly; + + return null; + } + + // note: direct check, no inheritance + public static bool Is (this TypeReference type, string @namespace, string name) + { + return ((type is not null) && (type.Name == name) && (type.Namespace == @namespace)); + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/FixModuleFlags.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/FixModuleFlags.cs new file mode 100644 index 000000000000..2bb01ab9bc50 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/FixModuleFlags.cs @@ -0,0 +1,20 @@ +using System; + +using Mono.Linker; +using Mono.Linker.Steps; + +using Mono.Cecil; + +namespace Mono.Tuner { + + public class FixModuleFlags : BaseStep { + + protected override void ProcessAssembly (AssemblyDefinition assembly) + { + if (Annotations.GetAction (assembly) != AssemblyAction.Link) + return; + + assembly.MainModule.Attributes = ModuleAttributes.ILOnly; + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/PreserveCrypto.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/PreserveCrypto.cs new file mode 100644 index 000000000000..5e9d3ee0726b --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/PreserveCrypto.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +using Mono.Linker; +using Mono.Linker.Steps; + +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace Mono.Tuner { + + public class PreserveCrypto : IStep { + + AnnotationStore annotations; + + public void Process (LinkContext context) + { + annotations = context.Annotations; + + ProcessCorlib (context); + ProcessSystemCore (context); + } + + void ProcessCorlib (LinkContext context) + { + AssemblyDefinition corlib; + if (!context.TryGetLinkedAssembly ("mscorlib", out corlib)) + return; + + AddPreserveInfo (corlib, "DES", "DESCryptoServiceProvider"); + AddPreserveInfo (corlib, "DSA", "DSACryptoServiceProvider"); + AddPreserveInfo (corlib, "RandomNumberGenerator", "RNGCryptoServiceProvider"); + AddPreserveInfo (corlib, "SHA1", "SHA1CryptoServiceProvider"); + AddPreserveInfo (corlib, "SHA1", "SHA1Managed"); + AddPreserveInfo (corlib, "MD5", "MD5CryptoServiceProvider"); + AddPreserveInfo (corlib, "RC2", "RC2CryptoServiceProvider"); + AddPreserveInfo (corlib, "TripleDES", "TripleDESCryptoServiceProvider"); + + AddPreserveInfo (corlib, "Rijndael", "RijndaelManaged"); + AddPreserveInfo (corlib, "RIPEMD160", "RIPEMD160Managed"); + AddPreserveInfo (corlib, "SHA256", "SHA256Managed"); + AddPreserveInfo (corlib, "SHA384", "SHA384Managed"); + AddPreserveInfo (corlib, "SHA512", "SHA512Managed"); + + AddPreserveInfo (corlib, "HMAC", "HMACMD5"); + AddPreserveInfo (corlib, "HMAC", "HMACRIPEMD160"); + AddPreserveInfo (corlib, "HMAC", "HMACSHA1"); + AddPreserveInfo (corlib, "HMAC", "HMACSHA256"); + AddPreserveInfo (corlib, "HMAC", "HMACSHA384"); + AddPreserveInfo (corlib, "HMAC", "HMACSHA512"); + + AddPreserveInfo (corlib, "HMACMD5", "MD5CryptoServiceProvider"); + AddPreserveInfo (corlib, "HMACRIPEMD160", "RIPEMD160Managed"); + AddPreserveInfo (corlib, "HMACSHA1", "SHA1CryptoServiceProvider"); + AddPreserveInfo (corlib, "HMACSHA1", "SHA1Managed"); + AddPreserveInfo (corlib, "HMACSHA256", "SHA256Managed"); + AddPreserveInfo (corlib, "HMACSHA384", "SHA384Managed"); + AddPreserveInfo (corlib, "HMACSHA512", "SHA512Managed"); + + TryAddPreserveInfo (corlib, "Aes", "AesManaged"); + + var corlibAes = GetCryptoType (corlib, "Aes"); + Preserve (corlibAes, GetCryptoType (corlib, "AesManaged")); + + AssemblyDefinition syscore; + if (context.TryGetLinkedAssembly ("System.Core", out syscore)) + Preserve (corlibAes, GetCryptoType (syscore, "AesCryptoServiceProvider")); + } + + void ProcessSystemCore (LinkContext context) + { + AssemblyDefinition syscore; + if (!context.TryGetLinkedAssembly ("System.Core", out syscore)) + return; + + // AddPreserveInfo (syscore, "Aes", "AesCryptoServiceProvider"); + TryAddPreserveInfo (syscore, "Aes", "AesManaged"); + } + + bool TryAddPreserveInfo (AssemblyDefinition assembly, string name, string type) + { + var marker = GetCryptoType (assembly, name); + if (marker is null) + return false; + + var implementation = GetCryptoType (assembly, type); + if (implementation is null) + return false; + + Preserve (marker, implementation); + return true; + } + + void AddPreserveInfo (AssemblyDefinition assembly, string name, string type) + { + var marker = GetCryptoType (assembly, name); + if (marker is null) + throw new ArgumentException (name); + + var implementation = GetCryptoType (assembly, type); + if (implementation is null) + throw new ArgumentException (type); + + Preserve (marker, implementation); + } + + void Preserve (TypeDefinition marker, TypeDefinition implementation) + { + if (marker is null || implementation is null) + return; + foreach (var constructor in implementation.GetConstructors ()) + annotations.AddPreservedMethod (marker, constructor); + } + + TypeDefinition GetCryptoType (AssemblyDefinition assembly, string name) + { + return assembly.MainModule.GetType ("System.Security.Cryptography." + name); + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/PreserveSoapHttpClients.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/PreserveSoapHttpClients.cs new file mode 100644 index 000000000000..ee486cae93d8 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/PreserveSoapHttpClients.cs @@ -0,0 +1,90 @@ +using System; + +using Mono.Linker; + +using Mono.Cecil; + +namespace Mono.Tuner { + + public class PreserveSoapHttpClients : BaseSubStep { + + public override SubStepTargets Targets { + get { return SubStepTargets.Type; } + } + + public override bool IsActiveFor (AssemblyDefinition assembly) + { + return Annotations.GetAction (assembly) == AssemblyAction.Link && !Profile.IsSdkAssembly (assembly); + } + + public override void ProcessType (TypeDefinition type) + { + if (IsWebServiceClient (type)) + PreserveClient (type); + } + + void PreserveClient (TypeDefinition type) + { + if (!type.HasMethods) + return; + + foreach (MethodDefinition method in type.Methods) { + string sync_method; + if (!TryExtractSyncMethod (method, out sync_method)) + continue; + + AddPreservedMethod (method, sync_method); + } + } + + void AddPreservedMethod (MethodDefinition target, string methodName) + { + foreach (MethodDefinition method in target.DeclaringType.Methods) + if (method.Name == methodName) + Annotations.AddPreservedMethod (target, method); + } + + static bool TryExtractSyncMethod (MethodDefinition method, out string sync_method) + { + if (TryExtractPrefixedMethodName ("Begin", method.Name, out sync_method)) + return true; + + if (TryExtractPrefixedMethodName ("End", method.Name, out sync_method)) + return true; + + if (TryExtractSuffixedMethodName ("Async", method.Name, out sync_method)) + return true; + + return false; + } + + static bool TryExtractPrefixedMethodName (string prefix, string fullName, out string methodName) + { + methodName = null; + + int pos = fullName.IndexOf (prefix, StringComparison.Ordinal); + if (pos == -1) + return false; + + methodName = fullName.Substring (prefix.Length); + return true; + } + + static bool TryExtractSuffixedMethodName (string suffix, string fullName, out string methodName) + { + methodName = null; + + int pos = fullName.LastIndexOf (suffix, StringComparison.Ordinal); + if (pos == -1) + return false; + + methodName = fullName.Substring (0, pos); + return true; + } + + static bool IsWebServiceClient (TypeDefinition type) + { + return type.Inherits ("System.Web.Services.Protocols", "SoapHttpClientProtocol"); + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/Profile.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/Profile.cs new file mode 100644 index 000000000000..9cfbf9f0942c --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/Profile.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; + +using Mono.Cecil; + +namespace Mono.Tuner { + + public abstract class Profile { + + static Profile current; + + public static Profile Current { + get { + if (current is not null) + return current; + + current = CreateProfile ("MonoTouch"); + if (current is not null) + return current; + + current = CreateProfile ("MonoDroid"); + if (current is not null) + return current; + + current = CreateProfile ("MonoMac"); + if (current is not null) + return current; + + throw new NotSupportedException ("No active profile"); + } + set { + current = value; + } + } + + static Profile CreateProfile (string name) + { + var type = Type.GetType (string.Format ("{0}.Tuner.{0}Profile", name)); + if (type is null) + return null; + + return (Profile) Activator.CreateInstance (type); + } + + public static bool IsSdkAssembly (AssemblyDefinition assembly) + { + return Current.IsSdk (assembly); + } + + public static bool IsSdkAssembly (string assemblyName) + { + return Current.IsSdk (assemblyName); + } + + public static bool IsProductAssembly (AssemblyDefinition assembly) + { + return Current.IsProduct (assembly); + } + + public static bool IsProductAssembly (string assemblyName) + { + return Current.IsProduct (assemblyName); + } + + protected virtual bool IsSdk (AssemblyDefinition assembly) + { + return IsSdk (assembly.Name.Name); + } + + protected virtual bool IsProduct (AssemblyDefinition assembly) + { + return IsProduct (assembly.Name.Name); + } + + protected abstract bool IsSdk (string assemblyName); + protected abstract bool IsProduct (string assemblyName); + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/RemoveAttributesBase.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/RemoveAttributesBase.cs new file mode 100644 index 000000000000..61101809fd47 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/RemoveAttributesBase.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +using Mono.Linker; +using Mono.Linker.Steps; + +using Mono.Tuner; + +using Mono.Cecil; + +namespace Mono.Tuner { + + public abstract class RemoveAttributesBase : BaseSubStep { + + public override SubStepTargets Targets { + get { + return SubStepTargets.Assembly + | SubStepTargets.Type + | SubStepTargets.Field + | SubStepTargets.Method + | SubStepTargets.Property + | SubStepTargets.Event; + } + } + + public override bool IsActiveFor (AssemblyDefinition assembly) + { + return Annotations.GetAction (assembly) == AssemblyAction.Link; + } + + public override void ProcessAssembly (AssemblyDefinition assembly) + { + ProcessAttributeProvider (assembly); + ProcessAttributeProvider (assembly.MainModule); + } + + public override void ProcessType (TypeDefinition type) + { + ProcessAttributeProvider (type); + + if (type.HasGenericParameters) + ProcessAttributeProviderCollection (type.GenericParameters); + } + + void ProcessAttributeProviderCollection (IList list) + { + for (int i = 0; i < list.Count; i++) + ProcessAttributeProvider ((ICustomAttributeProvider) list [i]); + } + + public override void ProcessField (FieldDefinition field) + { + ProcessAttributeProvider (field); + } + + public override void ProcessMethod (MethodDefinition method) + { + ProcessMethodAttributeProvider (method); + } + + void ProcessMethodAttributeProvider (MethodDefinition method) + { + ProcessAttributeProvider (method); + ProcessAttributeProvider (method.MethodReturnType); + + if (method.HasParameters) + ProcessAttributeProviderCollection (method.Parameters); + + if (method.HasGenericParameters) + ProcessAttributeProviderCollection (method.GenericParameters); + } + + public override void ProcessProperty (PropertyDefinition property) + { + ProcessAttributeProvider (property); + } + + public override void ProcessEvent (EventDefinition @event) + { + ProcessAttributeProvider (@event); + } + + void ProcessAttributeProvider (ICustomAttributeProvider provider) + { + if (!provider.HasCustomAttributes) + return; + + for (int i = 0; i < provider.CustomAttributes.Count; i++) { + var attrib = provider.CustomAttributes [i]; + if (!IsRemovedAttribute (attrib)) + continue; + + WillRemoveAttribute (provider, attrib); + provider.CustomAttributes.RemoveAt (i--); + } + } + + protected abstract bool IsRemovedAttribute (CustomAttribute attribute); + protected virtual void WillRemoveAttribute (ICustomAttributeProvider provider, CustomAttribute attribute) { } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/RemoveResources.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/RemoveResources.cs new file mode 100644 index 000000000000..803b1e76bf1a --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/RemoveResources.cs @@ -0,0 +1,63 @@ +using System; + +using Mono.Linker; +using Mono.Linker.Steps; + +using Mono.Cecil; + +namespace Mono.Tuner { + + public class RemoveResources : IStep { + + readonly I18nAssemblies assemblies; + + public RemoveResources (I18nAssemblies assemblies) + { + this.assemblies = assemblies; + } + + public virtual void Process (LinkContext context) + { + AssemblyDefinition assembly; + if (!context.TryGetLinkedAssembly ("mscorlib", out assembly)) + return; + + // skip this if we're not linking mscorlib, e.g. --linkskip=mscorlib + if (context.Annotations.GetAction (assembly) != AssemblyAction.Link) + return; + + var resources = assembly.MainModule.Resources; + + for (int i = 0; i < resources.Count; i++) { + var resource = resources [i] as EmbeddedResource; + if (resource is null) + continue; + + if (RemoveResource (resource.Name)) + resources.RemoveAt (i--); + } + } + + bool RemoveResource (string name) + { + switch (name) { + case "mscorlib.xml": + return true; + case "collation.core.bin": + case "collation.tailoring.bin": + return false; + default: + if (!name.Contains ("cjk")) + return false; + if (IncludeCJK ()) + return false; + return true; + } + } + + bool IncludeCJK () + { + return (assemblies & I18nAssemblies.CJK) != 0; + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/RemoveSecurity.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/RemoveSecurity.cs new file mode 100644 index 000000000000..2704564a65e1 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/RemoveSecurity.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using Mono.Linker; +using Mono.Linker.Steps; + +using Mono.Cecil; + +namespace Mono.Tuner { + + public class RemoveSecurity : BaseSubStep { + + public override SubStepTargets Targets { + get { + return SubStepTargets.Assembly + | SubStepTargets.Type + | SubStepTargets.Method; + } + } + + public override bool IsActiveFor (AssemblyDefinition assembly) + { + return Annotations.GetAction (assembly) == AssemblyAction.Link; + } + + public override void ProcessAssembly (AssemblyDefinition assembly) + { + ProcessSecurityProvider (assembly); + } + + public override void ProcessType (TypeDefinition type) + { + ProcessSecurityProvider (type); + } + + public override void ProcessMethod (MethodDefinition method) + { + ProcessSecurityProvider (method); + } + + static void ProcessSecurityProvider (ISecurityDeclarationProvider provider) + { + if (!provider.HasSecurityDeclarations) + return; + + provider.SecurityDeclarations.Clear (); + } + } +} diff --git a/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/TunerAnnotations.cs b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/TunerAnnotations.cs new file mode 100644 index 000000000000..2107ac4a0f59 --- /dev/null +++ b/builds/mono-ios-sdk-destdir/ios-sources/external/linker/src/tuner/Mono.Tuner/TunerAnnotations.cs @@ -0,0 +1,55 @@ +// +// TunerAnnotations.cs +// +// Author: +// Jb Evain (jbevain@novell.com) +// +// (C) 2007 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. +// + +using Mono.Cecil; +using Mono.Linker; + +namespace Mono.Tuner { + + public class TunerAnnotations { + + static readonly object _internalizedKey = new object (); + + public static void Internalized (LinkContext context, IMetadataTokenProvider provider) + { + var annotations = context.Annotations.GetCustomAnnotations (_internalizedKey); + annotations [provider] = _internalizedKey; + } + + public static bool IsInternalized (LinkContext context, IMetadataTokenProvider provider) + { + var annotations = context.Annotations.GetCustomAnnotations (_internalizedKey); + + return annotations.ContainsKey (provider); + } + + private TunerAnnotations () + { + } + } +} diff --git a/builds/mono-wrapper.in b/builds/mono-wrapper.in deleted file mode 100644 index d57106b82ccf..000000000000 --- a/builds/mono-wrapper.in +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -export PATH="@FRAMEWORK_ROOT@/bin:$PATH" -export MONO_CFG_DIR="@FRAMEWORK_ROOT@/etc" -export MONO_PATH="@FRAMEWORK_ROOT@/lib/@ARCH@:@FRAMEWORK_ROOT@/lib/mono/Xamarin.Mac" - -"@MONO@" "$@" diff --git a/tests/Makefile b/tests/Makefile index 2f7cb8bf75a7..03e623902bee 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -62,7 +62,6 @@ test.config: Makefile $(TOP)/Make.config $(TOP)/mk/mono.mk $(TOP)/eng/Version.De @echo "JENKINS_RESULTS_DIRECTORY=$(abspath $(JENKINS_RESULTS_DIRECTORY))" >> $@ @echo "INCLUDE_DEVICE=$(INCLUDE_DEVICE)" >> $@ @echo "XCODE_DEVELOPER_ROOT=$(XCODE_DEVELOPER_ROOT)" >> $@ - @echo "MONO_IOS_SDK_DESTDIR=$(MONO_IOS_SDK_DESTDIR)" >> $@ @echo "DOTNET=$(DOTNET)" >> $@ @echo "IOS_SDK_VERSION=$(IOS_SDK_VERSION)" >> $@ @echo "TVOS_SDK_VERSION=$(TVOS_SDK_VERSION)" >> $@ @@ -96,7 +95,6 @@ test-system.config: Makefile $(TOP)/Make.config $(TOP)/mk/mono.mk $(TOP)/eng/Ver @echo "MAC_DESTDIR=/" >> $@ @echo "JENKINS_RESULTS_DIRECTORY=$(abspath $(JENKINS_RESULTS_DIRECTORY))" >> $@ @echo "INCLUDE_DEVICE=$(INCLUDE_DEVICE)" >> $@ - @echo "MONO_IOS_SDK_DESTDIR=$(MONO_IOS_SDK_DESTDIR)" >> $@ @echo "DOTNET=$(DOTNET)" >> $@ @echo "IOS_SDK_VERSION=$(IOS_SDK_VERSION)" >> $@ @echo "TVOS_SDK_VERSION=$(TVOS_SDK_VERSION)" >> $@ @@ -339,7 +337,7 @@ runner: $(XHARNESS_EXECUTABLE) # This makefile target will run the device tests using the Xamarin.iOS version # installed on the system. vsts-device-tests: $(XHARNESS_EXECUTABLE) - $(MAKE) -C $(TOP)/builds .stamp-mono-ios-sdk-destdir download -j + $(MAKE) -C $(TOP)/builds download -j $(Q) ulimit -n 4096 && $(DOTNET) $< $(XHARNESS_VERBOSITY) --jenkins --autoconf --rootdir $(CURDIR) --sdkroot $(XCODE_DEVELOPER_ROOT) --use-system:true --label=skip-all-tests,run-device-tests --markdown-summary=$(CURDIR)/TestSummary.md $(TESTS_EXTRA_ARGUMENTS) $(TESTS_PERIODIC_COMMAND) verify-system-vsmac-xcode-match: diff --git a/tests/xharness/Harness.cs b/tests/xharness/Harness.cs index 7fb3f9118dc8..ffcba7d2e4c3 100644 --- a/tests/xharness/Harness.cs +++ b/tests/xharness/Harness.cs @@ -229,7 +229,6 @@ string GetVariable (string variable, string @default = null) public string JENKINS_RESULTS_DIRECTORY { get; } // Use same name as in Makefiles, so that a grep finds it. public string MAC_DESTDIR { get; } public string IOS_DESTDIR { get; } - public string MONO_IOS_SDK_DESTDIR { get; } public bool ENABLE_DOTNET { get; } public bool INCLUDE_XAMARIN_LEGACY { get; } public string SYSTEM_MONO { get; set; } @@ -305,7 +304,6 @@ public Harness (IResultParser resultParser, HarnessAction action, HarnessConfigu INCLUDE_MACCATALYST = IsVariableSet (nameof (INCLUDE_MACCATALYST)); MAC_DESTDIR = GetVariable (nameof (MAC_DESTDIR)); IOS_DESTDIR = GetVariable (nameof (IOS_DESTDIR)); - MONO_IOS_SDK_DESTDIR = GetVariable (nameof (MONO_IOS_SDK_DESTDIR)); ENABLE_DOTNET = IsVariableSet (nameof (ENABLE_DOTNET)); SYSTEM_MONO = GetVariable (nameof (SYSTEM_MONO)); DOTNET_DIR = GetVariable (nameof (DOTNET_DIR)); diff --git a/tests/xharness/IHarness.cs b/tests/xharness/IHarness.cs index 4c2ba87a8faf..b20f6a2f006e 100644 --- a/tests/xharness/IHarness.cs +++ b/tests/xharness/IHarness.cs @@ -40,7 +40,6 @@ public interface IHarness { string JENKINS_RESULTS_DIRECTORY { get; } string MAC_DESTDIR { get; } string IOS_DESTDIR { get; } - string MONO_IOS_SDK_DESTDIR { get; } bool ENABLE_DOTNET { get; } bool INCLUDE_XAMARIN_LEGACY { get; } string SYSTEM_MONO { get; set; } diff --git a/tools/devops/automation/scripts/bash/export-mono-filenames.sh b/tools/devops/automation/scripts/bash/export-mono-filenames.sh deleted file mode 100755 index ac0fe56c31f1..000000000000 --- a/tools/devops/automation/scripts/bash/export-mono-filenames.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash -eu - -env | sort - -set -o pipefail -IFS=$'\n\t ' - -FILE=$(pwd)/tmp.txt - -make -C "$BUILD_SOURCESDIRECTORY/xamarin-macios/tools/devops" print-variable-value-to-file FILE="$FILE" VARIABLE=MONO_IOS_FILENAME -MONO_IOS_FILENAME=$(cat "$FILE") -MONO_IOS_FILENAME=$(basename "$MONO_IOS_FILENAME" ".7z") - -make -C "$BUILD_SOURCESDIRECTORY/xamarin-macios/tools/devops" print-variable-value-to-file FILE="$FILE" VARIABLE=MONO_MAC_FILENAME -MONO_MAC_FILENAME=$(cat "$FILE") -MONO_MAC_FILENAME=$(basename "$MONO_MAC_FILENAME" ".7z") - -make -C "$BUILD_SOURCESDIRECTORY/xamarin-macios/tools/devops" print-variable-value-to-file FILE="$FILE" VARIABLE=MONO_MACCATALYST_FILENAME -MONO_MACCATALYST_FILENAME=$(cat "$FILE") -MONO_MACCATALYST_FILENAME=$(basename "$MONO_MACCATALYST_FILENAME" ".7z") - -# allow the rest of the build use the values -echo "##vso[task.setvariable variable=MONO_IOS_FILENAME;]$MONO_IOS_FILENAME" -echo "##vso[task.setvariable variable=MONO_MAC_FILENAME;]$MONO_MAC_FILENAME" -echo "##vso[task.setvariable variable=MONO_MACCATALYST_FILENAME;]$MONO_MACCATALYST_FILENAME" - -rm -f "$FILE" diff --git a/tools/devops/automation/templates/build/build.yml b/tools/devops/automation/templates/build/build.yml index a2648822460e..5bac994abb82 100644 --- a/tools/devops/automation/templates/build/build.yml +++ b/tools/devops/automation/templates/build/build.yml @@ -157,20 +157,6 @@ steps: rm -rf ~/Library/Caches/com.apple.dt.Xcode displayName: 'Clear Xcode cache' - # downloding mono takes time and has been shown to be problematic when we have network issues. This is why we - # are using the cache. Mono does not get bump a lot of times if any (after dotnet) - - - bash: $(System.DefaultWorkingDirectory)/xamarin-macios/tools/devops/automation/scripts/bash/export-mono-filenames.sh - displayName: "Export mono download filenames" - workingDirectory: "$(Build.SourcesDirectory)/xamarin-macios" - timeoutInMinutes: 5 - - - task: Cache@2 - displayName: 'Cache iOS Mono download' - inputs: - key: "$(MONO_IOS_FILENAME)" - path: "$(Build.SourcesDirectory)/xamarin-macios/builds/downloads/$(MONO_IOS_FILENAME)" - # Actual build of the project - bash: $(System.DefaultWorkingDirectory)/xamarin-macios/tools/devops/automation/scripts/bash/build-macios.sh name: build diff --git a/tools/devops/automation/templates/tests/run-tests.yml b/tools/devops/automation/templates/tests/run-tests.yml index f625ab48d289..057339f9a38a 100644 --- a/tools/devops/automation/templates/tests/run-tests.yml +++ b/tools/devops/automation/templates/tests/run-tests.yml @@ -147,8 +147,6 @@ steps: # show environment env -0 | sort -z | tr '\0' '\n' || true - make -C builds download -j - make -C builds .stamp-mono-ios-sdk-destdir -j make -C tests ${{ parameters.makeTarget }} # We reached the end! This means we succeeded!