5 changed files with 4286 additions and 0 deletions
@ -0,0 +1,754 @@
@@ -0,0 +1,754 @@
|
||||
//
|
||||
// CryptoConvert.cs - Crypto Convertion Routines
|
||||
//
|
||||
// Author:
|
||||
// Sebastien Pouliot <sebastien@ximian.com>
|
||||
//
|
||||
// (C) 2003 Motus Technologies Inc. (http://www.motus.com)
|
||||
// Copyright (C) 2004-2006 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 System.Globalization; |
||||
using System.Security.Cryptography; |
||||
using System.Text; |
||||
|
||||
namespace Mono.Security.Cryptography { |
||||
|
||||
#if INSIDE_CORLIB
|
||||
internal |
||||
#else
|
||||
public |
||||
#endif
|
||||
sealed class CryptoConvert { |
||||
|
||||
private CryptoConvert () |
||||
{ |
||||
} |
||||
|
||||
static private int ToInt32LE (byte [] bytes, int offset) |
||||
{ |
||||
return (bytes [offset+3] << 24) | (bytes [offset+2] << 16) | (bytes [offset+1] << 8) | bytes [offset]; |
||||
} |
||||
|
||||
static private uint ToUInt32LE (byte [] bytes, int offset) |
||||
{ |
||||
return (uint)((bytes [offset+3] << 24) | (bytes [offset+2] << 16) | (bytes [offset+1] << 8) | bytes [offset]); |
||||
} |
||||
|
||||
static private byte [] GetBytesLE (int val) |
||||
{ |
||||
return new byte [] { |
||||
(byte) (val & 0xff), |
||||
(byte) ((val >> 8) & 0xff), |
||||
(byte) ((val >> 16) & 0xff), |
||||
(byte) ((val >> 24) & 0xff) |
||||
}; |
||||
} |
||||
|
||||
static private byte[] Trim (byte[] array) |
||||
{ |
||||
for (int i=0; i < array.Length; i++) { |
||||
if (array [i] != 0x00) { |
||||
byte[] result = new byte [array.Length - i]; |
||||
Buffer.BlockCopy (array, i, result, 0, result.Length); |
||||
return result; |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
// convert the key from PRIVATEKEYBLOB to RSA
|
||||
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/security/Security/private_key_blobs.asp
|
||||
// e.g. SNK files, PVK files
|
||||
static public RSA FromCapiPrivateKeyBlob (byte[] blob) |
||||
{ |
||||
return FromCapiPrivateKeyBlob (blob, 0); |
||||
} |
||||
|
||||
static public RSA FromCapiPrivateKeyBlob (byte[] blob, int offset) |
||||
{ |
||||
if (blob == null) |
||||
throw new ArgumentNullException ("blob"); |
||||
if (offset >= blob.Length) |
||||
throw new ArgumentException ("blob is too small."); |
||||
|
||||
RSAParameters rsap = new RSAParameters (); |
||||
try { |
||||
if ((blob [offset] != 0x07) || // PRIVATEKEYBLOB (0x07)
|
||||
(blob [offset+1] != 0x02) || // Version (0x02)
|
||||
(blob [offset+2] != 0x00) || // Reserved (word)
|
||||
(blob [offset+3] != 0x00) || |
||||
(ToUInt32LE (blob, offset+8) != 0x32415352)) // DWORD magic = RSA2
|
||||
throw new CryptographicException ("Invalid blob header"); |
||||
|
||||
// ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...)
|
||||
// int algId = ToInt32LE (blob, offset+4);
|
||||
|
||||
// DWORD bitlen
|
||||
int bitLen = ToInt32LE (blob, offset+12); |
||||
|
||||
// DWORD public exponent
|
||||
byte[] exp = new byte [4]; |
||||
Buffer.BlockCopy (blob, offset+16, exp, 0, 4); |
||||
Array.Reverse (exp); |
||||
rsap.Exponent = Trim (exp); |
||||
|
||||
int pos = offset+20; |
||||
// BYTE modulus[rsapubkey.bitlen/8];
|
||||
int byteLen = (bitLen >> 3); |
||||
rsap.Modulus = new byte [byteLen]; |
||||
Buffer.BlockCopy (blob, pos, rsap.Modulus, 0, byteLen); |
||||
Array.Reverse (rsap.Modulus); |
||||
pos += byteLen; |
||||
|
||||
// BYTE prime1[rsapubkey.bitlen/16];
|
||||
int byteHalfLen = (byteLen >> 1); |
||||
rsap.P = new byte [byteHalfLen]; |
||||
Buffer.BlockCopy (blob, pos, rsap.P, 0, byteHalfLen); |
||||
Array.Reverse (rsap.P); |
||||
pos += byteHalfLen; |
||||
|
||||
// BYTE prime2[rsapubkey.bitlen/16];
|
||||
rsap.Q = new byte [byteHalfLen]; |
||||
Buffer.BlockCopy (blob, pos, rsap.Q, 0, byteHalfLen); |
||||
Array.Reverse (rsap.Q); |
||||
pos += byteHalfLen; |
||||
|
||||
// BYTE exponent1[rsapubkey.bitlen/16];
|
||||
rsap.DP = new byte [byteHalfLen]; |
||||
Buffer.BlockCopy (blob, pos, rsap.DP, 0, byteHalfLen); |
||||
Array.Reverse (rsap.DP); |
||||
pos += byteHalfLen; |
||||
|
||||
// BYTE exponent2[rsapubkey.bitlen/16];
|
||||
rsap.DQ = new byte [byteHalfLen]; |
||||
Buffer.BlockCopy (blob, pos, rsap.DQ, 0, byteHalfLen); |
||||
Array.Reverse (rsap.DQ); |
||||
pos += byteHalfLen; |
||||
|
||||
// BYTE coefficient[rsapubkey.bitlen/16];
|
||||
rsap.InverseQ = new byte [byteHalfLen]; |
||||
Buffer.BlockCopy (blob, pos, rsap.InverseQ, 0, byteHalfLen); |
||||
Array.Reverse (rsap.InverseQ); |
||||
pos += byteHalfLen; |
||||
|
||||
// ok, this is hackish but CryptoAPI support it so...
|
||||
// note: only works because CRT is used by default
|
||||
// http://bugzilla.ximian.com/show_bug.cgi?id=57941
|
||||
rsap.D = new byte [byteLen]; // must be allocated
|
||||
if (pos + byteLen + offset <= blob.Length) { |
||||
// BYTE privateExponent[rsapubkey.bitlen/8];
|
||||
Buffer.BlockCopy (blob, pos, rsap.D, 0, byteLen); |
||||
Array.Reverse (rsap.D); |
||||
} |
||||
} |
||||
catch (Exception e) { |
||||
throw new CryptographicException ("Invalid blob.", e); |
||||
} |
||||
|
||||
#if NET_2_1
|
||||
RSA rsa = RSA.Create (); |
||||
rsa.ImportParameters (rsap); |
||||
#else
|
||||
RSA rsa = null; |
||||
try { |
||||
rsa = RSA.Create (); |
||||
rsa.ImportParameters (rsap); |
||||
} |
||||
catch (CryptographicException ce) { |
||||
// this may cause problem when this code is run under
|
||||
// the SYSTEM identity on Windows (e.g. ASP.NET). See
|
||||
// http://bugzilla.ximian.com/show_bug.cgi?id=77559
|
||||
try { |
||||
CspParameters csp = new CspParameters (); |
||||
csp.Flags = CspProviderFlags.UseMachineKeyStore; |
||||
rsa = new RSACryptoServiceProvider (csp); |
||||
rsa.ImportParameters (rsap); |
||||
} |
||||
catch { |
||||
// rethrow original, not the later, exception if this fails
|
||||
throw ce; |
||||
} |
||||
} |
||||
#endif
|
||||
return rsa; |
||||
} |
||||
|
||||
static public DSA FromCapiPrivateKeyBlobDSA (byte[] blob) |
||||
{ |
||||
return FromCapiPrivateKeyBlobDSA (blob, 0); |
||||
} |
||||
|
||||
static public DSA FromCapiPrivateKeyBlobDSA (byte[] blob, int offset) |
||||
{ |
||||
if (blob == null) |
||||
throw new ArgumentNullException ("blob"); |
||||
if (offset >= blob.Length) |
||||
throw new ArgumentException ("blob is too small."); |
||||
|
||||
DSAParameters dsap = new DSAParameters (); |
||||
try { |
||||
if ((blob [offset] != 0x07) || // PRIVATEKEYBLOB (0x07)
|
||||
(blob [offset + 1] != 0x02) || // Version (0x02)
|
||||
(blob [offset + 2] != 0x00) || // Reserved (word)
|
||||
(blob [offset + 3] != 0x00) || |
||||
(ToUInt32LE (blob, offset + 8) != 0x32535344)) // DWORD magic
|
||||
throw new CryptographicException ("Invalid blob header"); |
||||
|
||||
int bitlen = ToInt32LE (blob, offset + 12); |
||||
int bytelen = bitlen >> 3; |
||||
int pos = offset + 16; |
||||
|
||||
dsap.P = new byte [bytelen]; |
||||
Buffer.BlockCopy (blob, pos, dsap.P, 0, bytelen); |
||||
Array.Reverse (dsap.P); |
||||
pos += bytelen; |
||||
|
||||
dsap.Q = new byte [20]; |
||||
Buffer.BlockCopy (blob, pos, dsap.Q, 0, 20); |
||||
Array.Reverse (dsap.Q); |
||||
pos += 20; |
||||
|
||||
dsap.G = new byte [bytelen]; |
||||
Buffer.BlockCopy (blob, pos, dsap.G, 0, bytelen); |
||||
Array.Reverse (dsap.G); |
||||
pos += bytelen; |
||||
|
||||
dsap.X = new byte [20]; |
||||
Buffer.BlockCopy (blob, pos, dsap.X, 0, 20); |
||||
Array.Reverse (dsap.X); |
||||
pos += 20; |
||||
|
||||
dsap.Counter = ToInt32LE (blob, pos); |
||||
pos += 4; |
||||
|
||||
dsap.Seed = new byte [20]; |
||||
Buffer.BlockCopy (blob, pos, dsap.Seed, 0, 20); |
||||
Array.Reverse (dsap.Seed); |
||||
pos += 20; |
||||
} |
||||
catch (Exception e) { |
||||
throw new CryptographicException ("Invalid blob.", e); |
||||
} |
||||
|
||||
#if NET_2_1
|
||||
DSA dsa = (DSA)DSA.Create (); |
||||
dsa.ImportParameters (dsap); |
||||
#else
|
||||
DSA dsa = null; |
||||
try { |
||||
dsa = (DSA)DSA.Create (); |
||||
dsa.ImportParameters (dsap); |
||||
} |
||||
catch (CryptographicException ce) { |
||||
// this may cause problem when this code is run under
|
||||
// the SYSTEM identity on Windows (e.g. ASP.NET). See
|
||||
// http://bugzilla.ximian.com/show_bug.cgi?id=77559
|
||||
try { |
||||
CspParameters csp = new CspParameters (); |
||||
csp.Flags = CspProviderFlags.UseMachineKeyStore; |
||||
dsa = new DSACryptoServiceProvider (csp); |
||||
dsa.ImportParameters (dsap); |
||||
} |
||||
catch { |
||||
// rethrow original, not the later, exception if this fails
|
||||
throw ce; |
||||
} |
||||
} |
||||
#endif
|
||||
return dsa; |
||||
} |
||||
|
||||
static public byte[] ToCapiPrivateKeyBlob (RSA rsa) |
||||
{ |
||||
RSAParameters p = rsa.ExportParameters (true); |
||||
int keyLength = p.Modulus.Length; // in bytes
|
||||
byte[] blob = new byte [20 + (keyLength << 2) + (keyLength >> 1)]; |
||||
|
||||
blob [0] = 0x07; // Type - PRIVATEKEYBLOB (0x07)
|
||||
blob [1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02)
|
||||
// [2], [3] // RESERVED - Always 0
|
||||
blob [5] = 0x24; // ALGID - Always 00 24 00 00 (for CALG_RSA_SIGN)
|
||||
blob [8] = 0x52; // Magic - RSA2 (ASCII in hex)
|
||||
blob [9] = 0x53; |
||||
blob [10] = 0x41; |
||||
blob [11] = 0x32; |
||||
|
||||
byte[] bitlen = GetBytesLE (keyLength << 3); |
||||
blob [12] = bitlen [0]; // bitlen
|
||||
blob [13] = bitlen [1]; |
||||
blob [14] = bitlen [2]; |
||||
blob [15] = bitlen [3]; |
||||
|
||||
// public exponent (DWORD)
|
||||
int pos = 16; |
||||
int n = p.Exponent.Length; |
||||
while (n > 0) |
||||
blob [pos++] = p.Exponent [--n]; |
||||
// modulus
|
||||
pos = 20; |
||||
byte[] part = p.Modulus; |
||||
int len = part.Length; |
||||
Array.Reverse (part, 0, len); |
||||
Buffer.BlockCopy (part, 0, blob, pos, len); |
||||
pos += len; |
||||
// private key
|
||||
part = p.P; |
||||
len = part.Length; |
||||
Array.Reverse (part, 0, len); |
||||
Buffer.BlockCopy (part, 0, blob, pos, len); |
||||
pos += len; |
||||
|
||||
part = p.Q; |
||||
len = part.Length; |
||||
Array.Reverse (part, 0, len); |
||||
Buffer.BlockCopy (part, 0, blob, pos, len); |
||||
pos += len; |
||||
|
||||
part = p.DP; |
||||
len = part.Length; |
||||
Array.Reverse (part, 0, len); |
||||
Buffer.BlockCopy (part, 0, blob, pos, len); |
||||
pos += len; |
||||
|
||||
part = p.DQ; |
||||
len = part.Length; |
||||
Array.Reverse (part, 0, len); |
||||
Buffer.BlockCopy (part, 0, blob, pos, len); |
||||
pos += len; |
||||
|
||||
part = p.InverseQ; |
||||
len = part.Length; |
||||
Array.Reverse (part, 0, len); |
||||
Buffer.BlockCopy (part, 0, blob, pos, len); |
||||
pos += len; |
||||
|
||||
part = p.D; |
||||
len = part.Length; |
||||
Array.Reverse (part, 0, len); |
||||
Buffer.BlockCopy (part, 0, blob, pos, len); |
||||
|
||||
return blob; |
||||
} |
||||
|
||||
static public byte[] ToCapiPrivateKeyBlob (DSA dsa) |
||||
{ |
||||
DSAParameters p = dsa.ExportParameters (true); |
||||
int keyLength = p.P.Length; // in bytes
|
||||
|
||||
// header + P + Q + G + X + count + seed
|
||||
byte[] blob = new byte [16 + keyLength + 20 + keyLength + 20 + 4 + 20]; |
||||
|
||||
blob [0] = 0x07; // Type - PRIVATEKEYBLOB (0x07)
|
||||
blob [1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02)
|
||||
// [2], [3] // RESERVED - Always 0
|
||||
blob [5] = 0x22; // ALGID
|
||||
blob [8] = 0x44; // Magic
|
||||
blob [9] = 0x53; |
||||
blob [10] = 0x53; |
||||
blob [11] = 0x32; |
||||
|
||||
byte[] bitlen = GetBytesLE (keyLength << 3); |
||||
blob [12] = bitlen [0]; |
||||
blob [13] = bitlen [1]; |
||||
blob [14] = bitlen [2]; |
||||
blob [15] = bitlen [3]; |
||||
|
||||
int pos = 16; |
||||
byte[] part = p.P; |
||||
Array.Reverse (part); |
||||
Buffer.BlockCopy (part, 0, blob, pos, keyLength); |
||||
pos += keyLength; |
||||
|
||||
part = p.Q; |
||||
Array.Reverse (part); |
||||
Buffer.BlockCopy (part, 0, blob, pos, 20); |
||||
pos += 20; |
||||
|
||||
part = p.G; |
||||
Array.Reverse (part); |
||||
Buffer.BlockCopy (part, 0, blob, pos, keyLength); |
||||
pos += keyLength; |
||||
|
||||
part = p.X; |
||||
Array.Reverse (part); |
||||
Buffer.BlockCopy (part, 0, blob, pos, 20); |
||||
pos += 20; |
||||
|
||||
Buffer.BlockCopy (GetBytesLE (p.Counter), 0, blob, pos, 4); |
||||
pos += 4; |
||||
|
||||
part = p.Seed; |
||||
Array.Reverse (part); |
||||
Buffer.BlockCopy (part, 0, blob, pos, 20); |
||||
|
||||
return blob; |
||||
} |
||||
|
||||
static public RSA FromCapiPublicKeyBlob (byte[] blob) |
||||
{ |
||||
return FromCapiPublicKeyBlob (blob, 0); |
||||
} |
||||
|
||||
static public RSA FromCapiPublicKeyBlob (byte[] blob, int offset) |
||||
{ |
||||
if (blob == null) |
||||
throw new ArgumentNullException ("blob"); |
||||
if (offset >= blob.Length) |
||||
throw new ArgumentException ("blob is too small."); |
||||
|
||||
try { |
||||
if ((blob [offset] != 0x06) || // PUBLICKEYBLOB (0x06)
|
||||
(blob [offset+1] != 0x02) || // Version (0x02)
|
||||
(blob [offset+2] != 0x00) || // Reserved (word)
|
||||
(blob [offset+3] != 0x00) || |
||||
(ToUInt32LE (blob, offset+8) != 0x31415352)) // DWORD magic = RSA1
|
||||
throw new CryptographicException ("Invalid blob header"); |
||||
|
||||
// ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...)
|
||||
// int algId = ToInt32LE (blob, offset+4);
|
||||
|
||||
// DWORD bitlen
|
||||
int bitLen = ToInt32LE (blob, offset+12); |
||||
|
||||
// DWORD public exponent
|
||||
RSAParameters rsap = new RSAParameters (); |
||||
rsap.Exponent = new byte [3]; |
||||
rsap.Exponent [0] = blob [offset+18]; |
||||
rsap.Exponent [1] = blob [offset+17]; |
||||
rsap.Exponent [2] = blob [offset+16]; |
||||
|
||||
int pos = offset+20; |
||||
// BYTE modulus[rsapubkey.bitlen/8];
|
||||
int byteLen = (bitLen >> 3); |
||||
rsap.Modulus = new byte [byteLen]; |
||||
Buffer.BlockCopy (blob, pos, rsap.Modulus, 0, byteLen); |
||||
Array.Reverse (rsap.Modulus); |
||||
#if NET_2_1
|
||||
RSA rsa = RSA.Create (); |
||||
rsa.ImportParameters (rsap); |
||||
#else
|
||||
RSA rsa = null; |
||||
try { |
||||
rsa = RSA.Create (); |
||||
rsa.ImportParameters (rsap); |
||||
} |
||||
catch (CryptographicException) { |
||||
// this may cause problem when this code is run under
|
||||
// the SYSTEM identity on Windows (e.g. ASP.NET). See
|
||||
// http://bugzilla.ximian.com/show_bug.cgi?id=77559
|
||||
CspParameters csp = new CspParameters (); |
||||
csp.Flags = CspProviderFlags.UseMachineKeyStore; |
||||
rsa = new RSACryptoServiceProvider (csp); |
||||
rsa.ImportParameters (rsap); |
||||
} |
||||
#endif
|
||||
return rsa; |
||||
} |
||||
catch (Exception e) { |
||||
throw new CryptographicException ("Invalid blob.", e); |
||||
} |
||||
} |
||||
|
||||
static public DSA FromCapiPublicKeyBlobDSA (byte[] blob) |
||||
{ |
||||
return FromCapiPublicKeyBlobDSA (blob, 0); |
||||
} |
||||
|
||||
static public DSA FromCapiPublicKeyBlobDSA (byte[] blob, int offset) |
||||
{ |
||||
if (blob == null) |
||||
throw new ArgumentNullException ("blob"); |
||||
if (offset >= blob.Length) |
||||
throw new ArgumentException ("blob is too small."); |
||||
|
||||
try { |
||||
if ((blob [offset] != 0x06) || // PUBLICKEYBLOB (0x06)
|
||||
(blob [offset + 1] != 0x02) || // Version (0x02)
|
||||
(blob [offset + 2] != 0x00) || // Reserved (word)
|
||||
(blob [offset + 3] != 0x00) || |
||||
(ToUInt32LE (blob, offset + 8) != 0x31535344)) // DWORD magic
|
||||
throw new CryptographicException ("Invalid blob header"); |
||||
|
||||
int bitlen = ToInt32LE (blob, offset + 12); |
||||
DSAParameters dsap = new DSAParameters (); |
||||
int bytelen = bitlen >> 3; |
||||
int pos = offset + 16; |
||||
|
||||
dsap.P = new byte [bytelen]; |
||||
Buffer.BlockCopy (blob, pos, dsap.P, 0, bytelen); |
||||
Array.Reverse (dsap.P); |
||||
pos += bytelen; |
||||
|
||||
dsap.Q = new byte [20]; |
||||
Buffer.BlockCopy (blob, pos, dsap.Q, 0, 20); |
||||
Array.Reverse (dsap.Q); |
||||
pos += 20; |
||||
|
||||
dsap.G = new byte [bytelen]; |
||||
Buffer.BlockCopy (blob, pos, dsap.G, 0, bytelen); |
||||
Array.Reverse (dsap.G); |
||||
pos += bytelen; |
||||
|
||||
dsap.Y = new byte [bytelen]; |
||||
Buffer.BlockCopy (blob, pos, dsap.Y, 0, bytelen); |
||||
Array.Reverse (dsap.Y); |
||||
pos += bytelen; |
||||
|
||||
dsap.Counter = ToInt32LE (blob, pos); |
||||
pos += 4; |
||||
|
||||
dsap.Seed = new byte [20]; |
||||
Buffer.BlockCopy (blob, pos, dsap.Seed, 0, 20); |
||||
Array.Reverse (dsap.Seed); |
||||
pos += 20; |
||||
|
||||
DSA dsa = (DSA)DSA.Create (); |
||||
dsa.ImportParameters (dsap); |
||||
return dsa; |
||||
} |
||||
catch (Exception e) { |
||||
throw new CryptographicException ("Invalid blob.", e); |
||||
} |
||||
} |
||||
|
||||
static public byte[] ToCapiPublicKeyBlob (RSA rsa) |
||||
{ |
||||
RSAParameters p = rsa.ExportParameters (false); |
||||
int keyLength = p.Modulus.Length; // in bytes
|
||||
byte[] blob = new byte [20 + keyLength]; |
||||
|
||||
blob [0] = 0x06; // Type - PUBLICKEYBLOB (0x06)
|
||||
blob [1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02)
|
||||
// [2], [3] // RESERVED - Always 0
|
||||
blob [5] = 0x24; // ALGID - Always 00 24 00 00 (for CALG_RSA_SIGN)
|
||||
blob [8] = 0x52; // Magic - RSA1 (ASCII in hex)
|
||||
blob [9] = 0x53; |
||||
blob [10] = 0x41; |
||||
blob [11] = 0x31; |
||||
|
||||
byte[] bitlen = GetBytesLE (keyLength << 3); |
||||
blob [12] = bitlen [0]; // bitlen
|
||||
blob [13] = bitlen [1]; |
||||
blob [14] = bitlen [2]; |
||||
blob [15] = bitlen [3]; |
||||
|
||||
// public exponent (DWORD)
|
||||
int pos = 16; |
||||
int n = p.Exponent.Length; |
||||
while (n > 0) |
||||
blob [pos++] = p.Exponent [--n]; |
||||
// modulus
|
||||
pos = 20; |
||||
byte[] part = p.Modulus; |
||||
int len = part.Length; |
||||
Array.Reverse (part, 0, len); |
||||
Buffer.BlockCopy (part, 0, blob, pos, len); |
||||
pos += len; |
||||
return blob; |
||||
} |
||||
|
||||
static public byte[] ToCapiPublicKeyBlob (DSA dsa) |
||||
{ |
||||
DSAParameters p = dsa.ExportParameters (false); |
||||
int keyLength = p.P.Length; // in bytes
|
||||
|
||||
// header + P + Q + G + Y + count + seed
|
||||
byte[] blob = new byte [16 + keyLength + 20 + keyLength + keyLength + 4 + 20]; |
||||
|
||||
blob [0] = 0x06; // Type - PUBLICKEYBLOB (0x06)
|
||||
blob [1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02)
|
||||
// [2], [3] // RESERVED - Always 0
|
||||
blob [5] = 0x22; // ALGID
|
||||
blob [8] = 0x44; // Magic
|
||||
blob [9] = 0x53; |
||||
blob [10] = 0x53; |
||||
blob [11] = 0x31; |
||||
|
||||
byte[] bitlen = GetBytesLE (keyLength << 3); |
||||
blob [12] = bitlen [0]; |
||||
blob [13] = bitlen [1]; |
||||
blob [14] = bitlen [2]; |
||||
blob [15] = bitlen [3]; |
||||
|
||||
int pos = 16; |
||||
byte[] part; |
||||
|
||||
part = p.P; |
||||
Array.Reverse (part); |
||||
Buffer.BlockCopy (part, 0, blob, pos, keyLength); |
||||
pos += keyLength; |
||||
|
||||
part = p.Q; |
||||
Array.Reverse (part); |
||||
Buffer.BlockCopy (part, 0, blob, pos, 20); |
||||
pos += 20; |
||||
|
||||
part = p.G; |
||||
Array.Reverse (part); |
||||
Buffer.BlockCopy (part, 0, blob, pos, keyLength); |
||||
pos += keyLength; |
||||
|
||||
part = p.Y; |
||||
Array.Reverse (part); |
||||
Buffer.BlockCopy (part, 0, blob, pos, keyLength); |
||||
pos += keyLength; |
||||
|
||||
Buffer.BlockCopy (GetBytesLE (p.Counter), 0, blob, pos, 4); |
||||
pos += 4; |
||||
|
||||
part = p.Seed; |
||||
Array.Reverse (part); |
||||
Buffer.BlockCopy (part, 0, blob, pos, 20); |
||||
|
||||
return blob; |
||||
} |
||||
|
||||
// PRIVATEKEYBLOB
|
||||
// PUBLICKEYBLOB
|
||||
static public RSA FromCapiKeyBlob (byte[] blob) |
||||
{ |
||||
return FromCapiKeyBlob (blob, 0); |
||||
} |
||||
|
||||
static public RSA FromCapiKeyBlob (byte[] blob, int offset) |
||||
{ |
||||
if (blob == null) |
||||
throw new ArgumentNullException ("blob"); |
||||
if (offset >= blob.Length) |
||||
throw new ArgumentException ("blob is too small."); |
||||
|
||||
switch (blob [offset]) { |
||||
case 0x00: |
||||
// this could be a public key inside an header
|
||||
// like "sn -e" would produce
|
||||
if (blob [offset + 12] == 0x06) { |
||||
return FromCapiPublicKeyBlob (blob, offset + 12); |
||||
} |
||||
break; |
||||
case 0x06: |
||||
return FromCapiPublicKeyBlob (blob, offset); |
||||
case 0x07: |
||||
return FromCapiPrivateKeyBlob (blob, offset); |
||||
} |
||||
throw new CryptographicException ("Unknown blob format."); |
||||
} |
||||
|
||||
static public DSA FromCapiKeyBlobDSA (byte[] blob) |
||||
{ |
||||
return FromCapiKeyBlobDSA (blob, 0); |
||||
} |
||||
|
||||
static public DSA FromCapiKeyBlobDSA (byte[] blob, int offset) |
||||
{ |
||||
if (blob == null) |
||||
throw new ArgumentNullException ("blob"); |
||||
if (offset >= blob.Length) |
||||
throw new ArgumentException ("blob is too small."); |
||||
|
||||
switch (blob [offset]) { |
||||
case 0x06: |
||||
return FromCapiPublicKeyBlobDSA (blob, offset); |
||||
case 0x07: |
||||
return FromCapiPrivateKeyBlobDSA (blob, offset); |
||||
} |
||||
throw new CryptographicException ("Unknown blob format."); |
||||
} |
||||
|
||||
static public byte[] ToCapiKeyBlob (AsymmetricAlgorithm keypair, bool includePrivateKey) |
||||
{ |
||||
if (keypair == null) |
||||
throw new ArgumentNullException ("keypair"); |
||||
|
||||
// check between RSA and DSA (and potentially others like DH)
|
||||
if (keypair is RSA) |
||||
return ToCapiKeyBlob ((RSA)keypair, includePrivateKey); |
||||
else if (keypair is DSA) |
||||
return ToCapiKeyBlob ((DSA)keypair, includePrivateKey); |
||||
else |
||||
return null; // TODO
|
||||
} |
||||
|
||||
static public byte[] ToCapiKeyBlob (RSA rsa, bool includePrivateKey) |
||||
{ |
||||
if (rsa == null) |
||||
throw new ArgumentNullException ("rsa"); |
||||
|
||||
if (includePrivateKey) |
||||
return ToCapiPrivateKeyBlob (rsa); |
||||
else |
||||
return ToCapiPublicKeyBlob (rsa); |
||||
} |
||||
|
||||
static public byte[] ToCapiKeyBlob (DSA dsa, bool includePrivateKey) |
||||
{ |
||||
if (dsa == null) |
||||
throw new ArgumentNullException ("dsa"); |
||||
|
||||
if (includePrivateKey) |
||||
return ToCapiPrivateKeyBlob (dsa); |
||||
else |
||||
return ToCapiPublicKeyBlob (dsa); |
||||
} |
||||
|
||||
static public string ToHex (byte[] input) |
||||
{ |
||||
if (input == null) |
||||
return null; |
||||
|
||||
StringBuilder sb = new StringBuilder (input.Length * 2); |
||||
foreach (byte b in input) { |
||||
sb.Append (b.ToString ("X2", CultureInfo.InvariantCulture)); |
||||
} |
||||
return sb.ToString (); |
||||
} |
||||
|
||||
static private byte FromHexChar (char c) |
||||
{ |
||||
if ((c >= 'a') && (c <= 'f')) |
||||
return (byte) (c - 'a' + 10); |
||||
if ((c >= 'A') && (c <= 'F')) |
||||
return (byte) (c - 'A' + 10); |
||||
if ((c >= '0') && (c <= '9')) |
||||
return (byte) (c - '0'); |
||||
throw new ArgumentException ("invalid hex char"); |
||||
} |
||||
|
||||
static public byte[] FromHex (string hex) |
||||
{ |
||||
if (hex == null) |
||||
return null; |
||||
if ((hex.Length & 0x1) == 0x1) |
||||
throw new ArgumentException ("Length must be a multiple of 2"); |
||||
|
||||
byte[] result = new byte [hex.Length >> 1]; |
||||
int n = 0; |
||||
int i = 0; |
||||
while (n < result.Length) { |
||||
result [n] = (byte) (FromHexChar (hex [i++]) << 4); |
||||
result [n++] += FromHexChar (hex [i++]); |
||||
} |
||||
return result; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,729 @@
@@ -0,0 +1,729 @@
|
||||
//
|
||||
// Mono.CSharp.Debugger/MonoSymbolFile.cs
|
||||
//
|
||||
// Author:
|
||||
// Martin Baulig (martin@ximian.com)
|
||||
//
|
||||
// (C) 2003 Ximian, Inc. http://www.ximian.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.Reflection; |
||||
using SRE = System.Reflection.Emit; |
||||
using System.Collections.Generic; |
||||
using System.Text; |
||||
using System.Threading; |
||||
using System.IO; |
||||
|
||||
namespace Mono.CompilerServices.SymbolWriter |
||||
{ |
||||
public class MonoSymbolFileException : Exception |
||||
{ |
||||
public MonoSymbolFileException () |
||||
: base () |
||||
{ } |
||||
|
||||
public MonoSymbolFileException (string message, params object[] args) |
||||
: base (String.Format (message, args)) |
||||
{ } |
||||
} |
||||
|
||||
internal class MyBinaryWriter : BinaryWriter |
||||
{ |
||||
public MyBinaryWriter (Stream stream) |
||||
: base (stream) |
||||
{ } |
||||
|
||||
public void WriteLeb128 (int value) |
||||
{ |
||||
base.Write7BitEncodedInt (value); |
||||
} |
||||
} |
||||
|
||||
internal class MyBinaryReader : BinaryReader |
||||
{ |
||||
public MyBinaryReader (Stream stream) |
||||
: base (stream) |
||||
{ } |
||||
|
||||
public int ReadLeb128 () |
||||
{ |
||||
return base.Read7BitEncodedInt (); |
||||
} |
||||
|
||||
public string ReadString (int offset) |
||||
{ |
||||
long old_pos = BaseStream.Position; |
||||
BaseStream.Position = offset; |
||||
|
||||
string text = ReadString (); |
||||
|
||||
BaseStream.Position = old_pos; |
||||
return text; |
||||
} |
||||
} |
||||
|
||||
public interface ISourceFile |
||||
{ |
||||
SourceFileEntry Entry { |
||||
get; |
||||
} |
||||
} |
||||
|
||||
public interface ICompileUnit |
||||
{ |
||||
CompileUnitEntry Entry { |
||||
get; |
||||
} |
||||
} |
||||
|
||||
public interface IMethodDef |
||||
{ |
||||
string Name { |
||||
get; |
||||
} |
||||
|
||||
int Token { |
||||
get; |
||||
} |
||||
} |
||||
|
||||
#if !CECIL
|
||||
internal class MonoDebuggerSupport |
||||
{ |
||||
static GetMethodTokenFunc get_method_token; |
||||
static GetGuidFunc get_guid; |
||||
static GetLocalIndexFunc get_local_index; |
||||
|
||||
delegate int GetMethodTokenFunc (MethodBase method); |
||||
delegate Guid GetGuidFunc (Module module); |
||||
delegate int GetLocalIndexFunc (SRE.LocalBuilder local); |
||||
|
||||
static Delegate create_delegate (Type type, Type delegate_type, string name) |
||||
{ |
||||
MethodInfo mi = type.GetMethod (name, BindingFlags.Static | |
||||
BindingFlags.NonPublic); |
||||
if (mi == null) |
||||
throw new Exception ("Can't find " + name); |
||||
|
||||
return Delegate.CreateDelegate (delegate_type, mi); |
||||
} |
||||
|
||||
static MonoDebuggerSupport () |
||||
{ |
||||
get_method_token = (GetMethodTokenFunc) create_delegate ( |
||||
typeof (Assembly), typeof (GetMethodTokenFunc), |
||||
"MonoDebugger_GetMethodToken"); |
||||
|
||||
get_guid = (GetGuidFunc) create_delegate ( |
||||
typeof (Module), typeof (GetGuidFunc), "Mono_GetGuid"); |
||||
|
||||
get_local_index = (GetLocalIndexFunc) create_delegate ( |
||||
typeof (SRE.LocalBuilder), typeof (GetLocalIndexFunc), |
||||
"Mono_GetLocalIndex"); |
||||
} |
||||
|
||||
public static int GetMethodToken (MethodBase method) |
||||
{ |
||||
return get_method_token (method); |
||||
} |
||||
|
||||
public static Guid GetGuid (Module module) |
||||
{ |
||||
return get_guid (module); |
||||
} |
||||
|
||||
public static int GetLocalIndex (SRE.LocalBuilder local) |
||||
{ |
||||
return get_local_index (local); |
||||
} |
||||
} |
||||
#endif
|
||||
|
||||
public class MonoSymbolFile : IDisposable |
||||
{ |
||||
List<MethodEntry> methods = new List<MethodEntry> (); |
||||
List<SourceFileEntry> sources = new List<SourceFileEntry> (); |
||||
List<CompileUnitEntry> comp_units = new List<CompileUnitEntry> (); |
||||
Dictionary<Type, int> type_hash = new Dictionary<Type, int> (); |
||||
Dictionary<int, AnonymousScopeEntry> anonymous_scopes; |
||||
|
||||
OffsetTable ot; |
||||
int last_type_index; |
||||
int last_method_index; |
||||
int last_namespace_index; |
||||
|
||||
public readonly string FileName = "<dynamic>"; |
||||
public readonly int MajorVersion = OffsetTable.MajorVersion; |
||||
public readonly int MinorVersion = OffsetTable.MinorVersion; |
||||
|
||||
public int NumLineNumbers; |
||||
|
||||
internal MonoSymbolFile () |
||||
{ |
||||
ot = new OffsetTable (); |
||||
} |
||||
|
||||
internal int AddSource (SourceFileEntry source) |
||||
{ |
||||
sources.Add (source); |
||||
return sources.Count; |
||||
} |
||||
|
||||
internal int AddCompileUnit (CompileUnitEntry entry) |
||||
{ |
||||
comp_units.Add (entry); |
||||
return comp_units.Count; |
||||
} |
||||
|
||||
internal int DefineType (Type type) |
||||
{ |
||||
int index; |
||||
if (type_hash.TryGetValue (type, out index)) |
||||
return index; |
||||
|
||||
index = ++last_type_index; |
||||
type_hash.Add (type, index); |
||||
return index; |
||||
} |
||||
|
||||
internal void AddMethod (MethodEntry entry) |
||||
{ |
||||
methods.Add (entry); |
||||
} |
||||
|
||||
public MethodEntry DefineMethod (CompileUnitEntry comp_unit, int token, |
||||
ScopeVariable[] scope_vars, LocalVariableEntry[] locals, |
||||
LineNumberEntry[] lines, CodeBlockEntry[] code_blocks, |
||||
string real_name, MethodEntry.Flags flags, |
||||
int namespace_id) |
||||
{ |
||||
if (reader != null) |
||||
throw new InvalidOperationException (); |
||||
|
||||
MethodEntry method = new MethodEntry ( |
||||
this, comp_unit, token, scope_vars, locals, lines, code_blocks, |
||||
real_name, flags, namespace_id); |
||||
AddMethod (method); |
||||
return method; |
||||
} |
||||
|
||||
internal void DefineAnonymousScope (int id) |
||||
{ |
||||
if (reader != null) |
||||
throw new InvalidOperationException (); |
||||
|
||||
if (anonymous_scopes == null) |
||||
anonymous_scopes = new Dictionary<int, AnonymousScopeEntry> (); |
||||
|
||||
anonymous_scopes.Add (id, new AnonymousScopeEntry (id)); |
||||
} |
||||
|
||||
internal void DefineCapturedVariable (int scope_id, string name, string captured_name, |
||||
CapturedVariable.CapturedKind kind) |
||||
{ |
||||
if (reader != null) |
||||
throw new InvalidOperationException (); |
||||
|
||||
AnonymousScopeEntry scope = anonymous_scopes [scope_id]; |
||||
scope.AddCapturedVariable (name, captured_name, kind); |
||||
} |
||||
|
||||
internal void DefineCapturedScope (int scope_id, int id, string captured_name) |
||||
{ |
||||
if (reader != null) |
||||
throw new InvalidOperationException (); |
||||
|
||||
AnonymousScopeEntry scope = anonymous_scopes [scope_id]; |
||||
scope.AddCapturedScope (id, captured_name); |
||||
} |
||||
|
||||
internal int GetNextTypeIndex () |
||||
{ |
||||
return ++last_type_index; |
||||
} |
||||
|
||||
internal int GetNextMethodIndex () |
||||
{ |
||||
return ++last_method_index; |
||||
} |
||||
|
||||
internal int GetNextNamespaceIndex () |
||||
{ |
||||
return ++last_namespace_index; |
||||
} |
||||
|
||||
void Write (MyBinaryWriter bw, Guid guid) |
||||
{ |
||||
// Magic number and file version.
|
||||
bw.Write (OffsetTable.Magic); |
||||
bw.Write (MajorVersion); |
||||
bw.Write (MinorVersion); |
||||
|
||||
bw.Write (guid.ToByteArray ()); |
||||
|
||||
//
|
||||
// Offsets of file sections; we must write this after we're done
|
||||
// writing the whole file, so we just reserve the space for it here.
|
||||
//
|
||||
long offset_table_offset = bw.BaseStream.Position; |
||||
ot.Write (bw, MajorVersion, MinorVersion); |
||||
|
||||
//
|
||||
// Sort the methods according to their tokens and update their index.
|
||||
//
|
||||
methods.Sort (); |
||||
for (int i = 0; i < methods.Count; i++) |
||||
((MethodEntry) methods [i]).Index = i + 1; |
||||
|
||||
//
|
||||
// Write data sections.
|
||||
//
|
||||
ot.DataSectionOffset = (int) bw.BaseStream.Position; |
||||
foreach (SourceFileEntry source in sources) |
||||
source.WriteData (bw); |
||||
foreach (CompileUnitEntry comp_unit in comp_units) |
||||
comp_unit.WriteData (bw); |
||||
foreach (MethodEntry method in methods) |
||||
method.WriteData (this, bw); |
||||
ot.DataSectionSize = (int) bw.BaseStream.Position - ot.DataSectionOffset; |
||||
|
||||
//
|
||||
// Write the method index table.
|
||||
//
|
||||
ot.MethodTableOffset = (int) bw.BaseStream.Position; |
||||
for (int i = 0; i < methods.Count; i++) { |
||||
MethodEntry entry = (MethodEntry) methods [i]; |
||||
entry.Write (bw); |
||||
} |
||||
ot.MethodTableSize = (int) bw.BaseStream.Position - ot.MethodTableOffset; |
||||
|
||||
//
|
||||
// Write source table.
|
||||
//
|
||||
ot.SourceTableOffset = (int) bw.BaseStream.Position; |
||||
for (int i = 0; i < sources.Count; i++) { |
||||
SourceFileEntry source = (SourceFileEntry) sources [i]; |
||||
source.Write (bw); |
||||
} |
||||
ot.SourceTableSize = (int) bw.BaseStream.Position - ot.SourceTableOffset; |
||||
|
||||
//
|
||||
// Write compilation unit table.
|
||||
//
|
||||
ot.CompileUnitTableOffset = (int) bw.BaseStream.Position; |
||||
for (int i = 0; i < comp_units.Count; i++) { |
||||
CompileUnitEntry unit = (CompileUnitEntry) comp_units [i]; |
||||
unit.Write (bw); |
||||
} |
||||
ot.CompileUnitTableSize = (int) bw.BaseStream.Position - ot.CompileUnitTableOffset; |
||||
|
||||
//
|
||||
// Write anonymous scope table.
|
||||
//
|
||||
ot.AnonymousScopeCount = anonymous_scopes != null ? anonymous_scopes.Count : 0; |
||||
ot.AnonymousScopeTableOffset = (int) bw.BaseStream.Position; |
||||
if (anonymous_scopes != null) { |
||||
foreach (AnonymousScopeEntry scope in anonymous_scopes.Values) |
||||
scope.Write (bw); |
||||
} |
||||
ot.AnonymousScopeTableSize = (int) bw.BaseStream.Position - ot.AnonymousScopeTableOffset; |
||||
|
||||
//
|
||||
// Fixup offset table.
|
||||
//
|
||||
ot.TypeCount = last_type_index; |
||||
ot.MethodCount = methods.Count; |
||||
ot.SourceCount = sources.Count; |
||||
ot.CompileUnitCount = comp_units.Count; |
||||
|
||||
//
|
||||
// Write offset table.
|
||||
//
|
||||
ot.TotalFileSize = (int) bw.BaseStream.Position; |
||||
bw.Seek ((int) offset_table_offset, SeekOrigin.Begin); |
||||
ot.Write (bw, MajorVersion, MinorVersion); |
||||
bw.Seek (0, SeekOrigin.End); |
||||
|
||||
#if false
|
||||
Console.WriteLine ("TOTAL: {0} line numbes, {1} bytes, extended {2} bytes, " + |
||||
"{3} methods.", NumLineNumbers, LineNumberSize, |
||||
ExtendedLineNumberSize, methods.Count); |
||||
#endif
|
||||
} |
||||
|
||||
public void CreateSymbolFile (Guid guid, FileStream fs) |
||||
{ |
||||
if (reader != null) |
||||
throw new InvalidOperationException (); |
||||
|
||||
Write (new MyBinaryWriter (fs), guid); |
||||
} |
||||
|
||||
MyBinaryReader reader; |
||||
Dictionary<int, SourceFileEntry> source_file_hash; |
||||
Dictionary<int, CompileUnitEntry> compile_unit_hash; |
||||
|
||||
List<MethodEntry> method_list; |
||||
Dictionary<int, MethodEntry> method_token_hash; |
||||
Dictionary<string, int> source_name_hash; |
||||
|
||||
Guid guid; |
||||
|
||||
MonoSymbolFile (string filename) |
||||
{ |
||||
this.FileName = filename; |
||||
FileStream stream = new FileStream (filename, FileMode.Open, FileAccess.Read); |
||||
reader = new MyBinaryReader (stream); |
||||
|
||||
try { |
||||
long magic = reader.ReadInt64 (); |
||||
int major_version = reader.ReadInt32 (); |
||||
int minor_version = reader.ReadInt32 (); |
||||
|
||||
if (magic != OffsetTable.Magic) |
||||
throw new MonoSymbolFileException ( |
||||
"Symbol file `{0}' is not a valid " + |
||||
"Mono symbol file", filename); |
||||
if (major_version != OffsetTable.MajorVersion) |
||||
throw new MonoSymbolFileException ( |
||||
"Symbol file `{0}' has version {1}, " + |
||||
"but expected {2}", filename, major_version, |
||||
OffsetTable.MajorVersion); |
||||
if (minor_version != OffsetTable.MinorVersion) |
||||
throw new MonoSymbolFileException ( |
||||
"Symbol file `{0}' has version {1}.{2}, " + |
||||
"but expected {3}.{4}", filename, major_version, |
||||
minor_version, OffsetTable.MajorVersion, |
||||
OffsetTable.MinorVersion); |
||||
|
||||
MajorVersion = major_version; |
||||
MinorVersion = minor_version; |
||||
guid = new Guid (reader.ReadBytes (16)); |
||||
|
||||
ot = new OffsetTable (reader, major_version, minor_version); |
||||
} catch { |
||||
throw new MonoSymbolFileException ( |
||||
"Cannot read symbol file `{0}'", filename); |
||||
} |
||||
|
||||
source_file_hash = new Dictionary<int, SourceFileEntry> (); |
||||
compile_unit_hash = new Dictionary<int, CompileUnitEntry> (); |
||||
} |
||||
|
||||
void CheckGuidMatch (Guid other, string filename, string assembly) |
||||
{ |
||||
if (other == guid) |
||||
return; |
||||
|
||||
throw new MonoSymbolFileException ( |
||||
"Symbol file `{0}' does not match assembly `{1}'", |
||||
filename, assembly); |
||||
} |
||||
|
||||
#if CECIL
|
||||
protected MonoSymbolFile (string filename, Mono.Cecil.AssemblyDefinition assembly) : this (filename) |
||||
{ |
||||
Guid mvid = assembly.MainModule.Mvid; |
||||
|
||||
CheckGuidMatch (mvid, filename, assembly.MainModule.Image.FileInformation.FullName); |
||||
} |
||||
|
||||
public static MonoSymbolFile ReadSymbolFile (Mono.Cecil.AssemblyDefinition assembly, string filename) |
||||
{ |
||||
string name = filename + ".mdb"; |
||||
|
||||
return new MonoSymbolFile (name, assembly); |
||||
} |
||||
#else
|
||||
protected MonoSymbolFile (string filename, Assembly assembly) : this (filename) |
||||
{ |
||||
// Check that the MDB file matches the assembly, if we have been
|
||||
// passed an assembly.
|
||||
if (assembly == null) |
||||
return; |
||||
|
||||
Module[] modules = assembly.GetModules (); |
||||
Guid assembly_guid = MonoDebuggerSupport.GetGuid (modules [0]); |
||||
|
||||
CheckGuidMatch (assembly_guid, filename, assembly.Location); |
||||
} |
||||
|
||||
public static MonoSymbolFile ReadSymbolFile (Assembly assembly) |
||||
{ |
||||
string filename = assembly.Location; |
||||
string name = filename + ".mdb"; |
||||
|
||||
return new MonoSymbolFile (name, assembly); |
||||
} |
||||
#endif
|
||||
|
||||
public static MonoSymbolFile ReadSymbolFile (string mdbFilename) |
||||
{ |
||||
return new MonoSymbolFile (mdbFilename, null); |
||||
} |
||||
|
||||
public int CompileUnitCount { |
||||
get { return ot.CompileUnitCount; } |
||||
} |
||||
|
||||
public int SourceCount { |
||||
get { return ot.SourceCount; } |
||||
} |
||||
|
||||
public int MethodCount { |
||||
get { return ot.MethodCount; } |
||||
} |
||||
|
||||
public int TypeCount { |
||||
get { return ot.TypeCount; } |
||||
} |
||||
|
||||
public int AnonymousScopeCount { |
||||
get { return ot.AnonymousScopeCount; } |
||||
} |
||||
|
||||
public int NamespaceCount { |
||||
get { return last_namespace_index; } |
||||
} |
||||
|
||||
public Guid Guid { |
||||
get { return guid; } |
||||
} |
||||
|
||||
public OffsetTable OffsetTable { |
||||
get { return ot; } |
||||
} |
||||
|
||||
internal int LineNumberCount = 0; |
||||
internal int LocalCount = 0; |
||||
internal int StringSize = 0; |
||||
|
||||
internal int LineNumberSize = 0; |
||||
internal int ExtendedLineNumberSize = 0; |
||||
|
||||
public SourceFileEntry GetSourceFile (int index) |
||||
{ |
||||
if ((index < 1) || (index > ot.SourceCount)) |
||||
throw new ArgumentException (); |
||||
if (reader == null) |
||||
throw new InvalidOperationException (); |
||||
|
||||
lock (this) { |
||||
SourceFileEntry source; |
||||
if (source_file_hash.TryGetValue (index, out source)) |
||||
return source; |
||||
|
||||
long old_pos = reader.BaseStream.Position; |
||||
|
||||
reader.BaseStream.Position = ot.SourceTableOffset + |
||||
SourceFileEntry.Size * (index - 1); |
||||
source = new SourceFileEntry (this, reader); |
||||
source_file_hash.Add (index, source); |
||||
|
||||
reader.BaseStream.Position = old_pos; |
||||
return source; |
||||
} |
||||
} |
||||
|
||||
public SourceFileEntry[] Sources { |
||||
get { |
||||
if (reader == null) |
||||
throw new InvalidOperationException (); |
||||
|
||||
SourceFileEntry[] retval = new SourceFileEntry [SourceCount]; |
||||
for (int i = 0; i < SourceCount; i++) |
||||
retval [i] = GetSourceFile (i + 1); |
||||
return retval; |
||||
} |
||||
} |
||||
|
||||
public CompileUnitEntry GetCompileUnit (int index) |
||||
{ |
||||
if ((index < 1) || (index > ot.CompileUnitCount)) |
||||
throw new ArgumentException (); |
||||
if (reader == null) |
||||
throw new InvalidOperationException (); |
||||
|
||||
lock (this) { |
||||
CompileUnitEntry unit; |
||||
if (compile_unit_hash.TryGetValue (index, out unit)) |
||||
return unit; |
||||
|
||||
long old_pos = reader.BaseStream.Position; |
||||
|
||||
reader.BaseStream.Position = ot.CompileUnitTableOffset + |
||||
CompileUnitEntry.Size * (index - 1); |
||||
unit = new CompileUnitEntry (this, reader); |
||||
compile_unit_hash.Add (index, unit); |
||||
|
||||
reader.BaseStream.Position = old_pos; |
||||
return unit; |
||||
} |
||||
} |
||||
|
||||
public CompileUnitEntry[] CompileUnits { |
||||
get { |
||||
if (reader == null) |
||||
throw new InvalidOperationException (); |
||||
|
||||
CompileUnitEntry[] retval = new CompileUnitEntry [CompileUnitCount]; |
||||
for (int i = 0; i < CompileUnitCount; i++) |
||||
retval [i] = GetCompileUnit (i + 1); |
||||
return retval; |
||||
} |
||||
} |
||||
|
||||
void read_methods () |
||||
{ |
||||
lock (this) { |
||||
if (method_token_hash != null) |
||||
return; |
||||
|
||||
method_token_hash = new Dictionary<int, MethodEntry> (); |
||||
method_list = new List<MethodEntry> (); |
||||
|
||||
long old_pos = reader.BaseStream.Position; |
||||
reader.BaseStream.Position = ot.MethodTableOffset; |
||||
|
||||
for (int i = 0; i < MethodCount; i++) { |
||||
MethodEntry entry = new MethodEntry (this, reader, i + 1); |
||||
method_token_hash.Add (entry.Token, entry); |
||||
method_list.Add (entry); |
||||
} |
||||
|
||||
reader.BaseStream.Position = old_pos; |
||||
} |
||||
} |
||||
|
||||
public MethodEntry GetMethodByToken (int token) |
||||
{ |
||||
if (reader == null) |
||||
throw new InvalidOperationException (); |
||||
|
||||
lock (this) { |
||||
read_methods (); |
||||
MethodEntry me; |
||||
method_token_hash.TryGetValue (token, out me); |
||||
return me; |
||||
} |
||||
} |
||||
|
||||
public MethodEntry GetMethod (int index) |
||||
{ |
||||
if ((index < 1) || (index > ot.MethodCount)) |
||||
throw new ArgumentException (); |
||||
if (reader == null) |
||||
throw new InvalidOperationException (); |
||||
|
||||
lock (this) { |
||||
read_methods (); |
||||
return (MethodEntry) method_list [index - 1]; |
||||
} |
||||
} |
||||
|
||||
public MethodEntry[] Methods { |
||||
get { |
||||
if (reader == null) |
||||
throw new InvalidOperationException (); |
||||
|
||||
lock (this) { |
||||
read_methods (); |
||||
MethodEntry[] retval = new MethodEntry [MethodCount]; |
||||
method_list.CopyTo (retval, 0); |
||||
return retval; |
||||
} |
||||
} |
||||
} |
||||
|
||||
public int FindSource (string file_name) |
||||
{ |
||||
if (reader == null) |
||||
throw new InvalidOperationException (); |
||||
|
||||
lock (this) { |
||||
if (source_name_hash == null) { |
||||
source_name_hash = new Dictionary<string, int> (); |
||||
|
||||
for (int i = 0; i < ot.SourceCount; i++) { |
||||
SourceFileEntry source = GetSourceFile (i + 1); |
||||
source_name_hash.Add (source.FileName, i); |
||||
} |
||||
} |
||||
|
||||
int value; |
||||
if (!source_name_hash.TryGetValue (file_name, out value)) |
||||
return -1; |
||||
return value; |
||||
} |
||||
} |
||||
|
||||
public AnonymousScopeEntry GetAnonymousScope (int id) |
||||
{ |
||||
if (reader == null) |
||||
throw new InvalidOperationException (); |
||||
|
||||
AnonymousScopeEntry scope; |
||||
lock (this) { |
||||
if (anonymous_scopes != null) { |
||||
anonymous_scopes.TryGetValue (id, out scope); |
||||
return scope; |
||||
} |
||||
|
||||
anonymous_scopes = new Dictionary<int, AnonymousScopeEntry> (); |
||||
reader.BaseStream.Position = ot.AnonymousScopeTableOffset; |
||||
for (int i = 0; i < ot.AnonymousScopeCount; i++) { |
||||
scope = new AnonymousScopeEntry (reader); |
||||
anonymous_scopes.Add (scope.ID, scope); |
||||
} |
||||
|
||||
return anonymous_scopes [id]; |
||||
} |
||||
} |
||||
|
||||
internal MyBinaryReader BinaryReader { |
||||
get { |
||||
if (reader == null) |
||||
throw new InvalidOperationException (); |
||||
|
||||
return reader; |
||||
} |
||||
} |
||||
|
||||
public void Dispose () |
||||
{ |
||||
Dispose (true); |
||||
} |
||||
|
||||
protected virtual void Dispose (bool disposing) |
||||
{ |
||||
if (disposing) { |
||||
if (reader != null) { |
||||
reader.Close (); |
||||
reader = null; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,403 @@
@@ -0,0 +1,403 @@
|
||||
//
|
||||
// Mono.CSharp.Debugger/MonoSymbolWriter.cs
|
||||
//
|
||||
// Author:
|
||||
// Martin Baulig (martin@ximian.com)
|
||||
//
|
||||
// This is the default implementation of the System.Diagnostics.SymbolStore.ISymbolWriter
|
||||
// interface.
|
||||
//
|
||||
// (C) 2002 Ximian, Inc. http://www.ximian.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.Runtime.CompilerServices; |
||||
using System.Collections.Generic; |
||||
using System.IO; |
||||
|
||||
namespace Mono.CompilerServices.SymbolWriter |
||||
{ |
||||
public class MonoSymbolWriter |
||||
{ |
||||
List<SourceMethodBuilder> methods; |
||||
List<SourceFileEntry> sources; |
||||
List<CompileUnitEntry> comp_units; |
||||
protected readonly MonoSymbolFile file; |
||||
string filename; |
||||
|
||||
private SourceMethodBuilder current_method; |
||||
#if NET_2_1
|
||||
System.Collections.Stack current_method_stack = new System.Collections.Stack (); |
||||
#else
|
||||
Stack<SourceMethodBuilder> current_method_stack = new Stack<SourceMethodBuilder> (); |
||||
#endif
|
||||
|
||||
public MonoSymbolWriter (string filename) |
||||
{ |
||||
this.methods = new List<SourceMethodBuilder> (); |
||||
this.sources = new List<SourceFileEntry> (); |
||||
this.comp_units = new List<CompileUnitEntry> (); |
||||
this.file = new MonoSymbolFile (); |
||||
|
||||
this.filename = filename + ".mdb"; |
||||
} |
||||
|
||||
public MonoSymbolFile SymbolFile { |
||||
get { return file; } |
||||
} |
||||
|
||||
public void CloseNamespace () |
||||
{ } |
||||
|
||||
public void DefineLocalVariable (int index, string name) |
||||
{ |
||||
if (current_method == null) |
||||
return; |
||||
|
||||
current_method.AddLocal (index, name); |
||||
} |
||||
|
||||
public void DefineCapturedLocal (int scope_id, string name, string captured_name) |
||||
{ |
||||
file.DefineCapturedVariable (scope_id, name, captured_name, |
||||
CapturedVariable.CapturedKind.Local); |
||||
} |
||||
|
||||
public void DefineCapturedParameter (int scope_id, string name, string captured_name) |
||||
{ |
||||
file.DefineCapturedVariable (scope_id, name, captured_name, |
||||
CapturedVariable.CapturedKind.Parameter); |
||||
} |
||||
|
||||
public void DefineCapturedThis (int scope_id, string captured_name) |
||||
{ |
||||
file.DefineCapturedVariable (scope_id, "this", captured_name, |
||||
CapturedVariable.CapturedKind.This); |
||||
} |
||||
|
||||
public void DefineCapturedScope (int scope_id, int id, string captured_name) |
||||
{ |
||||
file.DefineCapturedScope (scope_id, id, captured_name); |
||||
} |
||||
|
||||
public void DefineScopeVariable (int scope, int index) |
||||
{ |
||||
if (current_method == null) |
||||
return; |
||||
|
||||
current_method.AddScopeVariable (scope, index); |
||||
} |
||||
|
||||
public void MarkSequencePoint (int offset, SourceFileEntry file, int line, int column, |
||||
bool is_hidden) |
||||
{ |
||||
if (current_method == null) |
||||
return; |
||||
|
||||
current_method.MarkSequencePoint (offset, file, line, column, is_hidden); |
||||
} |
||||
|
||||
public SourceMethodBuilder OpenMethod (ICompileUnit file, int ns_id, IMethodDef method) |
||||
{ |
||||
SourceMethodBuilder builder = new SourceMethodBuilder (file, ns_id, method); |
||||
current_method_stack.Push (current_method); |
||||
current_method = builder; |
||||
methods.Add (current_method); |
||||
return builder; |
||||
} |
||||
|
||||
public void CloseMethod () |
||||
{ |
||||
current_method = (SourceMethodBuilder) current_method_stack.Pop (); |
||||
} |
||||
|
||||
public SourceFileEntry DefineDocument (string url) |
||||
{ |
||||
SourceFileEntry entry = new SourceFileEntry (file, url); |
||||
sources.Add (entry); |
||||
return entry; |
||||
} |
||||
|
||||
public SourceFileEntry DefineDocument (string url, byte[] guid, byte[] checksum) |
||||
{ |
||||
SourceFileEntry entry = new SourceFileEntry (file, url, guid, checksum); |
||||
sources.Add (entry); |
||||
return entry; |
||||
} |
||||
|
||||
public CompileUnitEntry DefineCompilationUnit (SourceFileEntry source) |
||||
{ |
||||
CompileUnitEntry entry = new CompileUnitEntry (file, source); |
||||
comp_units.Add (entry); |
||||
return entry; |
||||
} |
||||
|
||||
public int DefineNamespace (string name, CompileUnitEntry unit, |
||||
string[] using_clauses, int parent) |
||||
{ |
||||
if ((unit == null) || (using_clauses == null)) |
||||
throw new NullReferenceException (); |
||||
|
||||
return unit.DefineNamespace (name, using_clauses, parent); |
||||
} |
||||
|
||||
public int OpenScope (int start_offset) |
||||
{ |
||||
if (current_method == null) |
||||
return 0; |
||||
|
||||
current_method.StartBlock (CodeBlockEntry.Type.Lexical, start_offset); |
||||
return 0; |
||||
} |
||||
|
||||
public void CloseScope (int end_offset) |
||||
{ |
||||
if (current_method == null) |
||||
return; |
||||
|
||||
current_method.EndBlock (end_offset); |
||||
} |
||||
|
||||
public void OpenCompilerGeneratedBlock (int start_offset) |
||||
{ |
||||
if (current_method == null) |
||||
return; |
||||
|
||||
current_method.StartBlock (CodeBlockEntry.Type.CompilerGenerated, |
||||
start_offset); |
||||
} |
||||
|
||||
public void CloseCompilerGeneratedBlock (int end_offset) |
||||
{ |
||||
if (current_method == null) |
||||
return; |
||||
|
||||
current_method.EndBlock (end_offset); |
||||
} |
||||
|
||||
public void StartIteratorBody (int start_offset) |
||||
{ |
||||
current_method.StartBlock (CodeBlockEntry.Type.IteratorBody, |
||||
start_offset); |
||||
} |
||||
|
||||
public void EndIteratorBody (int end_offset) |
||||
{ |
||||
current_method.EndBlock (end_offset); |
||||
} |
||||
|
||||
public void StartIteratorDispatcher (int start_offset) |
||||
{ |
||||
current_method.StartBlock (CodeBlockEntry.Type.IteratorDispatcher, |
||||
start_offset); |
||||
} |
||||
|
||||
public void EndIteratorDispatcher (int end_offset) |
||||
{ |
||||
current_method.EndBlock (end_offset); |
||||
} |
||||
|
||||
public void DefineAnonymousScope (int id) |
||||
{ |
||||
file.DefineAnonymousScope (id); |
||||
} |
||||
|
||||
public void WriteSymbolFile (Guid guid) |
||||
{ |
||||
foreach (SourceMethodBuilder method in methods) |
||||
method.DefineMethod (file); |
||||
|
||||
try { |
||||
// We mmap the file, so unlink the previous version since it may be in use
|
||||
File.Delete (filename); |
||||
} catch { |
||||
// We can safely ignore
|
||||
} |
||||
using (FileStream fs = new FileStream (filename, FileMode.Create, FileAccess.Write)) { |
||||
file.CreateSymbolFile (guid, fs); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public class SourceMethodBuilder |
||||
{ |
||||
List<LocalVariableEntry> _locals; |
||||
List<CodeBlockEntry> _blocks; |
||||
List<ScopeVariable> _scope_vars; |
||||
#if NET_2_1
|
||||
System.Collections.Stack _block_stack; |
||||
#else
|
||||
Stack<CodeBlockEntry> _block_stack; |
||||
#endif
|
||||
string _real_name; |
||||
IMethodDef _method; |
||||
ICompileUnit _comp_unit; |
||||
// MethodEntry.Flags _method_flags;
|
||||
int _ns_id; |
||||
|
||||
public SourceMethodBuilder (ICompileUnit comp_unit, int ns_id, IMethodDef method) |
||||
{ |
||||
this._comp_unit = comp_unit; |
||||
this._method = method; |
||||
this._ns_id = ns_id; |
||||
|
||||
method_lines = new LineNumberEntry [32]; |
||||
} |
||||
|
||||
private LineNumberEntry [] method_lines; |
||||
private int method_lines_pos = 0; |
||||
|
||||
public void MarkSequencePoint (int offset, SourceFileEntry file, int line, int column, |
||||
bool is_hidden) |
||||
{ |
||||
if (method_lines_pos == method_lines.Length) { |
||||
LineNumberEntry [] tmp = method_lines; |
||||
method_lines = new LineNumberEntry [method_lines.Length * 2]; |
||||
Array.Copy (tmp, method_lines, method_lines_pos); |
||||
} |
||||
|
||||
int file_idx = file != null ? file.Index : 0; |
||||
method_lines [method_lines_pos++] = new LineNumberEntry ( |
||||
file_idx, line, offset, is_hidden); |
||||
} |
||||
|
||||
public void StartBlock (CodeBlockEntry.Type type, int start_offset) |
||||
{ |
||||
if (_block_stack == null) { |
||||
#if NET_2_1
|
||||
_block_stack = new System.Collections.Stack (); |
||||
#else
|
||||
_block_stack = new Stack<CodeBlockEntry> (); |
||||
#endif
|
||||
} |
||||
|
||||
if (_blocks == null) |
||||
_blocks = new List<CodeBlockEntry> (); |
||||
|
||||
int parent = CurrentBlock != null ? CurrentBlock.Index : -1; |
||||
|
||||
CodeBlockEntry block = new CodeBlockEntry ( |
||||
_blocks.Count + 1, parent, type, start_offset); |
||||
|
||||
_block_stack.Push (block); |
||||
_blocks.Add (block); |
||||
} |
||||
|
||||
public void EndBlock (int end_offset) |
||||
{ |
||||
CodeBlockEntry block = (CodeBlockEntry) _block_stack.Pop (); |
||||
block.Close (end_offset); |
||||
} |
||||
|
||||
public CodeBlockEntry[] Blocks { |
||||
get { |
||||
if (_blocks == null) |
||||
return new CodeBlockEntry [0]; |
||||
|
||||
CodeBlockEntry[] retval = new CodeBlockEntry [_blocks.Count]; |
||||
_blocks.CopyTo (retval, 0); |
||||
return retval; |
||||
} |
||||
} |
||||
|
||||
public CodeBlockEntry CurrentBlock { |
||||
get { |
||||
if ((_block_stack != null) && (_block_stack.Count > 0)) |
||||
return (CodeBlockEntry) _block_stack.Peek (); |
||||
else |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
public LocalVariableEntry[] Locals { |
||||
get { |
||||
if (_locals == null) |
||||
return new LocalVariableEntry [0]; |
||||
else { |
||||
LocalVariableEntry[] retval = |
||||
new LocalVariableEntry [_locals.Count]; |
||||
_locals.CopyTo (retval, 0); |
||||
return retval; |
||||
} |
||||
} |
||||
} |
||||
|
||||
public void AddLocal (int index, string name) |
||||
{ |
||||
if (_locals == null) |
||||
_locals = new List<LocalVariableEntry> (); |
||||
int block_idx = CurrentBlock != null ? CurrentBlock.Index : 0; |
||||
_locals.Add (new LocalVariableEntry (index, name, block_idx)); |
||||
} |
||||
|
||||
public ScopeVariable[] ScopeVariables { |
||||
get { |
||||
if (_scope_vars == null) |
||||
return new ScopeVariable [0]; |
||||
|
||||
ScopeVariable[] retval = new ScopeVariable [_scope_vars.Count]; |
||||
_scope_vars.CopyTo (retval); |
||||
return retval; |
||||
} |
||||
} |
||||
|
||||
public void AddScopeVariable (int scope, int index) |
||||
{ |
||||
if (_scope_vars == null) |
||||
_scope_vars = new List<ScopeVariable> (); |
||||
_scope_vars.Add ( |
||||
new ScopeVariable (scope, index)); |
||||
} |
||||
|
||||
public string RealMethodName { |
||||
get { return _real_name; } |
||||
} |
||||
|
||||
public void SetRealMethodName (string name) |
||||
{ |
||||
_real_name = name; |
||||
} |
||||
|
||||
public ICompileUnit SourceFile { |
||||
get { return _comp_unit; } |
||||
} |
||||
|
||||
public IMethodDef Method { |
||||
get { return _method; } |
||||
} |
||||
|
||||
public void DefineMethod (MonoSymbolFile file) |
||||
{ |
||||
LineNumberEntry[] lines = new LineNumberEntry [method_lines_pos]; |
||||
Array.Copy (method_lines, lines, method_lines_pos); |
||||
|
||||
MethodEntry entry = new MethodEntry ( |
||||
file, _comp_unit.Entry, _method.Token, ScopeVariables, |
||||
Locals, lines, Blocks, RealMethodName, 0, //_method_flags,
|
||||
_ns_id); |
||||
|
||||
file.AddMethod (entry); |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue