diff --git a/Src/Compilers/Core/Source/CodeAnalysis.csproj b/Src/Compilers/Core/Source/CodeAnalysis.csproj
index fe64abe2e4234b6458cb650ed7880cfb815632ba..dad27c5e5cf48f5d48374c20d63919a57c8bd354 100644
--- a/Src/Compilers/Core/Source/CodeAnalysis.csproj
+++ b/Src/Compilers/Core/Source/CodeAnalysis.csproj
@@ -355,6 +355,7 @@
+
diff --git a/Src/Compilers/Core/Source/CodeGen/LocalConstantDefinition.cs b/Src/Compilers/Core/Source/CodeGen/LocalConstantDefinition.cs
index bcae85e2eb665fc5c0a4e39b89007ea909a0bb5b..2b46c70500b4cd7ea7a125d5709be5f30997acaa 100644
--- a/Src/Compilers/Core/Source/CodeGen/LocalConstantDefinition.cs
+++ b/Src/Compilers/Core/Source/CodeGen/LocalConstantDefinition.cs
@@ -1,12 +1,8 @@
// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
-using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
-using Microsoft.Cci;
-using Microsoft.CodeAnalysis.Text;
-using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CodeGen
{
@@ -14,17 +10,17 @@ namespace Microsoft.CodeAnalysis.CodeGen
/// We need a CCI representation for local constants because they are emitted as locals in
/// PDB scopes to improve the debugging experience (see LocalScopeProvider.GetConstantsInScope).
///
- internal class LocalConstantDefinition : ILocalDefinition
+ internal sealed class LocalConstantDefinition : Cci.ILocalDefinition
{
private readonly string name;
private readonly Location location;
- private readonly IMetadataConstant compileTimeValue;
+ private readonly Cci.IMetadataConstant compileTimeValue;
private readonly bool isDynamic;
//Gives the synthesized dynamic attributes of the local definition
private readonly ImmutableArray dynamicTransformFlags;
- public LocalConstantDefinition(string name, Location location, IMetadataConstant compileTimeValue, bool isDynamic = false,
+ public LocalConstantDefinition(string name, Location location, Cci.IMetadataConstant compileTimeValue, bool isDynamic = false,
ImmutableArray dynamicTransformFlags = default(ImmutableArray))
{
Debug.Assert(!string.IsNullOrEmpty(name));
@@ -47,12 +43,12 @@ public Location Location
get { return location; }
}
- public IMetadataConstant CompileTimeValue
+ public Cci.IMetadataConstant CompileTimeValue
{
get { return compileTimeValue; }
}
- public ITypeReference Type
+ public Cci.ITypeReference Type
{
get { return this.compileTimeValue.Type; }
}
@@ -62,9 +58,9 @@ public bool IsConstant
get { return true; }
}
- public ImmutableArray CustomModifiers
+ public ImmutableArray CustomModifiers
{
- get { return ImmutableArray.Empty; }
+ get { return ImmutableArray.Empty; }
}
public bool IsModified
@@ -96,5 +92,13 @@ public ImmutableArray DynamicTransformFlags
{
get { return this.dynamicTransformFlags; }
}
+
+ public int SlotIndex
+ {
+ get
+ {
+ return -1;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Src/Compilers/Core/Source/CodeGen/LocalDefinition.cs b/Src/Compilers/Core/Source/CodeGen/LocalDefinition.cs
index 428033db062a10e82b21b9828ccc9dd73e02bea4..83c16904a145eb33495af8e11d03b01cca83a113 100644
--- a/Src/Compilers/Core/Source/CodeGen/LocalDefinition.cs
+++ b/Src/Compilers/Core/Source/CodeGen/LocalDefinition.cs
@@ -1,12 +1,11 @@
// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-using System.Collections.Generic;
using System.Collections.Immutable;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CodeGen
{
- internal sealed class LocalDefinition : Microsoft.Cci.ILocalDefinition
+ internal sealed class LocalDefinition : Cci.ILocalDefinition
{
//TODO: locals are really just typed slots. They do not have names.
// name only matters for pdb generation where it is a scope-specific mapping to a slot.
@@ -18,7 +17,7 @@ internal sealed class LocalDefinition : Microsoft.Cci.ILocalDefinition
private readonly string name; // null if it is a temp.
//data type associated with the local signature slot.
- private readonly Microsoft.Cci.ITypeReference type;
+ private readonly Cci.ITypeReference type;
// specifies whether local slot has a byref constraint and whether
// the type of the local has the "pinned modifier" (7.1.2).
@@ -53,7 +52,7 @@ internal sealed class LocalDefinition : Microsoft.Cci.ILocalDefinition
public LocalDefinition(
object identity,
string name,
- Microsoft.Cci.ITypeReference type,
+ Cci.ITypeReference type,
int slot,
bool isCompilerGenerated,
LocalSlotConstraints constraints,
@@ -147,7 +146,7 @@ public ImmutableArray DynamicTransformFlags
get { return this.dynamicTransformFlags; }
}
- public Microsoft.Cci.ITypeReference Type
+ public Cci.ITypeReference Type
{
get { return this.type; }
}
diff --git a/Src/Compilers/Core/Source/Emit/CommonPEModuleBuilder.cs b/Src/Compilers/Core/Source/Emit/CommonPEModuleBuilder.cs
index 12a77e6ba1e069a2d05fe9eaeade0cfe1f4cd275..b4459006359e8981aa89691294eb2d0fe8a2ca6d 100644
--- a/Src/Compilers/Core/Source/Emit/CommonPEModuleBuilder.cs
+++ b/Src/Compilers/Core/Source/Emit/CommonPEModuleBuilder.cs
@@ -82,6 +82,8 @@ internal abstract class PEModuleBuilder lazyExternNamespaces;
+
protected PEModuleBuilder(
TCompilation compilation,
TSourceModuleSymbol sourceModule,
@@ -306,6 +308,38 @@ private static void VisitTopLevelType(Cci.NoPiaReferenceIndexer noPiaIndexer, Cc
}
}
+ private ImmutableArray CalculateExternNamespaces()
+ {
+ var result = ArrayBuilder.GetInstance(compilation.ExternalReferences.Length);
+
+ var referenceManager = this.compilation.GetBoundReferenceManager();
+
+ // Enumerate external references (#r's don't define aliases) to preserve the order.
+ foreach (MetadataReference reference in compilation.ExternalReferences)
+ {
+ // duplicate references might have been skipped by the assembly binder:
+
+ IAssemblySymbol symbol;
+ ImmutableArray aliases;
+ if (referenceManager.TryGetReferencedAssemblySymbol(reference, out symbol, out aliases))
+ {
+ var displayName = symbol.Identity.GetDisplayName();
+ for (int i = 0; i < aliases.Length; i++)
+ {
+ string alias = aliases[i];
+
+ // filter out duplicates and global aliases:
+ if (alias != MetadataReferenceProperties.GlobalAlias && aliases.IndexOf(alias, 0, i) < 0)
+ {
+ result.Add(new Cci.ExternNamespace(alias, displayName));
+ }
+ }
+ }
+ }
+
+ return result.ToImmutableAndFree();
+ }
+
#region Synthesized Members
///
@@ -687,36 +721,17 @@ IEnumerable Cci.IModule.ModuleAttributes
get { return GetSourceModuleAttributes(); }
}
- ImmutableArray Cci.IModule.GetExternNamespaces()
+ ImmutableArray Cci.IModule.ExternNamespaces
{
- var result = ArrayBuilder.GetInstance(compilation.ExternalReferences.Length);
-
- var referenceManager = this.compilation.GetBoundReferenceManager();
-
- // Enumerate external references (#r's don't define aliases) to preserve the order.
- foreach (MetadataReference reference in compilation.ExternalReferences)
+ get
{
- // duplicate references might have been skipped by the assembly binder:
-
- IAssemblySymbol symbol;
- ImmutableArray aliases;
- if (referenceManager.TryGetReferencedAssemblySymbol(reference, out symbol, out aliases))
+ if (lazyExternNamespaces.IsDefault)
{
- var displayName = symbol.Identity.GetDisplayName();
- for (int i = 0; i < aliases.Length; i++)
- {
- string alias = aliases[i];
-
- // filter out duplicates and global aliases:
- if (alias != MetadataReferenceProperties.GlobalAlias && aliases.IndexOf(alias, 0, i) < 0)
- {
- result.Add(new Cci.ExternNamespace(alias, displayName));
- }
- }
+ ImmutableInterlocked.InterlockedCompareExchange(ref lazyExternNamespaces, CalculateExternNamespaces(), default(ImmutableArray));
}
- }
- return result.ToImmutableAndFree();
+ return this.lazyExternNamespaces;
+ }
}
// PE entry point, only available for console and windows apps:
diff --git a/Src/Compilers/Core/Source/PEWriter/CustomDebugInfoWriter.cs b/Src/Compilers/Core/Source/PEWriter/CustomDebugInfoWriter.cs
new file mode 100644
index 0000000000000000000000000000000000000000..17d5e741772772548200528af60d8d9ed5a8227a
--- /dev/null
+++ b/Src/Compilers/Core/Source/PEWriter/CustomDebugInfoWriter.cs
@@ -0,0 +1,390 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Roslyn.Utilities;
+
+namespace Microsoft.Cci
+{
+ internal sealed class CustomDebugInfoWriter
+ {
+ private uint methodTokenWithModuleInfo;
+ private IMethodBody methodBodyWithModuleInfo;
+
+ private uint previousMethodTokenWithUsingInfo;
+ private IMethodBody previousMethodBodyWithUsingInfo;
+
+ public CustomDebugInfoWriter()
+ {
+ }
+
+ ///
+ /// Returns true if the namespace scope for this method should be forwarded to another method.
+ /// Returns non-null if the forwarding should be done directly via UsingNamespace,
+ /// null if the forwarding is done via custom debug info.
+ ///
+ public bool ShouldForwardNamespaceScopes(IMethodBody methodBody, uint methodToken, out IMethodDefinition forwardToMethod)
+ {
+ if (ShouldForwardToPreviousMethodWithUsingInfo(methodBody) || methodBody.NamespaceScopes.IsEmpty)
+ {
+ // SerializeNamespaceScopeMetadata will do the actual forwarding in case this is a CSharp method.
+ // VB on the other hand adds a "@methodtoken" to the scopes instead.
+ if (methodBody.CustomDebugInfoKind == CustomDebugInfoKind.VisualBasicStyle)
+ {
+ forwardToMethod = this.previousMethodBodyWithUsingInfo.MethodDefinition;
+ }
+ else
+ {
+ forwardToMethod = null;
+ }
+
+ return true;
+ }
+
+ this.previousMethodBodyWithUsingInfo = methodBody;
+ this.previousMethodTokenWithUsingInfo = methodToken;
+ forwardToMethod = null;
+ return false;
+ }
+
+ public byte[] SerializeMethodDebugInfo(IModule module, IMethodBody methodBody, uint methodToken, out bool emitExternNamespaces)
+ {
+ emitExternNamespaces = false;
+
+ // CONSIDER: this may not be the same "first" method as in Dev10, but
+ // it shouldn't matter since all methods will still forward to a method
+ // containing the appropriate information.
+ if (this.methodBodyWithModuleInfo == null) //UNDONE: || edit-and-continue
+ {
+ // This module level information could go on every method (and does in
+ // the edit-and-continue case), but - as an optimization - we'll just
+ // put it on the first method we happen to encounter and then put a
+ // reference to the first method's token in every other method (so they
+ // can find the information).
+ if (module.ExternNamespaces.Any())
+ {
+ this.methodTokenWithModuleInfo = methodToken;
+ this.methodBodyWithModuleInfo = methodBody;
+ emitExternNamespaces = true;
+ }
+ }
+
+ var customDebugInfo = ArrayBuilder.GetInstance();
+
+ SerializeIteratorClassMetadata(methodBody, customDebugInfo);
+
+ // NOTE: This is an attempt to match Dev10's apparent behavior. For iterator methods (i.e. the method
+ // that appears in source, not the synthesized ones), Dev10 only emits the ForwardIterator and IteratorLocal
+ // custom debug info (e.g. there will be no information about the usings that were in scope).
+ // NOTE: There seems to be an unusual behavior in ISymUnmanagedWriter where, if all the methods in a type are
+ // iterator methods, no custom debug info is emitted for any method. Adding a single non-iterator
+ // method causes the custom debug info to be produced for all methods (including the iterator methods).
+ // Since we are making the same ISymUnmanagedWriter calls as Dev10, we see the same behavior (i.e. this
+ // is not a regression).
+ if (methodBody.IteratorClassName == null)
+ {
+ SerializeNamespaceScopeMetadata(methodBody, customDebugInfo);
+ SerializeIteratorLocalScopes(methodBody, customDebugInfo);
+ }
+
+ SerializeDynamicLocalInfo(methodBody, customDebugInfo);
+
+ byte[] result;
+ if (methodBody.CustomDebugInfoKind == CustomDebugInfoKind.CSharpStyle)
+ {
+ result = SerializeCustomDebugMetadata(customDebugInfo);
+ }
+ else
+ {
+ result = null;
+ }
+
+ customDebugInfo.Free();
+ return result;
+ }
+
+ private static void SerializeIteratorClassMetadata(IMethodBody methodBody, ArrayBuilder customDebugInfo)
+ {
+ SerializeReferenceToIteratorClass(methodBody.IteratorClassName, customDebugInfo);
+ }
+
+ private static void SerializeReferenceToIteratorClass(string iteratorClassName, ArrayBuilder customDebugInfo)
+ {
+ if (iteratorClassName == null) return;
+ MemoryStream customMetadata = new MemoryStream();
+ BinaryWriter cmw = new BinaryWriter(customMetadata, true);
+ cmw.WriteByte(4); // version
+ cmw.WriteByte(4); // kind: ForwardIterator
+ cmw.Align(4);
+ uint length = 10 + (uint)iteratorClassName.Length * 2;
+ if ((length & 3) != 0) length += 4 - (length & 3);
+ cmw.WriteUint(length);
+ cmw.WriteString(iteratorClassName, true);
+ cmw.Align(4);
+ Debug.Assert(customMetadata.Position == length);
+ customDebugInfo.Add(customMetadata);
+ }
+
+ private static void SerializeIteratorLocalScopes(IMethodBody methodBody, ArrayBuilder customDebugInfo)
+ {
+ ImmutableArray scopes = methodBody.IteratorScopes;
+ uint numberOfScopes = (uint)scopes.Length;
+ if (numberOfScopes == 0)
+ {
+ return;
+ }
+
+ MemoryStream customMetadata = new MemoryStream();
+ BinaryWriter cmw = new BinaryWriter(customMetadata);
+ cmw.WriteByte(4); // version
+ cmw.WriteByte(3); // kind: IteratorLocals
+ cmw.Align(4);
+ cmw.WriteUint(12 + numberOfScopes * 8);
+ cmw.WriteUint(numberOfScopes);
+ foreach (var scope in scopes)
+ {
+ cmw.WriteUint(scope.Offset);
+ cmw.WriteUint(scope.Offset + scope.Length);
+ }
+
+ customDebugInfo.Add(customMetadata);
+ }
+
+ private static void SerializeDynamicLocalInfo(IMethodBody methodBody, ArrayBuilder customDebugInfo)
+ {
+ if (!methodBody.HasDynamicLocalVariables)
+ {
+ return; //There are no dynamic locals
+ }
+
+ var dynamicLocals = ArrayBuilder.GetInstance();
+
+ foreach (ILocalDefinition local in methodBody.LocalVariables)
+ {
+ if (local.IsDynamic)
+ {
+ dynamicLocals.Add(local);
+ }
+ }
+
+ int dynamicVariableCount = dynamicLocals.Count;
+
+ foreach (var currentScope in methodBody.LocalScopes)
+ {
+ foreach (var localConstant in currentScope.Constants)
+ {
+ if (localConstant.IsDynamic)
+ {
+ dynamicLocals.Add(localConstant);
+ }
+ }
+ }
+
+ Debug.Assert(dynamicLocals.Any()); // There must be atleast one dynamic local if this point is reached
+
+ const int blobSize = 200;//DynamicAttribute - 64, DynamicAttributeLength - 4, SlotIndex -4, IdentifierName - 128
+ MemoryStream customMetadata = new MemoryStream();
+ BinaryWriter cmw = new BinaryWriter(customMetadata, true);
+ cmw.WriteByte(4);//Version
+ cmw.WriteByte(5);//Kind : Dynamic Locals
+ cmw.Align(4);
+ // size = Version,Kind + size + cBuckets + (dynamicCount * sizeOf(Local Blob))
+ cmw.WriteUint(4 + 4 + 4 + (uint)dynamicLocals.Count * blobSize);//Size of the Dynamic Block
+ cmw.WriteUint((uint)dynamicLocals.Count);
+
+ int localIndex = 0;
+ foreach (ILocalDefinition local in dynamicLocals)
+ {
+ if (local.Name.Length > 63)//Ignore and push empty information
+ {
+ cmw.WriteBytes(0, blobSize);
+ continue;
+ }
+
+ var dynamicTransformFlags = local.DynamicTransformFlags;
+ if (!dynamicTransformFlags.IsDefault && dynamicTransformFlags.Length <= 64)
+ {
+ byte[] flag = new byte[64];
+ for (int k = 0; k < dynamicTransformFlags.Length; k++)
+ {
+ if ((bool)dynamicTransformFlags[k].Value)
+ {
+ flag[k] = (byte)1;
+ }
+ }
+ cmw.WriteBytes(flag); //Written Flag
+ cmw.WriteUint((uint)dynamicTransformFlags.Length); //Written Length
+ }
+ else
+ {
+ cmw.WriteBytes(0, 68); //Empty flag array and size.
+ }
+
+ if (localIndex < dynamicVariableCount)
+ {
+ // Dynamic variable
+ cmw.WriteUint((uint)local.SlotIndex);
+ }
+ else
+ {
+ // Dynamic constant
+ cmw.WriteUint(0);
+ }
+
+ char[] localName = new char[64];
+ local.Name.CopyTo(0, localName, 0, local.Name.Length);
+ cmw.WriteChars(localName);
+
+ localIndex++;
+ }
+
+ dynamicLocals.Free();
+ customDebugInfo.Add(customMetadata);
+ }
+
+ private static byte[] SerializeCustomDebugMetadata(ArrayBuilder customDebugInfo)
+ {
+ if (customDebugInfo.Count == 0)
+ {
+ return null;
+ }
+
+ MemoryStream customMetadata = MemoryStream.GetInstance();
+ BinaryWriter cmw = new BinaryWriter(customMetadata);
+ cmw.WriteByte(4); // version
+ cmw.WriteByte((byte)customDebugInfo.Count); // count
+ cmw.Align(4);
+ foreach (MemoryStream ms in customDebugInfo)
+ {
+ ms.WriteTo(customMetadata);
+ }
+
+ var result = customMetadata.ToArray();
+ customMetadata.Free();
+ return result;
+ }
+
+ private void SerializeNamespaceScopeMetadata(IMethodBody methodBody, ArrayBuilder customDebugInfo)
+ {
+ if (ShouldForwardToPreviousMethodWithUsingInfo(methodBody))
+ {
+ Debug.Assert(!ReferenceEquals(this.previousMethodBodyWithUsingInfo, methodBody));
+ SerializeReferenceToPreviousMethodWithUsingInfo(customDebugInfo);
+ return;
+ }
+
+ MemoryStream customMetadata = new MemoryStream();
+ List usingCounts = new List();
+ BinaryWriter cmw = new BinaryWriter(customMetadata);
+ foreach (NamespaceScope namespaceScope in methodBody.NamespaceScopes)
+ {
+ usingCounts.Add((ushort)namespaceScope.UsedNamespaces.Length);
+ }
+
+ // ACASEY: This originally wrote (uint)12, (ushort)1, (ushort)0 in the
+ // case where usingCounts was empty, but I'm not sure why.
+ if (usingCounts.Count > 0)
+ {
+ uint streamLength = 0;
+ cmw.WriteByte(4); // version
+ cmw.WriteByte(0); // kind: UsingInfo
+ cmw.Align(4);
+
+ cmw.WriteUint(streamLength = PeWriter.Aligned((uint)usingCounts.Count * 2 + 10, 4));
+ cmw.WriteUshort((ushort)usingCounts.Count);
+ foreach (ushort uc in usingCounts)
+ {
+ cmw.WriteUshort(uc);
+ }
+
+ cmw.Align(4);
+ Debug.Assert(streamLength == customMetadata.Length);
+ customDebugInfo.Add(customMetadata);
+ }
+
+ if (this.methodBodyWithModuleInfo != null && !ReferenceEquals(this.methodBodyWithModuleInfo, methodBody))
+ {
+ SerializeReferenceToMethodWithModuleInfo(customDebugInfo);
+ }
+ }
+
+ private bool ShouldForwardToPreviousMethodWithUsingInfo(IMethodBody methodBody)
+ {
+ if (this.previousMethodBodyWithUsingInfo == null || ReferenceEquals(this.previousMethodBodyWithUsingInfo, methodBody))
+ {
+ return false;
+ }
+
+ // CONSIDER: is there a more efficient way to check if the scopes are the same?
+ // CONSIDER: might want to cache the list of scopes.
+ var previousScopes = this.previousMethodBodyWithUsingInfo.NamespaceScopes;
+ return methodBody.NamespaceScopes.SequenceEqual(previousScopes, NamespaceScopeComparer.Instance);
+ }
+
+ private void SerializeReferenceToMethodWithModuleInfo(ArrayBuilder customDebugInfo)
+ {
+ MemoryStream customMetadata = new MemoryStream(12);
+ BinaryWriter cmw = new BinaryWriter(customMetadata);
+ cmw.WriteByte(4); // version
+ cmw.WriteByte(2); // kind: ForwardToModuleInfo
+ cmw.Align(4);
+ cmw.WriteUint(12);
+ cmw.WriteUint(this.methodTokenWithModuleInfo);
+ customDebugInfo.Add(customMetadata);
+ }
+
+ private void SerializeReferenceToPreviousMethodWithUsingInfo(ArrayBuilder customDebugInfo)
+ {
+ MemoryStream customMetadata = new MemoryStream(12);
+ BinaryWriter cmw = new BinaryWriter(customMetadata);
+ cmw.WriteByte(4); // version
+ cmw.WriteByte(1); // kind: ForwardInfo
+ cmw.Align(4);
+ cmw.WriteUint(12);
+ cmw.WriteUint(this.previousMethodTokenWithUsingInfo);
+ customDebugInfo.Add(customMetadata);
+ }
+
+ private class NamespaceScopeComparer : IEqualityComparer
+ {
+ public static readonly IEqualityComparer Instance = new NamespaceScopeComparer();
+
+ public bool Equals(NamespaceScope x, NamespaceScope y)
+ {
+ Debug.Assert(x != null);
+ Debug.Assert(y != null);
+ return x.UsedNamespaces.SequenceEqual(y.UsedNamespaces, UsedNamespaceOrTypeComparer.Instance);
+ }
+
+ public int GetHashCode(NamespaceScope obj)
+ {
+ throw ExceptionUtilities.Unreachable;
+ }
+ }
+
+ private class UsedNamespaceOrTypeComparer : IEqualityComparer
+ {
+ public static readonly IEqualityComparer Instance = new UsedNamespaceOrTypeComparer();
+
+ public bool Equals(UsedNamespaceOrType x, UsedNamespaceOrType y)
+ {
+ Debug.Assert(x != null);
+ Debug.Assert(y != null);
+ return x.Kind == y.Kind &&
+ x.Alias == y.Alias &&
+ x.TargetName == y.TargetName &&
+ x.ExternAlias == y.ExternAlias &&
+ x.ProjectLevel == y.ProjectLevel;
+ }
+
+ public int GetHashCode(UsedNamespaceOrType obj)
+ {
+ Debug.Assert(false);
+ return 0;
+ }
+ }
+ }
+}
diff --git a/Src/Compilers/Core/Source/PEWriter/Members.cs b/Src/Compilers/Core/Source/PEWriter/Members.cs
index a4e294cb3fb8a99c80a7ccaa88a9fefd5ca1bcb8..572c50387fbd74e5a70e2660fac92b4671574897 100644
--- a/Src/Compilers/Core/Source/PEWriter/Members.cs
+++ b/Src/Compilers/Core/Source/PEWriter/Members.cs
@@ -301,6 +301,11 @@ ImmutableArray CustomModifiers
/// Use rather than null.
///
Location Location { get; }
+
+ ///
+ /// Slot index or -1 if not applicable.
+ ///
+ int SlotIndex { get; }
}
///
diff --git a/Src/Compilers/Core/Source/PEWriter/PeDebugDirectory.cs b/Src/Compilers/Core/Source/PEWriter/PeDebugDirectory.cs
index 12d703f74075a64e58b7c34fb500ea09e5104a78..cac3d63df1c1ee6f78f0f0835166f66e8c84bb22 100644
--- a/Src/Compilers/Core/Source/PEWriter/PeDebugDirectory.cs
+++ b/Src/Compilers/Core/Source/PEWriter/PeDebugDirectory.cs
@@ -1,6 +1,6 @@
// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
- namespace Microsoft.Cci
+namespace Microsoft.Cci
{
internal class PeDebugDirectory
{
diff --git a/Src/Compilers/Core/Source/PEWriter/PeWriter.cs b/Src/Compilers/Core/Source/PEWriter/PeWriter.cs
index e62808a2b76413e5bcee134ad583597f5508fe35..7c3a0ff3d978c45560e6eead63fe3f3a83e77ec6 100644
--- a/Src/Compilers/Core/Source/PEWriter/PeWriter.cs
+++ b/Src/Compilers/Core/Source/PEWriter/PeWriter.cs
@@ -15,7 +15,6 @@
using System.Text;
using System.Threading;
using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.Collections;
using Roslyn.Utilities;
using EmitContext = Microsoft.CodeAnalysis.Emit.EmitContext;
@@ -499,9 +498,7 @@ protected virtual void OnSerializedMetadataTables()
// A map of method body to RVA.
private readonly Dictionary possiblyDuplicateMethodBodies;
private readonly MemoryStream methodStream = new MemoryStream(32 * 1024);
- private uint tokenOfMethodWithModuleInfo;
- private IMethodBody previousMethodBodyWithUsingInfo;
-
+
private byte blobIndexSize;
private byte stringIndexSize;
private byte guidIndexSize;
@@ -820,7 +817,7 @@ private void PopulateTablesAndSerializeMethodBodies(bool separateMethodIL)
FillInSectionHeaders(); // Do this here so that tables and win32 resources can contain actual RVAs without the need for fixups.
}
- private static uint Aligned(uint position, uint alignment)
+ internal static uint Aligned(uint position, uint alignment)
{
uint result = position & ~(alignment - 1);
if (result == position)
@@ -2687,7 +2684,7 @@ public static uint GetTypeDefFlags(ITypeDefinition typeDef, EmitContext context)
result |= TypeAttributes.UnicodeClass;
break;
- case Cci.Constants.CharSet_Auto:
+ case Constants.CharSet_Auto:
result |= TypeAttributes.AutoClass;
break;
}
@@ -5232,6 +5229,8 @@ private void SerializeMethodBodies(bool separateMethodIL)
writer.WriteUint(0);
}
+ var customDebugInfoWriter = new CustomDebugInfoWriter();
+
foreach (IMethodDefinition method in this.GetMethodDefs())
{
cancellationToken.ThrowIfCancellationRequested();
@@ -5244,7 +5243,7 @@ private void SerializeMethodBodies(bool separateMethodIL)
IMethodBody body = method.GetBody(Context);
if (body != null)
{
- this.SerializeMethodBody(body, writer);
+ this.SerializeMethodBody(body, writer, customDebugInfoWriter);
this.OnSerializedMethodBody(method, body);
}
else if (!allowMissingMethodBodies)
@@ -5254,9 +5253,7 @@ private void SerializeMethodBodies(bool separateMethodIL)
}
}
- private readonly List customDebugMetadataForCurrentMethod = new List();
-
- private void SerializeMethodBody(IMethodBody methodBody, BinaryWriter writer)
+ private void SerializeMethodBody(IMethodBody methodBody, BinaryWriter writer, CustomDebugInfoWriter customDebugInfoWriter)
{
uint localVariableSignatureToken = this.SerializeLocalVariableSignatureAndReturnToken(methodBody);
@@ -5275,7 +5272,7 @@ private void SerializeMethodBody(IMethodBody methodBody, BinaryWriter writer)
if (possiblyDuplicateMethodBodies.TryGetValue(il, out ilBeginningOffset))
{
this.methodBodyIndex[methodBody.MethodDefinition] = ilBeginningOffset;
- SerializeDebugInfo(methodBody, (uint)il.Length);
+ SerializeDebugInfo(methodBody, (uint)il.Length, customDebugInfoWriter);
return;
}
@@ -5312,22 +5309,13 @@ private void SerializeMethodBody(IMethodBody methodBody, BinaryWriter writer)
this.SerializeMethodBodyExceptionHandlerTable(methodBody, numberOfExceptionHandlers, writer);
}
- SerializeDebugInfo(methodBody, (uint)il.Length);
+ SerializeDebugInfo(methodBody, (uint)il.Length, customDebugInfoWriter);
}
- private void SerializeDebugInfo(IMethodBody methodBody, uint ilLength)
+ private void SerializeDebugInfo(IMethodBody methodBody, uint ilLength, CustomDebugInfoWriter customDebugInfoWriter)
{
- // NOTE: This is an attempt to match Dev10's apparent behavior. For iterator methods (i.e. the method
- // that appears in source, not the synthesized ones), Dev10 only emits the ForwardIterator and IteratorLocal
- // custom debug info (e.g. there will be no information about the usings that were in scope).
- // NOTE: There seems to be an unusual behavior in ISymUnmanagedWriter where, if all the methods in a type are
- // iterator methods, no custom debug info is emitted for any method. Adding a single non-iterator
- // method causes the custom debug info to be produced for all methods (including the iterator methods).
- // Since we are making the same ISymUnmanagedWriter calls as Dev10, we see the same behavior (i.e. this
- // is not a regression).
- bool hasIteratorClassName = methodBody.IteratorClassName != null;
-
- bool emitDebugInfo = this.pdbWriter != null && (hasIteratorClassName || methodBody.HasAnyLocations);
+ bool isIterator = methodBody.IteratorClassName != null;
+ bool emitDebugInfo = this.pdbWriter != null && (isIterator || methodBody.HasAnyLocations);
if (!emitDebugInfo)
{
@@ -5341,9 +5329,7 @@ private void SerializeDebugInfo(IMethodBody methodBody, uint ilLength)
var scopes = methodBody.LocalScopes;
- this.customDebugMetadataForCurrentMethod.Clear();
-
- // EDMAURER CCI originally didn't have the notion of the default scope that is open
+ // CCI originally didn't have the notion of the default scope that is open
// when a method is opened. In order to reproduce CSC PDBs, this must be added. Otherwise
// a seemingly unecessary scope that contains only other scopes is put in the PDB.
if (scopes.Length > 0)
@@ -5351,25 +5337,26 @@ private void SerializeDebugInfo(IMethodBody methodBody, uint ilLength)
this.DefineScopeLocals(scopes[0]);
}
- // NOTE: skipping using debug info for iterator methods (see comment on hasIteratorClassName).
- if (!hasIteratorClassName)
+ // NOTE: This is an attempt to match Dev10's apparent behavior. For iterator methods (i.e. the method
+ // that appears in source, not the synthesized ones), Dev10 only emits the ForwardIterator and IteratorLocal
+ // custom debug info (e.g. there will be no information about the usings that were in scope).
+ if (!isIterator)
{
- this.SerializeNamespaceScopes(methodBody);
- }
+ IMethodDefinition forwardToMethod;
+ if (customDebugInfoWriter.ShouldForwardNamespaceScopes(methodBody, methodToken, out forwardToMethod))
+ {
+ if (forwardToMethod != null)
+ {
+ string usingString = "@" + this.GetMethodToken(forwardToMethod);
+ Debug.Assert(!IsUsingStringTooLong(usingString));
+ this.pdbWriter.UsingNamespace(usingString, methodBody.MethodDefinition.Name);
+ }
- // CONSIDER: this may not be the same "first" method as in Dev10, but
- // it shouldn't matter since all methods will still forward to a method
- // containing the appropriate information.
- if (this.tokenOfMethodWithModuleInfo == 0) //UNDONE: || edit-and-continue
- {
- // This module level information could go on every method (and does in
- // the edit-and-continue case), but - as an optimization - we'll just
- // put it on the first method we happen to encounter and then put a
- // reference to the first method's token in every other method (so they
- // can find the information).
- if (this.EmitExternNamespaceAssemblies(this.module))
+ // otherwise, the forwarding is done via custom debug info
+ }
+ else
{
- this.tokenOfMethodWithModuleInfo = methodToken;
+ this.SerializeNamespaceScopes(methodBody);
}
}
@@ -5388,70 +5375,25 @@ private void SerializeDebugInfo(IMethodBody methodBody, uint ilLength)
asyncDebugInfo.ResumeOffsets);
}
- this.SerializeIteratorClassMetadata(methodBody);
-
- // NOTE: skipping using and iterator local debug info for iterator methods (see comment on hasIteratorClassName).
- if (!hasIteratorClassName)
+ bool emitExternNamespaces;
+ byte[] blob = customDebugInfoWriter.SerializeMethodDebugInfo(module, methodBody, methodToken, out emitExternNamespaces);
+ if (blob != null)
{
- this.SerializeNamespaceScopeMetadata(methodBody);
- this.SerializeIteratorLocalScopes(methodBody);
+ this.pdbWriter.DefineCustomMetadata("MD2", blob);
}
- this.SerializeDynamicLocalInfo(methodBody);
-
- if (methodBody.CustomDebugInfoKind == CustomDebugInfoKind.CSharpStyle)
+ if (emitExternNamespaces)
{
- this.SerializeCustomDebugMetadata();
+ this.EmitExternNamespaceAssemblies(this.module);
}
this.pdbWriter.CloseMethod(ilLength);
}
- private void SerializeReferenceToMethodWithModuleInfo()
- {
- MemoryStream customMetadata = new MemoryStream(12);
- BinaryWriter cmw = new BinaryWriter(customMetadata);
- cmw.WriteByte(4); // version
- cmw.WriteByte(2); // kind: ForwardToModuleInfo
- cmw.Align(4);
- cmw.WriteUint(12);
- cmw.WriteUint(this.tokenOfMethodWithModuleInfo);
- this.customDebugMetadataForCurrentMethod.Add(customMetadata);
- }
-
- private void SerializeReferenceToPreviousMethodWithUsingInfo()
- {
- MemoryStream customMetadata = new MemoryStream(12);
- BinaryWriter cmw = new BinaryWriter(customMetadata);
- cmw.WriteByte(4); // version
- cmw.WriteByte(1); // kind: ForwardInfo
- cmw.Align(4);
- cmw.WriteUint(12);
- cmw.WriteUint(this.GetMethodToken(this.previousMethodBodyWithUsingInfo.MethodDefinition));
- this.customDebugMetadataForCurrentMethod.Add(customMetadata);
- }
-
private void SerializeNamespaceScopes(IMethodBody methodBody)
{
// TODO: add nopia namespaces
-
- ImmutableArray scopes;
- if (ShouldForwardToPreviousMethodWithUsingInfo(methodBody, out scopes) || scopes.IsEmpty)
- {
- // SerializeNamespaceScopeMetadata will do the actual forwarding in case this is a CSharp method.
- // VB on the other hand adds a "@methodtoken" to the scopes instead.
- if (methodBody.CustomDebugInfoKind == CustomDebugInfoKind.VisualBasicStyle)
- {
- UInt32 methodToken = this.GetMethodToken(this.previousMethodBodyWithUsingInfo.MethodDefinition);
- string usingString = "@" + methodToken;
- Debug.Assert(!IsUsingStringTooLong(usingString));
- this.pdbWriter.UsingNamespace(usingString, methodBody.MethodDefinition.Name);
- }
- return;
- }
-
- this.previousMethodBodyWithUsingInfo = methodBody;
-
+
// in case that the using namespace is too long, the native API SymWriter::UsingNamespace() would return
// an E_OUTOFMEMORY which will be translated to an OutOfMemory exception.
// To avoid this we're checking the length before calling. However if _MAX_SYM_SIZE ever gets changed in
@@ -5471,7 +5413,7 @@ private void SerializeNamespaceScopes(IMethodBody methodBody)
// DevDiv Bug 479703: "Possible access violation because of uninitialized data in string pool of SymWriter" (Resolved in RTM)
IMethodDefinition methodDefinition = methodBody.MethodDefinition;
- foreach (NamespaceScope namespaceScope in scopes)
+ foreach (NamespaceScope namespaceScope in methodBody.NamespaceScopes)
{
foreach (UsedNamespaceOrType used in namespaceScope.UsedNamespaces)
{
@@ -5484,275 +5426,16 @@ private void SerializeNamespaceScopes(IMethodBody methodBody)
}
}
- private bool EmitExternNamespaceAssemblies(IModule module)
+ private void EmitExternNamespaceAssemblies(IModule module)
{
- bool emitted = false;
- foreach (ExternNamespace @extern in module.GetExternNamespaces())
+ foreach (ExternNamespace @extern in module.ExternNamespaces)
{
string usingString = "Z" + @extern.NamespaceAlias + " " + @extern.AssemblyName;
if (!IsUsingStringTooLong(usingString))
{
this.pdbWriter.UsingNamespace(usingString, @extern.NamespaceAlias);
}
-
- emitted = true; // Not strictly true if the name was too long, but there's no point in retrying.
}
-
- return emitted;
- }
-
- private void SerializeIteratorLocalScopes(IMethodBody methodBody)
- {
- ImmutableArray scopes = methodBody.IteratorScopes;
- uint numberOfScopes = (uint)scopes.Length;
- if (numberOfScopes == 0)
- {
- return;
- }
-
- MemoryStream customMetadata = new MemoryStream();
- BinaryWriter cmw = new BinaryWriter(customMetadata);
- cmw.WriteByte(4); // version
- cmw.WriteByte(3); // kind: IteratorLocals
- cmw.Align(4);
- cmw.WriteUint(12 + numberOfScopes * 8);
- cmw.WriteUint(numberOfScopes);
- foreach (var scope in scopes)
- {
- cmw.WriteUint(scope.Offset);
- cmw.WriteUint(scope.Offset + scope.Length);
- }
-
- this.customDebugMetadataForCurrentMethod.Add(customMetadata);
- }
-
- private void SerializeDynamicLocalInfo(IMethodBody methodBody)
- {
- if (!methodBody.HasDynamicLocalVariables)
- {
- return; //There are no dynamic locals
- }
-
- IEnumerable locals = methodBody.LocalVariables;
- ArrayBuilder dynamicLocals = ArrayBuilder.GetInstance();
- foreach (ILocalDefinition local in locals)
- {
- if (local.IsDynamic)
- {
- dynamicLocals.Add(local);
- }
- }
-
- foreach (var currentScope in methodBody.LocalScopes)
- {
- foreach (var localConstant in currentScope.Constants)
- {
- if (localConstant.IsDynamic)
- {
- dynamicLocals.Add(localConstant);
- }
- }
- }
-
- Debug.Assert(dynamicLocals.Any()); // There must be atleast one dynamic local if this point is reached
-
- const int blobSize = 200;//DynamicAttribute - 64, DynamicAttributeLength - 4, SlotIndex -4, IdentifierName - 128
- MemoryStream customMetadata = new MemoryStream();
- BinaryWriter cmw = new BinaryWriter(customMetadata, true);
- cmw.WriteByte(4);//Version
- cmw.WriteByte(5);//Kind : Dynamic Locals
- cmw.Align(4);
- // size = Version,Kind + size + cBuckets + (dynamicCount * sizeOf(Local Blob))
- cmw.WriteUint(4 + 4 + 4 + (uint)dynamicLocals.Count * blobSize);//Size of the Dynamic Block
- cmw.WriteUint((uint)dynamicLocals.Count);
-
- foreach (ILocalDefinition local in dynamicLocals)
- {
- if (local.Name.Length > 63)//Ignore and push empty information
- {
- cmw.WriteBytes(0, blobSize);
- continue;
- }
-
- var dynamicTransformFlags = local.DynamicTransformFlags;
- if (!dynamicTransformFlags.IsDefault && dynamicTransformFlags.Length <= 64)
- {
- byte[] flag = new byte[64];
- for (int k = 0; k < dynamicTransformFlags.Length; k++)
- {
- if ((bool)dynamicTransformFlags[k].Value)
- {
- flag[k] = (byte)1;
- }
- }
- cmw.WriteBytes(flag); //Written Flag
- cmw.WriteUint((uint)dynamicTransformFlags.Length); //Written Length
- }
- else
- {
- cmw.WriteBytes(0, 68); //Empty flag array and size.
- }
-
- if (local is LocalDefinition)
- {
- cmw.WriteUint((uint)(local as LocalDefinition).SlotIndex); //Written Slot Index
- }
- else
- {
- Debug.Assert(local is LocalConstantDefinition);
- cmw.WriteUint((uint)0);// Slot number is "0" for const dynamics. Native Behavior.
- }
- char[] localName = new char[64];
- local.Name.CopyTo(0, localName, 0, local.Name.Length);
- cmw.WriteChars(localName);
- }
-
- dynamicLocals.Free();
- this.customDebugMetadataForCurrentMethod.Add(customMetadata);
- }
-
- private void SerializeCustomDebugMetadata()
- {
- if (this.customDebugMetadataForCurrentMethod.Count == 0)
- {
- return;
- }
-
- MemoryStream customMetadata = MemoryStream.GetInstance();
- BinaryWriter cmw = new BinaryWriter(customMetadata);
- cmw.WriteByte(4); // version
- cmw.WriteByte((byte)this.customDebugMetadataForCurrentMethod.Count); // count
- cmw.Align(4);
- foreach (MemoryStream ms in this.customDebugMetadataForCurrentMethod)
- {
- ms.WriteTo(customMetadata);
- }
-
- this.pdbWriter.DefineCustomMetadata("MD2", customMetadata.ToArray());
- customMetadata.Free();
- }
-
- private void SerializeNamespaceScopeMetadata(IMethodBody methodBody)
- {
- ImmutableArray scopes;
- if (ShouldForwardToPreviousMethodWithUsingInfo(methodBody, out scopes))
- {
- Debug.Assert(!ReferenceEquals(this.previousMethodBodyWithUsingInfo, methodBody));
- this.SerializeReferenceToPreviousMethodWithUsingInfo();
- return;
- }
-
- MemoryStream customMetadata = new MemoryStream();
- List usingCounts = new List();
- BinaryWriter cmw = new BinaryWriter(customMetadata);
- foreach (NamespaceScope namespaceScope in scopes)
- {
- usingCounts.Add((ushort)namespaceScope.UsedNamespaces.Length);
- }
-
- // ACASEY: This originally wrote (uint)12, (ushort)1, (ushort)0 in the
- // case where usingCounts was empty, but I'm not sure why.
- if (usingCounts.Count > 0)
- {
- uint streamLength = 0;
- cmw.WriteByte(4); // version
- cmw.WriteByte(0); // kind: UsingInfo
- cmw.Align(4);
-
- cmw.WriteUint(streamLength = Aligned((uint)usingCounts.Count * 2 + 10, 4));
- cmw.WriteUshort((ushort)usingCounts.Count);
- foreach (ushort uc in usingCounts)
- {
- cmw.WriteUshort(uc);
- }
-
- cmw.Align(4);
- Debug.Assert(streamLength == customMetadata.Length);
- this.customDebugMetadataForCurrentMethod.Add(customMetadata);
- }
-
- if (this.tokenOfMethodWithModuleInfo != 0 && this.tokenOfMethodWithModuleInfo != this.GetMethodToken(methodBody.MethodDefinition))
- {
- this.SerializeReferenceToMethodWithModuleInfo();
- }
- }
-
- private bool ShouldForwardToPreviousMethodWithUsingInfo(IMethodBody methodBody, out ImmutableArray currentScopes)
- {
- currentScopes = methodBody.NamespaceScopes;
-
- if (ReferenceEquals(this.previousMethodBodyWithUsingInfo, null) ||
- ReferenceEquals(this.previousMethodBodyWithUsingInfo, methodBody))
- {
- return false;
- }
-
- // CONSIDER: is there a more efficient way to check if the scopes are the same?
- // CONSIDER: might want to cache the list of scopes.
- var previousScopes = this.previousMethodBodyWithUsingInfo.NamespaceScopes;
- return currentScopes.SequenceEqual(previousScopes, NamespaceScopeComparer.Instance);
- }
-
- private class NamespaceScopeComparer : IEqualityComparer
- {
- public static readonly IEqualityComparer Instance = new NamespaceScopeComparer();
-
- public bool Equals(NamespaceScope x, NamespaceScope y)
- {
- Debug.Assert(x != null);
- Debug.Assert(y != null);
- return x.UsedNamespaces.SequenceEqual(y.UsedNamespaces, UsedNamespaceOrTypeComparer.Instance);
- }
-
- public int GetHashCode(NamespaceScope obj)
- {
- Debug.Assert(false);
- return 0;
- }
- }
-
- private class UsedNamespaceOrTypeComparer : IEqualityComparer
- {
- public static readonly IEqualityComparer Instance = new UsedNamespaceOrTypeComparer();
-
- public bool Equals(UsedNamespaceOrType x, UsedNamespaceOrType y)
- {
- Debug.Assert(x != null);
- Debug.Assert(y != null);
- return x.Kind == y.Kind &&
- x.Alias == y.Alias &&
- x.TargetName == y.TargetName &&
- x.ExternAlias == y.ExternAlias &&
- x.ProjectLevel == y.ProjectLevel;
- }
-
- public int GetHashCode(UsedNamespaceOrType obj)
- {
- Debug.Assert(false);
- return 0;
- }
- }
-
- private void SerializeIteratorClassMetadata(IMethodBody methodBody)
- {
- this.SerializeReferenceToIteratorClass(methodBody.IteratorClassName);
- }
-
- private void SerializeReferenceToIteratorClass(string iteratorClassName)
- {
- if (iteratorClassName == null) return;
- MemoryStream customMetadata = new MemoryStream();
- BinaryWriter cmw = new BinaryWriter(customMetadata, true);
- cmw.WriteByte(4); // version
- cmw.WriteByte(4); // kind: ForwardIterator
- cmw.Align(4);
- uint length = 10 + (uint)iteratorClassName.Length * 2;
- if ((length & 3) != 0) length += 4 - (length & 3);
- cmw.WriteUint(length);
- cmw.WriteString(iteratorClassName, true);
- cmw.Align(4);
- Debug.Assert(customMetadata.Position == length);
- this.customDebugMetadataForCurrentMethod.Add(customMetadata);
}
private uint ReadUint(byte[] buffer, int pos)
@@ -5846,11 +5529,11 @@ private byte[] SerializeMethodBodyIL(IMethodBody methodBody)
return methodBodyIL;
}
- private void EmitPdbScopes(ImmutableArray scopes)
+ private void EmitPdbScopes(ImmutableArray scopes)
{
// The order of OpenScope and CloseScope calls must follow the scope nesting.
- var scopeStack = ArrayBuilder.GetInstance();
+ var scopeStack = ArrayBuilder.GetInstance();
for (int i = 1; i < scopes.Length; i++)
{
@@ -5859,7 +5542,7 @@ private void EmitPdbScopes(ImmutableArray scopes)
// Close any scopes that have finished.
while (scopeStack.Count > 0)
{
- Cci.LocalScope topScope = scopeStack.Last();
+ LocalScope topScope = scopeStack.Last();
if (currentScope.Offset < topScope.Offset + topScope.Length)
{
break;
@@ -5878,7 +5561,7 @@ private void EmitPdbScopes(ImmutableArray scopes)
// Close remaining scopes.
for (int i = scopeStack.Count - 1; i >= 0; i--)
{
- Cci.LocalScope scope = scopeStack[i];
+ LocalScope scope = scopeStack[i];
this.pdbWriter.CloseScope(scope.Offset + scope.Length);
}
@@ -6195,7 +5878,7 @@ private void SerializeMarshallingDescriptor(IMarshallingInformation marshallingI
break;
- case Cci.Constants.UnmanagedType_CustomMarshaler:
+ case Constants.UnmanagedType_CustomMarshaler:
writer.WriteUshort(0); // padding
object marshaller = marshallingInformation.GetCustomMarshaller(Context);
diff --git a/Src/Compilers/Core/Source/PEWriter/Units.cs b/Src/Compilers/Core/Source/PEWriter/Units.cs
index 7b4ab805f46085f423054b32629eb80965c10152..55c0b7034c486d21be767958f4560a025adcd148 100644
--- a/Src/Compilers/Core/Source/PEWriter/Units.cs
+++ b/Src/Compilers/Core/Source/PEWriter/Units.cs
@@ -390,7 +390,7 @@ ulong SizeOfStackReserve
///
MultiDictionary GetSymbolToLocationMap();
- ImmutableArray GetExternNamespaces();
+ ImmutableArray ExternNamespaces { get; }
ushort MajorSubsystemVersion { get; }
ushort MinorSubsystemVersion { get; }
diff --git a/Src/Compilers/Core/Source/ReferenceManager/CommonReferenceManager.Resolution.cs b/Src/Compilers/Core/Source/ReferenceManager/CommonReferenceManager.Resolution.cs
index 5d83140e05446de83d1ab29071d64d28a3156023..654de0a5ba3cebb9d7a9b7546e7b97a1427749ef 100644
--- a/Src/Compilers/Core/Source/ReferenceManager/CommonReferenceManager.Resolution.cs
+++ b/Src/Compilers/Core/Source/ReferenceManager/CommonReferenceManager.Resolution.cs
@@ -684,7 +684,7 @@ private static void AddModule(PEModule module, int referenceIndex, ResolvedRefer
boundReferenceDirectives.Add(referenceDirective.File, boundReference);
}
- // Workaround for bug #531342: include facades if we reference a Portable Library:
+ // Workaround for bug #797360: include facades if we reference a Portable Library:
referencesBuilder.AddRange(ResolveFacadeReferences(compilation));
// add external reference at the end, so that they are processed first: