The Labs \ Source Viewer \ SSCLI \ System.Xml.Serialization \ TempAssemblyCache

  1. //------------------------------------------------------------------------------
  2. // <copyright file="Compilation.cs" company="Microsoft">
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. // </copyright>
  14. // <owner current="true" primary="true">ElenaK</owner>
  15. //------------------------------------------------------------------------------
  16. namespace System.Xml.Serialization
  17. {
  18.    
  19.     using System.Reflection;
  20.     using System.Reflection.Emit;
  21.     using System.Collections;
  22.     using System.IO;
  23.     using System;
  24.     using System.Text;
  25.     using System.Xml;
  26.     using System.Threading;
  27.     using System.Security;
  28.     using System.Security.Permissions;
  29.     using System.Security.Policy;
  30.     using System.Xml.Serialization.Configuration;
  31.     using System.Diagnostics;
  32.     using System.CodeDom.Compiler;
  33.     using System.Globalization;
  34.    
  35.     internal class TempAssembly
  36.     {
  37.         const string GeneratedAssemblyNamespace = "Microsoft.Xml.Serialization.GeneratedAssembly";
  38.         Assembly assembly;
  39.         bool pregeneratedAssmbly = false;
  40.         XmlSerializerImplementation contract = null;
  41.         Hashtable writerMethods;
  42.         Hashtable readerMethods;
  43.         TempMethodDictionary methods;
  44.         static object[] emptyObjectArray = new object[0];
  45.         Hashtable assemblies = new Hashtable();
  46.         static FileIOPermission fileIOPermission;
  47.        
  48.         internal class TempMethod
  49.         {
  50.             internal MethodInfo writeMethod;
  51.             internal MethodInfo readMethod;
  52.             internal string name;
  53.             internal string ns;
  54.             internal bool isSoap;
  55.             internal string methodKey;
  56.         }
  57.        
  58.         private TempAssembly()
  59.         {
  60.         }
  61.        
  62.         internal TempAssembly(XmlMapping[] xmlMappings, Type[] types, string defaultNamespace, string location, Evidence evidence)
  63.         {
  64.             CompilerParameters parameters = new CompilerParameters();
  65.             parameters.GenerateInMemory = true;
  66.             TempFileCollection tempFiles = new TempFileCollection(location);
  67.             parameters.TempFiles = tempFiles;
  68.             assembly = GenerateAssembly(xmlMappings, types, defaultNamespace, evidence, parameters, null, assemblies);
  69.             #if DEBUG
  70.             // use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe
  71.             if (assembly == null)
  72.                 throw new InvalidOperationException(Res.GetString(Res.XmlInternalErrorDetails, "Failed to generate XmlSerializer assembly, but did not throw"));
  73.             #endif
  74.             InitAssemblyMethods(xmlMappings);
  75.         }
  76.        
  77.         internal TempAssembly(XmlMapping[] xmlMappings, Assembly assembly, XmlSerializerImplementation contract)
  78.         {
  79.             this.assembly = assembly;
  80.             InitAssemblyMethods(xmlMappings);
  81.             this.contract = contract;
  82.             pregeneratedAssmbly = true;
  83.         }
  84.        
  85.         internal TempAssembly(XmlSerializerImplementation contract)
  86.         {
  87.             this.contract = contract;
  88.             pregeneratedAssmbly = true;
  89.         }
  90.        
  91.         internal XmlSerializerImplementation Contract {
  92.             get {
  93.                 if (contract == null) {
  94.                     contract = (XmlSerializerImplementation)Activator.CreateInstance(GetTypeFromAssembly(this.assembly, "XmlSerializerContract"));
  95.                 }
  96.                 return contract;
  97.             }
  98.         }
  99.        
  100.         internal void InitAssemblyMethods(XmlMapping[] xmlMappings)
  101.         {
  102.             methods = new TempMethodDictionary();
  103.             for (int i = 0; i < xmlMappings.Length; i++) {
  104.                 TempMethod method = new TempMethod();
  105.                 method.isSoap = xmlMappings[i].IsSoap;
  106.                 method.methodKey = xmlMappings[i].Key;
  107.                 XmlTypeMapping xmlTypeMapping = xmlMappings[i] as XmlTypeMapping;
  108.                 if (xmlTypeMapping != null) {
  109.                     method.name = xmlTypeMapping.ElementName;
  110.                     method.ns = xmlTypeMapping.Namespace;
  111.                 }
  112.                 methods.Add(xmlMappings[i].Key, method);
  113.             }
  114.         }
  115.        
  116.         /// <devdoc>
  117.         /// <para>
  118.         /// Attempts to load pre-generated serialization assembly.
  119.         /// First check for the [XmlSerializerAssembly] attribute
  120.         /// </para>
  121.         /// </devdoc>
  122.         static internal Assembly LoadGeneratedAssembly(Type type, string defaultNamespace, out XmlSerializerImplementation contract)
  123.         {
  124.             Assembly serializer = null;
  125.             contract = null;
  126.             string serializerName = null;
  127.             bool logEnabled = DiagnosticsSwitches.PregenEventLog.Enabled;
  128.            
  129.             // check to see if we loading explicit pre-generated assembly
  130.             object[] attrs = type.GetCustomAttributes(typeof(XmlSerializerAssemblyAttribute), false);
  131.             if (attrs.Length == 0) {
  132.                 // Guess serializer name: if parent assembly signed use strong name
  133.                 AssemblyName name = GetName(type.Assembly, true);
  134.                 serializerName = Compiler.GetTempAssemblyName(name, defaultNamespace);
  135.                 // use strong name
  136.                 name.Name = serializerName;
  137.                 name.CodeBase = null;
  138.                 name.CultureInfo = CultureInfo.InvariantCulture;
  139.                 try {
  140.                     serializer = Assembly.Load(name);
  141.                 }
  142.                 catch (Exception e) {
  143.                     if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {
  144.                         throw;
  145.                     }
  146.                     byte[] token = name.GetPublicKeyToken();
  147.                     if (token != null && token.Length > 0) {
  148.                         // the parent assembly was signed, so do not try to LoadWithPartialName
  149.                         return null;
  150.                     }
  151.                     #pragma warning disable 618
  152.                     serializer = Assembly.LoadWithPartialName(serializerName, null);
  153.                     #pragma warning restore 618
  154.                 }
  155.                 catch {
  156.                     return null;
  157.                 }
  158.                 if (serializer == null) {
  159.                     return null;
  160.                 }
  161.                 if (!IsSerializerVersionMatch(serializer, type, defaultNamespace, null)) {
  162.                     return null;
  163.                 }
  164.             }
  165.             else {
  166.                 XmlSerializerAssemblyAttribute assemblyAttribute = (XmlSerializerAssemblyAttribute)attrs[0];
  167.                 if (assemblyAttribute.AssemblyName != null && assemblyAttribute.CodeBase != null)
  168.                     throw new InvalidOperationException(Res.GetString(Res.XmlPregenInvalidXmlSerializerAssemblyAttribute, "AssemblyName", "CodeBase"));
  169.                
  170.                 // found XmlSerializerAssemblyAttribute attribute, it should have all needed information to load the pre-generated serializer
  171.                 if (assemblyAttribute.AssemblyName != null) {
  172.                     serializerName = assemblyAttribute.AssemblyName;
  173.                     #pragma warning disable 618
  174.                     serializer = Assembly.LoadWithPartialName(serializerName, null);
  175.                     #pragma warning restore 618
  176.                 }
  177.                 else if (assemblyAttribute.CodeBase != null && assemblyAttribute.CodeBase.Length > 0) {
  178.                     serializerName = assemblyAttribute.CodeBase;
  179.                     serializer = Assembly.LoadFrom(serializerName);
  180.                 }
  181.                 else {
  182.                     serializerName = type.Assembly.FullName;
  183.                     serializer = type.Assembly;
  184.                 }
  185.                 if (serializer == null) {
  186.                     throw new FileNotFoundException(null, serializerName);
  187.                 }
  188.             }
  189.             Type contractType = GetTypeFromAssembly(serializer, "XmlSerializerContract");
  190.             contract = (XmlSerializerImplementation)Activator.CreateInstance(contractType);
  191.             if (contract.CanSerialize(type))
  192.                 return serializer;
  193.            
  194.             return null;
  195.         }
  196.        
  197.        
  198.         static AssemblyName GetName(Assembly assembly, bool copyName)
  199.         {
  200.             PermissionSet perms = new PermissionSet(PermissionState.None);
  201.             perms.AddPermission(new FileIOPermission(PermissionState.Unrestricted));
  202.             perms.Assert();
  203.             return assembly.GetName(copyName);
  204.         }
  205.        
  206.        
  207.         static bool IsSerializerVersionMatch(Assembly serializer, Type type, string defaultNamespace, string location)
  208.         {
  209.             if (serializer == null)
  210.                 return false;
  211.             object[] attrs = serializer.GetCustomAttributes(typeof(XmlSerializerVersionAttribute), false);
  212.             if (attrs.Length != 1)
  213.                 return false;
  214.            
  215.             XmlSerializerVersionAttribute assemblyInfo = (XmlSerializerVersionAttribute)attrs[0];
  216.             if (assemblyInfo.ParentAssemblyId == GenerateAssemblyId(type) && assemblyInfo.Namespace == defaultNamespace)
  217.                 return true;
  218.             return false;
  219.         }
  220.         static string GenerateAssemblyId(Type type)
  221.         {
  222.             Module[] modules = type.Assembly.GetModules();
  223.             ArrayList list = new ArrayList();
  224.             for (int i = 0; i < modules.Length; i++) {
  225.                 list.Add(modules[i].ModuleVersionId.ToString());
  226.             }
  227.             list.Sort();
  228.             StringBuilder sb = new StringBuilder();
  229.             for (int i = 0; i < list.Count; i++) {
  230.                 sb.Append(list[i].ToString());
  231.                 sb.Append(",");
  232.             }
  233.             return sb.ToString();
  234.         }
  235.        
  236.         static internal Assembly GenerateAssembly(XmlMapping[] xmlMappings, Type[] types, string defaultNamespace, Evidence evidence, CompilerParameters parameters, Assembly assembly, Hashtable assemblies)
  237.         {
  238.             FileIOPermission.Assert();
  239.             for (int i = 0; i < xmlMappings.Length; i++) {
  240.                 xmlMappings[i].CheckShallow();
  241.             }
  242.             Compiler compiler = new Compiler();
  243.             try {
  244.                 Hashtable scopeTable = new Hashtable();
  245.                 foreach (XmlMapping mapping in xmlMappings)
  246.                     scopeTable[mapping.Scope] = mapping;
  247.                 TypeScope[] scopes = new TypeScope[scopeTable.Keys.Count];
  248.                 scopeTable.Keys.CopyTo(scopes, 0);
  249.                
  250.                 assemblies.Clear();
  251.                 Hashtable importedTypes = new Hashtable();
  252.                 foreach (TypeScope scope in scopes) {
  253.                     foreach (Type t in scope.Types) {
  254.                         compiler.AddImport(t, importedTypes);
  255.                         Assembly a = t.Assembly;
  256.                         string name = a.FullName;
  257.                         if (assemblies[name] != null)
  258.                             continue;
  259.                         if (!a.GlobalAssemblyCache) {
  260.                             assemblies[name] = a;
  261.                         }
  262.                     }
  263.                 }
  264.                 for (int i = 0; i < types.Length; i++) {
  265.                     compiler.AddImport(types[i], importedTypes);
  266.                 }
  267.                 compiler.AddImport(typeof(object).Assembly);
  268.                 compiler.AddImport(typeof(XmlSerializer).Assembly);
  269.                
  270.                 IndentedWriter writer = new IndentedWriter(compiler.Source, false);
  271.                
  272.                 writer.WriteLine("#if _DYNAMIC_XMLSERIALIZER_COMPILATION");
  273.                 writer.WriteLine("[assembly:System.Security.AllowPartiallyTrustedCallers()]");
  274.                 writer.WriteLine("[assembly:System.Security.SecurityTransparent()]");
  275.                 writer.WriteLine("#endif");
  276.                 // Add AssemblyVersion attribute to match parent accembly version
  277.                 if (types != null && types.Length > 0 && types[0] != null) {
  278.                     writer.WriteLine("[assembly:System.Reflection.AssemblyVersionAttribute(\"" + types[0].Assembly.GetName().Version.ToString() + "\")]");
  279.                 }
  280.                 if (assembly != null && types.Length > 0) {
  281.                     for (int i = 0; i < types.Length; i++) {
  282.                         Type type = types[i];
  283.                         if (type == null)
  284.                             continue;
  285.                         if (DynamicAssemblies.IsTypeDynamic(type)) {
  286.                             throw new InvalidOperationException(Res.GetString(Res.XmlPregenTypeDynamic, types[i].FullName));
  287.                         }
  288.                     }
  289.                     writer.Write("[assembly:");
  290.                     writer.Write(typeof(XmlSerializerVersionAttribute).FullName);
  291.                     writer.Write("(");
  292.                     writer.Write("ParentAssemblyId=");
  293.                     ReflectionAwareCodeGen.WriteQuotedCSharpString(writer, GenerateAssemblyId(types[0]));
  294.                     writer.Write(", Version=");
  295.                     ReflectionAwareCodeGen.WriteQuotedCSharpString(writer, ThisAssembly.Version);
  296.                     if (defaultNamespace != null) {
  297.                         writer.Write(", Namespace=");
  298.                         ReflectionAwareCodeGen.WriteQuotedCSharpString(writer, defaultNamespace);
  299.                     }
  300.                     writer.WriteLine(")]");
  301.                 }
  302.                 CodeIdentifiers classes = new CodeIdentifiers();
  303.                 classes.AddUnique("XmlSerializationWriter", "XmlSerializationWriter");
  304.                 classes.AddUnique("XmlSerializationReader", "XmlSerializationReader");
  305.                 string suffix = null;
  306.                 if (types != null && types.Length == 1 && types[0] != null) {
  307.                     suffix = CodeIdentifier.MakeValid(types[0].Name);
  308.                     if (types[0].IsArray) {
  309.                         suffix += "Array";
  310.                     }
  311.                 }
  312.                
  313.                 writer.WriteLine("namespace " + GeneratedAssemblyNamespace + " {");
  314.                 writer.Indent++;
  315.                
  316.                 writer.WriteLine();
  317.                
  318.                 string writerClass = "XmlSerializationWriter" + suffix;
  319.                 writerClass = classes.AddUnique(writerClass, writerClass);
  320.                 XmlSerializationWriterCodeGen writerCodeGen = new XmlSerializationWriterCodeGen(writer, scopes, "public", writerClass);
  321.                
  322.                 writerCodeGen.GenerateBegin();
  323.                 string[] writeMethodNames = new string[xmlMappings.Length];
  324.                
  325.                 for (int i = 0; i < xmlMappings.Length; i++) {
  326.                     writeMethodNames[i] = writerCodeGen.GenerateElement(xmlMappings[i]);
  327.                 }
  328.                 writerCodeGen.GenerateEnd();
  329.                
  330.                 writer.WriteLine();
  331.                
  332.                 string readerClass = "XmlSerializationReader" + suffix;
  333.                 readerClass = classes.AddUnique(readerClass, readerClass);
  334.                 XmlSerializationReaderCodeGen readerCodeGen = new XmlSerializationReaderCodeGen(writer, scopes, "public", readerClass);
  335.                
  336.                 readerCodeGen.GenerateBegin();
  337.                 string[] readMethodNames = new string[xmlMappings.Length];
  338.                 for (int i = 0; i < xmlMappings.Length; i++) {
  339.                     readMethodNames[i] = readerCodeGen.GenerateElement(xmlMappings[i]);
  340.                 }
  341.                 readerCodeGen.GenerateEnd(readMethodNames, xmlMappings, types);
  342.                
  343.                 string baseSerializer = readerCodeGen.GenerateBaseSerializer("XmlSerializer1", readerClass, writerClass, classes);
  344.                 Hashtable serializers = new Hashtable();
  345.                 for (int i = 0; i < xmlMappings.Length; i++) {
  346.                     if (serializers[xmlMappings[i].Key] == null) {
  347.                         serializers[xmlMappings[i].Key] = readerCodeGen.GenerateTypedSerializer(readMethodNames[i], writeMethodNames[i], xmlMappings[i], classes, baseSerializer, readerClass, writerClass);
  348.                     }
  349.                 }
  350.                 readerCodeGen.GenerateSerializerContract("XmlSerializerContract", xmlMappings, types, readerClass, readMethodNames, writerClass, writeMethodNames, serializers);
  351.                 writer.Indent--;
  352.                 writer.WriteLine("}");
  353.                
  354.                 return compiler.Compile(assembly, defaultNamespace, parameters, evidence);
  355.             }
  356.             finally {
  357.                 compiler.Close();
  358.             }
  359.         }
  360.        
  361.         static MethodInfo GetMethodFromType(Type type, string methodName, Assembly assembly)
  362.         {
  363.             MethodInfo method = type.GetMethod(methodName);
  364.             if (method != null)
  365.                 return method;
  366.            
  367.             MissingMethodException missingMethod = new MissingMethodException(type.FullName, methodName);
  368.             if (assembly != null) {
  369.                 throw new InvalidOperationException(Res.GetString(Res.XmlSerializerExpired, assembly.FullName, assembly.CodeBase), missingMethod);
  370.             }
  371.             throw missingMethod;
  372.         }
  373.        
  374.         static internal Type GetTypeFromAssembly(Assembly assembly, string typeName)
  375.         {
  376.             typeName = GeneratedAssemblyNamespace + "." + typeName;
  377.             Type type = assembly.GetType(typeName);
  378.             if (type == null)
  379.                 throw new InvalidOperationException(Res.GetString(Res.XmlMissingType, typeName, assembly.FullName));
  380.             return type;
  381.         }
  382.        
  383.         internal bool CanRead(XmlMapping mapping, XmlReader xmlReader)
  384.         {
  385.             if (mapping == null)
  386.                 return false;
  387.            
  388.             if (mapping.Accessor.Any) {
  389.                 return true;
  390.             }
  391.             TempMethod method = methods[mapping.Key];
  392.             return xmlReader.IsStartElement(method.name, method.ns);
  393.         }
  394.        
  395.         string ValidateEncodingStyle(string encodingStyle, string methodKey)
  396.         {
  397.             if (encodingStyle != null && encodingStyle.Length > 0) {
  398.                 if (methods[methodKey].isSoap) {
  399.                     if (encodingStyle != Soap.Encoding && encodingStyle != Soap12.Encoding) {
  400.                         throw new InvalidOperationException(Res.GetString(Res.XmlInvalidEncoding3, encodingStyle, Soap.Encoding, Soap12.Encoding));
  401.                     }
  402.                 }
  403.                 else {
  404.                     throw new InvalidOperationException(Res.GetString(Res.XmlInvalidEncodingNotEncoded1, encodingStyle));
  405.                 }
  406.             }
  407.             else {
  408.                 if (methods[methodKey].isSoap) {
  409.                     encodingStyle = Soap.Encoding;
  410.                 }
  411.             }
  412.             return encodingStyle;
  413.         }
  414.        
  415.         static internal FileIOPermission FileIOPermission {
  416.             get {
  417.                 if (fileIOPermission == null)
  418.                     fileIOPermission = new FileIOPermission(PermissionState.Unrestricted);
  419.                 return fileIOPermission;
  420.             }
  421.         }
  422.        
  423.         internal object InvokeReader(XmlMapping mapping, XmlReader xmlReader, XmlDeserializationEvents events, string encodingStyle)
  424.         {
  425.             XmlSerializationReader reader = null;
  426.             try {
  427.                 encodingStyle = ValidateEncodingStyle(encodingStyle, mapping.Key);
  428.                 reader = Contract.Reader;
  429.                 reader.Init(xmlReader, events, encodingStyle, this);
  430.                 if (methods[mapping.Key].readMethod == null) {
  431.                     if (readerMethods == null) {
  432.                         readerMethods = Contract.ReadMethods;
  433.                     }
  434.                     string methodName = (string)readerMethods[mapping.Key];
  435.                     if (methodName == null) {
  436.                         throw new InvalidOperationException(Res.GetString(Res.XmlNotSerializable, mapping.Accessor.Name));
  437.                     }
  438.                     methods[mapping.Key].readMethod = GetMethodFromType(reader.GetType(), methodName, pregeneratedAssmbly ? this.assembly : null);
  439.                 }
  440.                 return methods[mapping.Key].readMethod.Invoke(reader, emptyObjectArray);
  441.             }
  442.             catch (SecurityException e) {
  443.                 throw new InvalidOperationException(Res.GetString(Res.XmlNoPartialTrust), e);
  444.             }
  445.             finally {
  446.                 if (reader != null)
  447.                     reader.Dispose();
  448.             }
  449.         }
  450.        
  451.         internal void InvokeWriter(XmlMapping mapping, XmlWriter xmlWriter, object o, XmlSerializerNamespaces namespaces, string encodingStyle, string id)
  452.         {
  453.             XmlSerializationWriter writer = null;
  454.             try {
  455.                 encodingStyle = ValidateEncodingStyle(encodingStyle, mapping.Key);
  456.                 writer = Contract.Writer;
  457.                 writer.Init(xmlWriter, namespaces, encodingStyle, id, this);
  458.                 if (methods[mapping.Key].writeMethod == null) {
  459.                     if (writerMethods == null) {
  460.                         writerMethods = Contract.WriteMethods;
  461.                     }
  462.                     string methodName = (string)writerMethods[mapping.Key];
  463.                     if (methodName == null) {
  464.                         throw new InvalidOperationException(Res.GetString(Res.XmlNotSerializable, mapping.Accessor.Name));
  465.                     }
  466.                     methods[mapping.Key].writeMethod = GetMethodFromType(writer.GetType(), methodName, pregeneratedAssmbly ? assembly : null);
  467.                 }
  468.                 methods[mapping.Key].writeMethod.Invoke(writer, new object[] {o});
  469.             }
  470.             catch (SecurityException e) {
  471.                 throw new InvalidOperationException(Res.GetString(Res.XmlNoPartialTrust), e);
  472.             }
  473.             finally {
  474.                 if (writer != null)
  475.                     writer.Dispose();
  476.             }
  477.         }
  478.        
  479.         internal Assembly GetReferencedAssembly(string name)
  480.         {
  481.             return assemblies != null && name != null ? (Assembly)assemblies[name] : null;
  482.         }
  483.        
  484.         internal bool NeedAssembyResolve {
  485.             get { return assemblies != null && assemblies.Count > 0; }
  486.         }
  487.        
  488.         internal sealed class TempMethodDictionary : DictionaryBase
  489.         {
  490.             internal TempMethod this[string key]
  491.             {
  492.                 get { return (TempMethod)Dictionary[key]; }
  493.             }
  494.             internal void Add(string key, TempMethod value)
  495.             {
  496.                 Dictionary.Add(key, value);
  497.             }
  498.         }
  499.     }
  500.    
  501.     class TempAssemblyCacheKey
  502.     {
  503.         string ns;
  504.         object type;
  505.        
  506.         internal TempAssemblyCacheKey(string ns, object type)
  507.         {
  508.             this.type = type;
  509.             this.ns = ns;
  510.         }
  511.        
  512.         public override bool Equals(object o)
  513.         {
  514.             TempAssemblyCacheKey key = o as TempAssemblyCacheKey;
  515.             if (key == null)
  516.                 return false;
  517.             return (key.type == this.type && key.ns == this.ns);
  518.         }
  519.        
  520.         public override int GetHashCode()
  521.         {
  522.             return ((ns != null ? ns.GetHashCode() : 0) ^ (type != null ? type.GetHashCode() : 0));
  523.         }
  524.     }
  525.    
  526.     internal class TempAssemblyCache
  527.     {
  528.         Hashtable cache = new Hashtable();
  529.        
  530.         internal TempAssembly this[string ns, object o]
  531.         {
  532.             get { return (TempAssembly)cache[new TempAssemblyCacheKey(ns, o)]; }
  533.         }
  534.        
  535.         internal void Add(string ns, object o, TempAssembly assembly)
  536.         {
  537.             TempAssemblyCacheKey key = new TempAssemblyCacheKey(ns, o);
  538.             lock (this) {
  539.                 if (cache[key] == assembly)
  540.                     return;
  541.                 Hashtable clone = new Hashtable();
  542.                 foreach (object k in cache.Keys) {
  543.                     clone.Add(k, cache[k]);
  544.                 }
  545.                 cache = clone;
  546.                 cache[key] = assembly;
  547.             }
  548.         }
  549.     }
  550. }

Developer Fusion