// Author:
// Miguel de Icaza (miguel@ximian.com)
//
-// (C) 2001 Ximian, Inc.
+// (C) 2001, 2002, 2003 Ximian, Inc.
+// (C) 2004 Novell, Inc.
//
-
+//#define PRODUCTION
using System;
using System.IO;
using System.Collections;
+using System.Collections.Specialized;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
+using System.Security;
using System.Security.Cryptography;
+using System.Security.Permissions;
using Mono.Security.Cryptography;
//
// Initializes the symbol writer
//
- static void InitializeSymbolWriter ()
+ static void InitializeSymbolWriter (string filename)
{
- SymbolWriter = SymbolWriter.GetSymbolWriter (Module.Builder);
+ SymbolWriter = SymbolWriter.GetSymbolWriter (Module.Builder, filename);
//
// If we got an ISymbolWriter instance, initialize it.
//
if (SymbolWriter == null) {
Report.Warning (
- -18, "Could not find the symbol writer assembly (Mono.CSharp.Debugger.dll). This is normally an installation problem. Please make sure to compile and install the mcs/class/Mono.CSharp.Debugger directory.");
+ -18, "Could not find the symbol writer assembly (Mono.CompilerServices.SymbolWriter.dll). This is normally an installation problem. Please make sure to compile and install the mcs/class/Mono.CompilerServices.SymbolWriter directory.");
return;
}
}
// load the default symbol writer.
//
Module.Builder = Assembly.Builder.DefineDynamicModule (
- Basename (name), Basename (output), want_debugging_support);
+ Basename (name), Basename (output), false);
if (want_debugging_support)
- InitializeSymbolWriter ();
+ InitializeSymbolWriter (output);
}
static public void Save (string name)
catch (System.IO.IOException io) {
Report.Error (16, "Could not write to file `"+name+"', cause: " + io.Message);
}
+
+ if (SymbolWriter != null)
+ SymbolWriter.WriteSymbolFile ();
}
}
// variable mapping.
//
public class VariableStorage {
- ILGenerator ig;
FieldBuilder fb;
LocalBuilder local;
fb = ec.CurrentIterator.MapVariable ("s_", count.ToString (), t);
else
local = ec.ig.DeclareLocal (t);
- ig = ec.ig;
}
- public void EmitThis ()
+ public void EmitThis (ILGenerator ig)
{
if (fb != null)
ig.Emit (OpCodes.Ldarg_0);
}
- public void EmitStore ()
+ public void EmitStore (ILGenerator ig)
{
if (fb == null)
ig.Emit (OpCodes.Stloc, local);
ig.Emit (OpCodes.Stfld, fb);
}
- public void EmitLoad ()
+ public void EmitLoad (ILGenerator ig)
{
if (fb == null)
ig.Emit (OpCodes.Ldloc, local);
else
ig.Emit (OpCodes.Ldfld, fb);
}
+
+ public void EmitLoadAddress (ILGenerator ig)
+ {
+ if (fb == null)
+ ig.Emit (OpCodes.Ldloca, local);
+ else
+ ig.Emit (OpCodes.Ldflda, fb);
+ }
- public void EmitCall (MethodInfo mi)
+ public void EmitCall (ILGenerator ig, MethodInfo mi)
{
// FIXME : we should handle a call like tostring
// here, where boxing is needed. However, we will
// never encounter that with the current usage.
bool value_type_call;
- EmitThis ();
+ EmitThis (ig);
if (fb == null) {
value_type_call = local.LocalType.IsValueType;
/// <summary>
/// Whether we are inside an anonymous method.
/// </summary>
- public bool InAnonymousMethod;
+ public AnonymousMethod CurrentAnonymousMethod;
/// <summary>
/// Location for this EmitContext
/// </summary>
public bool InEnumContext;
+ /// <summary>
+ /// Anonymous methods can capture local variables and fields,
+ /// this object tracks it. It is copied from the TopLevelBlock
+ /// field.
+ /// </summary>
+ public CaptureContext capture_context;
+
+ /// <summary>
+ /// Trace when method is called and is obsolete then this member suppress message
+ /// when call is inside next [Obsolete] method or type.
+ /// </summary>
+ public bool TestObsoleteMethodUsage = true;
+
+ /// <summary>
+ /// The current iterator
+ /// </summary>
public Iterator CurrentIterator;
+ /// <summary>
+ /// Whether we are in the resolving stage or not
+ /// </summary>
+ enum Phase {
+ Created,
+ Resolving,
+ Emitting
+ }
+
+ Phase current_phase;
+
FlowBranching current_flow_branching;
public EmitContext (DeclSpace parent, DeclSpace ds, Location l, ILGenerator ig,
DeclSpace = ds;
CheckState = RootContext.Checked;
ConstantCheckState = true;
+
+ if ((return_type is TypeBuilder) && return_type.IsGenericTypeDefinition)
+ throw new Exception ("FUCK");
IsStatic = (code_flags & Modifiers.STATIC) != 0;
InIterator = (code_flags & Modifiers.METHOD_YIELDS) != 0;
IsConstructor = is_constructor;
CurrentBlock = null;
CurrentFile = 0;
+ current_phase = Phase.Created;
if (parent != null){
// Can only be null for the ResolveType contexts.
}
}
+ public bool HaveCaptureInfo {
+ get {
+ return capture_context != null;
+ }
+ }
+
// <summary>
// Starts a new code branching. This inherits the state of all local
// variables and parameters from the current branching.
current_flow_branching = current_flow_branching.Parent;
}
- public void EmitTopBlock (Block block, InternalParameters ip, Location loc)
+ public void CaptureVariable (LocalInfo li)
+ {
+ capture_context.AddLocal (CurrentAnonymousMethod, li);
+ li.IsCaptured = true;
+ }
+
+ public void CaptureParameter (string name, Type t, int idx)
+ {
+
+ capture_context.AddParameter (this, CurrentAnonymousMethod, name, t, idx);
+ }
+
+ //
+ // Use to register a field as captured
+ //
+ public void CaptureField (FieldExpr fe)
+ {
+ capture_context.AddField (fe);
+ }
+
+ //
+ // Whether anonymous methods have captured variables
+ //
+ public bool HaveCapturedVariables ()
+ {
+ if (capture_context != null)
+ return capture_context.HaveCapturedVariables;
+ return false;
+ }
+
+ //
+ // Whether anonymous methods have captured fields or this.
+ //
+ public bool HaveCapturedFields ()
+ {
+ if (capture_context != null)
+ return capture_context.HaveCapturedFields;
+ return false;
+ }
+
+ //
+ // Emits the instance pointer for the host method
+ //
+ public void EmitMethodHostInstance (EmitContext target, AnonymousMethod am)
+ {
+ if (capture_context != null)
+ capture_context.EmitMethodHostInstance (target, am);
+ else if (IsStatic)
+ target.ig.Emit (OpCodes.Ldnull);
+ else
+ target.ig.Emit (OpCodes.Ldarg_0);
+ }
+
+ //
+ // Returns whether the `local' variable has been captured by an anonymous
+ // method
+ //
+ public bool IsCaptured (LocalInfo local)
+ {
+ return capture_context.IsCaptured (local);
+ }
+
+ public bool IsParameterCaptured (string name)
+ {
+ if (capture_context != null)
+ return capture_context.IsParameterCaptured (name);
+ return false;
+ }
+
+ public void EmitMeta (ToplevelBlock b, InternalParameters ip)
{
- bool unreachable = false;
+ if (capture_context != null)
+ capture_context.EmitHelperClasses (this);
+ b.EmitMeta (this);
+
+ if (HasReturnLabel)
+ ReturnLabel = ig.DefineLabel ();
+ }
+
+ //
+ // Here until we can fix the problem with Mono.CSharp.Switch, which
+ // currently can not cope with ig == null during resolve (which must
+ // be fixed for switch statements to work on anonymous methods).
+ //
+ public void EmitTopBlock (ToplevelBlock block, InternalParameters ip, Location loc)
+ {
+ if (block == null)
+ return;
+
+ bool unreachable;
+
+ if (ResolveTopBlock (null, block, ip, loc, out unreachable)){
+ EmitMeta (block, ip);
+
+ current_phase = Phase.Emitting;
+ EmitResolvedTopBlock (block, unreachable);
+ }
+ }
+
+ public bool ResolveTopBlock (EmitContext anonymous_method_host, ToplevelBlock block,
+ InternalParameters ip, Location loc, out bool unreachable)
+ {
+ current_phase = Phase.Resolving;
+
+ unreachable = false;
+
+ capture_context = block.CaptureContext;
if (!Location.IsNull (loc))
CurrentFile = loc.File;
- if (block != null){
+#if PRODUCTION
try {
+#endif
int errors = Report.Errors;
- block.EmitMeta (this, ip);
+ block.ResolveMeta (block, this, ip);
+
if (Report.Errors == errors){
bool old_do_flow_analysis = DoFlowAnalysis;
DoFlowAnalysis = true;
+ if (anonymous_method_host != null)
+ current_flow_branching = FlowBranching.CreateBranching (
+ anonymous_method_host.CurrentBranching, FlowBranching.BranchingType.Block,
+ block, loc);
+ else
current_flow_branching = FlowBranching.CreateBranching (
null, FlowBranching.BranchingType.Block, block, loc);
if (!block.Resolve (this)) {
current_flow_branching = null;
DoFlowAnalysis = old_do_flow_analysis;
- return;
+ return false;
}
FlowBranching.Reachability reachability = current_flow_branching.MergeTopBlock ();
DoFlowAnalysis = old_do_flow_analysis;
- block.Emit (this);
-
if (reachability.AlwaysReturns ||
reachability.AlwaysThrows ||
reachability.IsUnreachable)
unreachable = true;
}
-#if FIXME
+#if PRODUCTION
} catch (Exception e) {
Console.WriteLine ("Exception caught by the compiler while compiling:");
Console.WriteLine (" Block that caused the problem begin at: " + loc);
CurrentBlock.StartLocation, CurrentBlock.EndLocation);
}
Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
- Console.WriteLine (Report.FriendlyStackTrace (e));
-
- Environment.Exit (1);
-#else
- } finally {
-#endif
- }
+ throw;
}
+#endif
if (ReturnType != null && !unreachable){
if (!InIterator){
+ if (CurrentAnonymousMethod != null){
+ Report.Error (1643, loc, "Not all code paths return a value in anonymous method of type `{0}'",
+ CurrentAnonymousMethod.Type);
+ } else {
Report.Error (161, loc, "Not all code paths return a value");
- return;
+ }
+
+ return false;
}
}
+ block.CompleteContexts ();
+
+ return true;
+ }
+
+ public void EmitResolvedTopBlock (ToplevelBlock block, bool unreachable)
+ {
+ if (block != null)
+ block.Emit (this);
if (HasReturnLabel)
ig.MarkLabel (ReturnLabel);
+
if (return_value != null){
ig.Emit (OpCodes.Ldloc, return_value);
ig.Emit (OpCodes.Ret);
ig.Emit (OpCodes.Ret);
}
}
+
+ //
+ // Close pending helper classes if we are the toplevel
+ //
+ if (capture_context != null && capture_context.ParentToplevel == null)
+ capture_context.CloseHelperClasses ();
}
/// <summary>
if (check_file && (CurrentFile != loc.File))
return;
- ig.MarkSequencePoint (null, loc.Row, 0, 0, 0);
+ CodeGen.SymbolWriter.MarkSequencePoint (ig, loc.Row, 0);
+ }
+
+ public void DefineLocalVariable (string name, LocalBuilder builder)
+ {
+ if (CodeGen.SymbolWriter == null)
+ return;
+
+ CodeGen.SymbolWriter.DefineLocalVariable (name, builder);
}
/// <summary>
/// return value from the function. By default this is not
/// used. This is only required when returns are found inside
/// Try or Catch statements.
+ ///
+ /// This method is typically invoked from the Emit phase, so
+ /// we allow the creation of a return label if it was not
+ /// requested during the resolution phase. Could be cleaned
+ /// up, but it would replicate a lot of logic in the Emit phase
+ /// of the code that uses it.
/// </summary>
public LocalBuilder TemporaryReturn ()
{
if (return_value == null){
return_value = ig.DeclareLocal (ReturnType);
+ if (!HasReturnLabel){
ReturnLabel = ig.DefineLabel ();
HasReturnLabel = true;
}
+ }
return return_value;
}
+ /// <summary>
+ /// This method is used during the Resolution phase to flag the
+ /// need to define the ReturnLabel
+ /// </summary>
public void NeedReturnLabel ()
{
- if (!InIterator && !HasReturnLabel) {
- ReturnLabel = ig.DefineLabel ();
+ if (current_phase != Phase.Resolving){
+ //
+ // The reason is that the `ReturnLabel' is declared between
+ // resolution and emission
+ //
+ throw new Exception ("NeedReturnLabel called from Emit phase, should only be called during Resolve");
+ }
+
+ if (!InIterator && !HasReturnLabel)
HasReturnLabel = true;
}
- }
//
// Creates a field `name' with the type `t' on the proxy class
throw new Exception ("MapVariable for an unknown state");
}
- //
- // Invoke this routine to remap a VariableInfo into the
- // proper MemberAccess expression
- //
- public Expression RemapLocal (LocalInfo local_info)
- {
- FieldExpr fe = new FieldExpr (local_info.FieldBuilder, loc);
- fe.InstanceExpression = new ProxyInstance ();
- return fe.DoResolve (this);
- }
-
- public Expression RemapLocalLValue (LocalInfo local_info, Expression right_side)
- {
- FieldExpr fe = new FieldExpr (local_info.FieldBuilder, loc);
- fe.InstanceExpression = new ProxyInstance ();
- return fe.DoResolveLValue (this, right_side);
- }
-
public Expression RemapParameter (int idx)
{
FieldExpr fe = new FieldExprNoAddress (CurrentIterator.parameter_fields [idx].FieldBuilder, loc);
public void EmitThis ()
{
ig.Emit (OpCodes.Ldarg_0);
- if (InIterator && !IsStatic){
+ if (InIterator){
+ if (!IsStatic){
FieldBuilder this_field = CurrentIterator.this_field.FieldBuilder;
if (TypeManager.IsValueType (this_field.FieldType))
ig.Emit (OpCodes.Ldflda, this_field);
else
ig.Emit (OpCodes.Ldfld, this_field);
}
+ } else if (capture_context != null && CurrentAnonymousMethod != null){
+ ScopeInfo si = CurrentAnonymousMethod.Scope;
+ while (si != null){
+ if (si.ParentLink != null)
+ ig.Emit (OpCodes.Ldfld, si.ParentLink);
+ if (si.THIS != null){
+ ig.Emit (OpCodes.Ldfld, si.THIS);
+ break;
+ }
+ si = si.ParentScope;
+ }
+ }
+ }
+
+ //
+ // Emits the code necessary to load the instance required
+ // to access the captured LocalInfo
+ //
+ public void EmitCapturedVariableInstance (LocalInfo li)
+ {
+ if (RemapToProxy){
+ ig.Emit (OpCodes.Ldarg_0);
+ return;
+ }
+
+ if (capture_context == null)
+ throw new Exception ("Calling EmitCapturedContext when there is no capture_context");
+
+ capture_context.EmitCapturedVariableInstance (this, li, CurrentAnonymousMethod);
+ }
+
+ public void EmitParameter (string name)
+ {
+ capture_context.EmitParameter (this, name);
+ }
+
+ public void EmitAssignParameter (string name, Expression source, bool leave_copy, bool prepare_for_load)
+ {
+ capture_context.EmitAssignParameter (this, name, source, leave_copy, prepare_for_load);
+ }
+
+ public void EmitAddressOfParameter (string name)
+ {
+ capture_context.EmitAddressOfParameter (this, name);
}
public Expression GetThis (Location loc)
public abstract class CommonAssemblyModulClass: Attributable {
- static string[] attribute_targets = new string [] { "assembly", "module" };
-
protected CommonAssemblyModulClass ():
base (null)
{
return;
}
OptAttributes.AddAttributes (attrs);
- OptAttributes.CheckTargets (ValidAttributeTargets);
}
public virtual void Emit (TypeContainer tc)
if (OptAttributes == null)
return null;
- EmitContext temp_ec = new EmitContext (new RootTypes (), Mono.CSharp.Location.Null, null, null, 0, false);
+ // Ensure that we only have GlobalAttributes, since the Search below isn't safe with other types.
+ if (!OptAttributes.CheckTargets (this))
+ return null;
+
+ EmitContext temp_ec = new EmitContext (RootContext.Tree.Types, Mono.CSharp.Location.Null, null, null, 0, false);
Attribute a = OptAttributes.Search (TypeManager.cls_compliant_attribute_type, temp_ec);
if (a != null) {
a.Resolve (temp_ec);
}
return a;
}
-
- protected override string[] ValidAttributeTargets {
- get {
- return attribute_targets;
- }
- }
}
public class AssemblyClass: CommonAssemblyModulClass {
public AssemblyBuilder Builder;
bool is_cls_compliant;
+ public Attribute ClsCompliantAttribute;
+
+ ListDictionary declarative_security;
+
+ static string[] attribute_targets = new string [] { "assembly" };
public AssemblyClass (): base ()
{
public void ResolveClsCompliance ()
{
- Attribute a = GetClsCompliantAttribute ();
- if (a == null)
+ ClsCompliantAttribute = GetClsCompliantAttribute ();
+ if (ClsCompliantAttribute == null)
return;
- is_cls_compliant = a.GetClsCompliantAttributeValue (null);
+ is_cls_compliant = ClsCompliantAttribute.GetClsCompliantAttributeValue (null);
}
+ // fix bug #56621
+ private void SetPublicKey (AssemblyName an, byte[] strongNameBlob)
+ {
+ try {
+ // check for possible ECMA key
+ if (strongNameBlob.Length == 16) {
+ // will be rejected if not "the" ECMA key
+ an.SetPublicKey (strongNameBlob);
+ }
+ else {
+ // take it, with or without, a private key
+ RSA rsa = CryptoConvert.FromCapiKeyBlob (strongNameBlob);
+ // and make sure we only feed the public part to Sys.Ref
+ byte[] publickey = CryptoConvert.ToCapiPublicKeyBlob (rsa);
+
+ // AssemblyName.SetPublicKey requires an additional header
+ byte[] publicKeyHeader = new byte [12] { 0x00, 0x24, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00 };
+
+ byte[] encodedPublicKey = new byte [12 + publickey.Length];
+ Buffer.BlockCopy (publicKeyHeader, 0, encodedPublicKey, 0, 12);
+ Buffer.BlockCopy (publickey, 0, encodedPublicKey, 12, publickey.Length);
+ an.SetPublicKey (encodedPublicKey);
+ }
+ }
+ catch (Exception) {
+ Report.Error (1548, "Could not strongname the assembly. File `" +
+ RootContext.StrongNameKeyFile + "' incorrectly encoded.");
+ Environment.Exit (1);
+ }
+ }
+
+ // TODO: rewrite this code (to kill N bugs and make it faster) and use standard ApplyAttribute way.
public AssemblyName GetAssemblyName (string name, string output)
{
if (OptAttributes != null) {
foreach (Attribute a in OptAttributes.Attrs) {
- if (a.Target != "assembly")
- continue;
+ // cannot rely on any resolve-based members before you call Resolve
+ if (a.ExplicitTarget == null || a.ExplicitTarget != "assembly")
+ continue;
+
// TODO: This code is buggy: comparing Attribute name without resolving it is wrong.
// However, this is invoked by CodeGen.Init, at which time none of the namespaces
// are loaded yet.
case "AssemblyKeyFileAttribute":
case "System.Reflection.AssemblyKeyFileAttribute":
if (RootContext.StrongNameKeyFile != null) {
- Report.Warning (1616, "Compiler option -keyfile overrides " +
- "AssemblyKeyFileAttribute");
+ Report.SymbolRelatedToPreviousError (a.Location, a.Name);
+ Report.Warning (1616, "Compiler option '{0}' overrides '{1}' given in source", "keyfile", "System.Reflection.AssemblyKeyFileAttribute");
}
else {
string value = a.GetString ();
case "AssemblyKeyNameAttribute":
case "System.Reflection.AssemblyKeyNameAttribute":
if (RootContext.StrongNameKeyContainer != null) {
- Report.Warning (1616, "Compiler option -keycontainer overrides " +
- "AssemblyKeyNameAttribute");
+ Report.SymbolRelatedToPreviousError (a.Location, a.Name);
+ Report.Warning (1616, "keycontainer", "Compiler option '{0}' overrides '{1}' given in source", "System.Reflection.AssemblyKeyNameAttribute");
}
else {
string value = a.GetString ();
if (RootContext.StrongNameDelaySign) {
// delayed signing - DO NOT include private key
- try {
- // check for possible ECMA key
- if (snkeypair.Length == 16) {
- // will be rejected if not "the" ECMA key
- an.KeyPair = new StrongNameKeyPair (snkeypair);
- }
- else {
- // take it, with or without, a private key
- RSA rsa = CryptoConvert.FromCapiKeyBlob (snkeypair);
- // and make sure we only feed the public part to Sys.Ref
- byte[] publickey = CryptoConvert.ToCapiPublicKeyBlob (rsa);
- an.KeyPair = new StrongNameKeyPair (publickey);
- }
- }
- catch (Exception) {
- Report.Error (1548, "Could not strongname the assembly. File `" +
- RootContext.StrongNameKeyFile + "' incorrectly encoded.");
- Environment.Exit (1);
- }
+ SetPublicKey (an, snkeypair);
}
else {
// no delay so we make sure we have the private key
public override void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder customBuilder)
{
+ if (a.Type.IsSubclassOf (TypeManager.security_attr_type) && a.CheckSecurityActionValidity (true)) {
+ if (declarative_security == null)
+ declarative_security = new ListDictionary ();
+
+ a.ExtractSecurityPermissionSet (declarative_security);
+ return;
+ }
+
Builder.SetCustomAttribute (customBuilder);
}
+
+ public override void Emit (TypeContainer tc)
+ {
+ base.Emit (tc);
+
+ if (declarative_security != null) {
+
+ MethodInfo add_permission = typeof (AssemblyBuilder).GetMethod ("AddPermissionRequests", BindingFlags.Instance | BindingFlags.NonPublic);
+ object builder_instance = Builder;
+
+ try {
+ // Microsoft runtime hacking
+ if (add_permission == null) {
+ Type assembly_builder = typeof (AssemblyBuilder).Assembly.GetType ("System.Reflection.Emit.AssemblyBuilderData");
+ add_permission = assembly_builder.GetMethod ("AddPermissionRequests", BindingFlags.Instance | BindingFlags.NonPublic);
+
+ FieldInfo fi = typeof (AssemblyBuilder).GetField ("m_assemblyData", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField);
+ builder_instance = fi.GetValue (Builder);
+ }
+
+ object[] args = new object [] { declarative_security [SecurityAction.RequestMinimum],
+ declarative_security [SecurityAction.RequestOptional],
+ declarative_security [SecurityAction.RequestRefuse] };
+ add_permission.Invoke (builder_instance, args);
+ }
+ catch {
+ Report.RuntimeMissingSupport (Location.Null, "assembly permission setting");
+ }
+ }
+ }
+
+ public override string[] ValidAttributeTargets {
+ get {
+ return attribute_targets;
+ }
+ }
}
public class ModuleClass: CommonAssemblyModulClass {
public ModuleBuilder Builder;
bool m_module_is_unsafe;
+ static string[] attribute_targets = new string [] { "module" };
+
public ModuleClass (bool is_unsafe)
{
m_module_is_unsafe = is_unsafe;
return;
}
- ApplyAttributeBuilder (null, new CustomAttributeBuilder (TypeManager.unverifiable_code_ctor, new object [0]));
+ Builder.SetCustomAttribute (new CustomAttributeBuilder (TypeManager.unverifiable_code_ctor, new object [0]));
}
public override void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder customBuilder)
{
- if (a != null && a.Type == TypeManager.cls_compliant_attribute_type) {
- Report.Warning_T (3012, a.Location);
- return;
+ if (a.Type == TypeManager.cls_compliant_attribute_type) {
+ if (CodeGen.Assembly.ClsCompliantAttribute == null) {
+ Report.Warning (3012, a.Location, "You must specify the CLSCompliant attribute on the assembly, not the module, to enable CLS compliance checking");
+ }
+ else if (CodeGen.Assembly.IsClsCompliant != a.GetBoolean ()) {
+ Report.SymbolRelatedToPreviousError (CodeGen.Assembly.ClsCompliantAttribute.Location, CodeGen.Assembly.ClsCompliantAttribute.Name);
+ Report.Error (3017, a.Location, "You cannot specify the CLSCompliant attribute on a module that differs from the CLSCompliant attribute on the assembly");
+ return;
+ }
}
Builder.SetCustomAttribute (customBuilder);
}
- }
+ public override string[] ValidAttributeTargets {
+ get {
+ return attribute_targets;
+ }
+ }
+ }
}