ILRepack/PlatformFixer.cs (195 lines of code) (raw):

// // Copyright (c) 2011 Simon Goldschmidt // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // using ILRepacking.Mixins; using Mono.Cecil; using System; using System.Collections; using System.IO; namespace ILRepacking { internal class PlatformFixer { readonly IRepackContext repack; private TargetRuntime sourceRuntime; private TargetRuntime targetRuntime; private string targetPlatformDirectory; /// <summary>Loaded assemblies are stored here to prevent them loading more than once.</summary> private Hashtable platformAssemblies = new Hashtable(); public PlatformFixer(IRepackContext repack, TargetRuntime runtime) { this.repack = repack; sourceRuntime = runtime; } public void ParseTargetPlatformDirectory(TargetRuntime runtime, string platformDirectory) { targetRuntime = runtime; targetPlatformDirectory = platformDirectory; if (!string.IsNullOrEmpty(targetPlatformDirectory)) { if (!Directory.Exists(targetPlatformDirectory)) throw new ArgumentException("Platform directory not found: \"" + targetPlatformDirectory + "\"."); } } private AssemblyDefinition TryGetPlatformAssembly(AssemblyNameReference sourceAssemblyName) { try { string platformFile = Path.Combine(targetPlatformDirectory, sourceAssemblyName.Name + ".dll"); AssemblyDefinition platformAsm = null; platformAsm = (AssemblyDefinition)platformAssemblies[platformFile]; if (platformAsm == null) { if (File.Exists(platformFile)) { // file exists, must be a platform file so exchange it // TODO: is this OK? platformAsm = AssemblyDefinition.ReadAssembly(platformFile); platformAssemblies[platformFile] = platformAsm; } } return platformAsm; } catch { return null; } } public IMetadataScope FixPlatformVersion(AssemblyNameReference assyName) { return GetFixedPlatformVersion(assyName) ?? repack.MergeScope(assyName); } IMetadataScope GetFixedPlatformVersion(AssemblyNameReference assyName) { if (targetPlatformDirectory == null) return null; AssemblyDefinition fixedDef = TryGetPlatformAssembly(assyName); if (fixedDef == null) return null; var ret = repack.MergeScope(fixedDef.Name); return ret; } public void FixPlatformVersion(GenericParameterConstraint constraint) { if (constraint == null) return; FixPlatformVersion(constraint.ConstraintType); if (constraint.HasCustomAttributes) foreach (CustomAttribute ca in constraint.CustomAttributes) FixPlatformVersion(ca); } public void FixPlatformVersion(TypeReference reference) { if (reference == null || targetPlatformDirectory == null) return; AssemblyNameReference scopeAsm = reference.Scope as AssemblyNameReference; if (scopeAsm == null) return; var platformAsm = GetFixedPlatformVersion(scopeAsm); if (platformAsm == null) return; if (reference is TypeSpecification) { FixPlatformVersion(((TypeSpecification)reference).ElementType); if (reference is OptionalModifierType) FixPlatformVersion(((OptionalModifierType)reference).ModifierType); else if (reference is RequiredModifierType) FixPlatformVersion(((RequiredModifierType)reference).ModifierType); else if (reference is GenericInstanceType) { var instance = (GenericInstanceType)reference; FixPlatformVersion(instance.ElementType); if (instance.HasGenericArguments) foreach (var ga in instance.GenericArguments) FixPlatformVersion(ga); } else if (reference is FunctionPointerType) { var instance = (FunctionPointerType)reference; FixPlatformVersion(instance.ReturnType); if (instance.HasParameters) foreach (var p in instance.Parameters) FixPlatformVersion(p); } } else if (!(reference is GenericParameter)) { reference.Scope = platformAsm; } if (reference.HasGenericParameters) foreach (var gp in reference.GenericParameters) FixPlatformVersion(gp); FixPlatformVersion(reference.DeclaringType); } void FixPlatformVersionOnMethodSpecification(MethodReference method) { if (!method.IsGenericInstance) throw new NotSupportedException(); var instance = (GenericInstanceMethod)method; FixPlatformVersion(instance.ElementMethod); if (instance.HasGenericArguments) foreach (var ga in instance.GenericArguments) FixPlatformVersion(ga); } public void FixPlatformVersion(MethodReference reference) { if (reference == null || targetPlatformDirectory == null) return; if (reference.IsGenericInstance) { FixPlatformVersionOnMethodSpecification(reference); return; } FixPlatformVersion(reference.ReturnType); FixPlatformVersion(reference.DeclaringType); if (reference.HasParameters) foreach (ParameterDefinition pd in reference.Parameters) FixPlatformVersion(pd); if (reference.HasGenericParameters) foreach (GenericParameter gp in reference.GenericParameters) FixPlatformVersion(gp); } public void FixPlatformVersion(FieldReference reference) { if (reference == null || targetPlatformDirectory == null) return; FixPlatformVersion(reference.FieldType); FixPlatformVersion(reference.DeclaringType); } private void FixPlatformVersion(ParameterDefinition pd) { FixPlatformVersion(pd.ParameterType); if (pd.HasCustomAttributes) foreach (CustomAttribute ca in pd.CustomAttributes) FixPlatformVersion(ca); } private void FixPlatformVersion(GenericParameter gp) { if (gp.HasConstraints) foreach (GenericParameterConstraint tr in gp.Constraints) FixPlatformVersion(tr); if (gp.HasCustomAttributes) foreach (CustomAttribute ca in gp.CustomAttributes) FixPlatformVersion(ca); if (gp.HasGenericParameters) foreach (GenericParameter gp1 in gp.GenericParameters) FixPlatformVersion(gp1); } private void FixPlatformVersion(CustomAttribute ca) { FixPlatformVersion(ca.Constructor); if (ca.HasConstructorArguments) foreach (CustomAttributeArgument caa in ca.ConstructorArguments) FixPlatformVersion(caa); if (ca.HasFields) foreach (CustomAttributeNamedArgument cana in ca.Fields) FixPlatformVersion(cana); if (ca.HasProperties) foreach (CustomAttributeNamedArgument cana in ca.Properties) FixPlatformVersion(cana); } private void FixPlatformVersion(CustomAttributeArgument caa) { FixPlatformVersion(caa.Type); } private void FixPlatformVersion(CustomAttributeNamedArgument cana) { FixPlatformVersion(cana.Argument); } } }