コード例 #1
0
ファイル: MethodFinder.cs プロジェクト: Spatterjaaay/SMAPI
 /*********
 ** Public methods
 *********/
 /// <summary>Construct an instance.</summary>
 /// <param name="fullTypeName">The full type name for which to find references.</param>
 /// <param name="methodName">The method name for which to find references.</param>
 /// <param name="result">The result to return for matching instructions.</param>
 public MethodFinder(string fullTypeName, string methodName, InstructionHandleResult result)
     : base(defaultPhrase: $"{fullTypeName}.{methodName} method")
 {
     this.FullTypeName = fullTypeName;
     this.MethodName   = methodName;
     this.Result       = result;
 }
コード例 #2
0
 /*********
 ** Public methods
 *********/
 /// <summary>Construct an instance.</summary>
 /// <param name="fullTypeName">The full type name for which to find references.</param>
 /// <param name="eventName">The event name for which to find references.</param>
 /// <param name="result">The result to return for matching instructions.</param>
 public EventFinder(string fullTypeName, string eventName, InstructionHandleResult result)
     : base(defaultPhrase: $"{fullTypeName}.{eventName} event")
 {
     this.FullTypeName = fullTypeName;
     this.EventName    = eventName;
     this.Result       = result;
 }
コード例 #3
0
ファイル: PropertyFinder.cs プロジェクト: Nauja/SoGModLoader
 /*********
 ** Public methods
 *********/
 /// <summary>Construct an instance.</summary>
 /// <param name="fullTypeName">The full type name for which to find references.</param>
 /// <param name="propertyName">The property name for which to find references.</param>
 /// <param name="result">The result to return for matching instructions.</param>
 public PropertyFinder(string fullTypeName, string propertyName, InstructionHandleResult result)
     : base(defaultPhrase: $"{fullTypeName}.{propertyName} property")
 {
     this.FullTypeName = fullTypeName;
     this.PropertyName = propertyName;
     this.Result       = result;
 }
コード例 #4
0
ファイル: MethodFinder.cs プロジェクト: Karefha/SMAPI
 /*********
 ** Public methods
 *********/
 /// <summary>Construct an instance.</summary>
 /// <param name="fullTypeName">The full type name for which to find references.</param>
 /// <param name="methodName">The method name for which to find references.</param>
 /// <param name="result">The result to return for matching instructions.</param>
 public MethodFinder(string fullTypeName, string methodName, InstructionHandleResult result)
 {
     this.FullTypeName = fullTypeName;
     this.MethodName   = methodName;
     this.Result       = result;
     this.NounPhrase   = $"{fullTypeName}.{methodName} method";
 }
コード例 #5
0
ファイル: TypeFinder.cs プロジェクト: Karefha/SMAPI
 /*********
 ** Public methods
 *********/
 /// <summary>Construct an instance.</summary>
 /// <param name="fullTypeName">The full type name to match.</param>
 /// <param name="result">The result to return for matching instructions.</param>
 /// <param name="shouldIgnore">A lambda which overrides a matched type.</param>
 public TypeFinder(string fullTypeName, InstructionHandleResult result, Func <TypeReference, bool> shouldIgnore = null)
 {
     this.FullTypeName = fullTypeName;
     this.Result       = result;
     this.NounPhrase   = $"{fullTypeName} type";
     this.ShouldIgnore = shouldIgnore ?? (p => false);
 }
コード例 #6
0
 /*********
 ** Public methods
 *********/
 /// <summary>Construct an instance.</summary>
 /// <param name="fullTypeName">The full type name for which to find references.</param>
 /// <param name="fieldName">The field name for which to find references.</param>
 /// <param name="result">The result to return for matching instructions.</param>
 public FieldFinder(string fullTypeName, string fieldName, InstructionHandleResult result)
     : base(defaultPhrase: $"{fullTypeName}.{fieldName} field")
 {
     this.FullTypeName = fullTypeName;
     this.FieldName    = fieldName;
     this.Result       = result;
 }
コード例 #7
0
ファイル: FieldFinder.cs プロジェクト: kurumushi/SMAPI
 /*********
 ** Public methods
 *********/
 /// <summary>Construct an instance.</summary>
 /// <param name="fullTypeName">The full type name for which to find references.</param>
 /// <param name="fieldNames">The field names for which to find references.</param>
 /// <param name="result">The result to return for matching instructions.</param>
 public FieldFinder(string fullTypeName, string[] fieldNames, InstructionHandleResult result)
     : base(defaultPhrase: $"{string.Join(", ", fieldNames.Select(p => $"{fullTypeName}.{p}"))} field{(fieldNames.Length != 1 ? "s" : "")}") // default phrase should never be used
 {
     this.FullTypeName = fullTypeName;
     this.FieldNames   = new HashSet <string>(fieldNames);
     this.Result       = result;
 }
コード例 #8
0
ファイル: FieldFinder.cs プロジェクト: sherlock221/SMAPI
 /*********
 ** Public methods
 *********/
 /// <summary>Construct an instance.</summary>
 /// <param name="fullTypeName">The full type name for which to find references.</param>
 /// <param name="fieldName">The field name for which to find references.</param>
 /// <param name="result">The result to return for matching instructions.</param>
 public FieldFinder(string fullTypeName, string fieldName, InstructionHandleResult result)
 {
     this.FullTypeName = fullTypeName;
     this.FieldName    = fieldName;
     this.Result       = result;
     this.NounPhrase   = $"{fullTypeName}.{fieldName} field";
 }
コード例 #9
0
 /*********
 ** Public methods
 *********/
 /// <summary>Construct an instance.</summary>
 /// <param name="fullTypeName">The full type name for which to find references.</param>
 /// <param name="propertyName">The property name for which to find references.</param>
 /// <param name="result">The result to return for matching instructions.</param>
 public PropertyFinder(string fullTypeName, string propertyName, InstructionHandleResult result)
 {
     this.FullTypeName = fullTypeName;
     this.PropertyName = propertyName;
     this.Result       = result;
     this.NounPhrase   = $"{fullTypeName}.{propertyName} property";
 }
コード例 #10
0
ファイル: EventFinder.cs プロジェクト: Karefha/SMAPI
 /*********
 ** Public methods
 *********/
 /// <summary>Construct an instance.</summary>
 /// <param name="fullTypeName">The full type name for which to find references.</param>
 /// <param name="eventName">The event name for which to find references.</param>
 /// <param name="result">The result to return for matching instructions.</param>
 public EventFinder(string fullTypeName, string eventName, InstructionHandleResult result)
 {
     this.FullTypeName = fullTypeName;
     this.EventName    = eventName;
     this.Result       = result;
     this.NounPhrase   = $"{fullTypeName}.{eventName} event";
 }
コード例 #11
0
 /*********
 ** Public methods
 *********/
 /// <summary>Construct an instance.</summary>
 /// <param name="fullTypeNames">The full type names to match.</param>
 /// <param name="result">The result to return for matching instructions.</param>
 /// <param name="shouldIgnore">Get whether a matched type should be ignored.</param>
 public TypeFinder(string[] fullTypeNames, InstructionHandleResult result, Func <TypeReference, bool> shouldIgnore = null)
     : base(defaultPhrase: $"{string.Join(", ", fullTypeNames)} type{(fullTypeNames.Length != 1 ? "s" : "")}") // default phrase should never be used
 {
     this.FullTypeNames = new HashSet <string>(fullTypeNames);
     this.Result        = result;
     this.ShouldIgnore  = shouldIgnore;
 }
コード例 #12
0
ファイル: TypeFinder.cs プロジェクト: wxwssg/SMAPI
 /*********
 ** Public methods
 *********/
 /// <summary>Construct an instance.</summary>
 /// <param name="fullTypeName">The full type name to match.</param>
 /// <param name="result">The result to return for matching instructions.</param>
 /// <param name="shouldIgnore">Get whether a matched type should be ignored.</param>
 public TypeFinder(string fullTypeName, InstructionHandleResult result, Func <TypeReference, bool> shouldIgnore = null)
     : base(defaultPhrase: $"{fullTypeName} type")
 {
     this.FullTypeName = fullTypeName;
     this.Result       = result;
     this.ShouldIgnore = shouldIgnore;
 }
コード例 #13
0
ファイル: TypeAssemblyFinder.cs プロジェクト: wxwssg/SMAPI
 /*********
 ** Public methods
 *********/
 /// <summary>Construct an instance.</summary>
 /// <param name="assemblyName">The full assembly name to which to find references.</param>
 /// <param name="result">The result to return for matching instructions.</param>
 /// <param name="shouldIgnore">Get whether a matched type should be ignored.</param>
 public TypeAssemblyFinder(string assemblyName, InstructionHandleResult result, Func <TypeReference, bool> shouldIgnore = null)
     : base(defaultPhrase: $"{assemblyName} assembly")
 {
     this.AssemblyName = assemblyName;
     this.Result       = result;
     this.ShouldIgnore = shouldIgnore;
 }
コード例 #14
0
 /// <summary>Raise a result flag.</summary>
 /// <param name="flag">The result flag to set.</param>
 /// <param name="resultMessage">The result message to add.</param>
 /// <returns>Returns true for convenience.</returns>
 protected bool MarkFlag(InstructionHandleResult flag, string resultMessage = null)
 {
     this.Flags.Add(flag);
     if (resultMessage != null)
     {
         this.Phrases.Add(resultMessage);
     }
     return(true);
 }
コード例 #15
0
        /*********
        ** Public methods
        *********/
        /// <summary>Construct an instance.</summary>
        /// <param name="fullTypeName">The full type name for which to find references.</param>
        /// <param name="eventNames">The event names for which to find references.</param>
        /// <param name="result">The result to return for matching instructions.</param>
        public EventFinder(string fullTypeName, string[] eventNames, InstructionHandleResult result)
            : base(defaultPhrase: $"{string.Join(", ", eventNames.Select(p => $"{fullTypeName}.{p}"))} event{(eventNames.Length != 1 ? "s" : "")}") // default phrase should never be used
        {
            this.FullTypeName = fullTypeName;
            this.Result       = result;

            this.MethodNames = new HashSet <string>();
            foreach (string name in eventNames)
            {
                this.MethodNames.Add($"add_{name}");
                this.MethodNames.Add($"remove_{name}");
            }
        }
コード例 #16
0
 /*********
 ** Public methods
 *********/
 /// <summary>Construct an instance.</summary>
 /// <param name="fullTypeName">The full type name to match.</param>
 /// <param name="result">The result to return for matching instructions.</param>
 public TypeFinder(string fullTypeName, InstructionHandleResult result)
 {
     this.FullTypeName = fullTypeName;
     this.Result       = result;
     this.NounPhrase   = $"{fullTypeName} type";
 }
コード例 #17
0
 /// <summary>Construct an instance.</summary>
 /// <param name="fullTypeName">The full type name to match.</param>
 /// <param name="result">The result to return for matching instructions.</param>
 /// <param name="shouldIgnore">Get whether a matched type should be ignored.</param>
 public TypeFinder(string fullTypeName, InstructionHandleResult result, Func <TypeReference, bool> shouldIgnore = null)
     : this(new[] { fullTypeName }, result, shouldIgnore)
 {
 }
コード例 #18
0
 /// <summary>Construct an instance.</summary>
 /// <param name="fullTypeName">The full type name for which to find references.</param>
 /// <param name="eventName">The event name for which to find references.</param>
 /// <param name="result">The result to return for matching instructions.</param>
 public EventFinder(string fullTypeName, string eventName, InstructionHandleResult result)
     : this(fullTypeName, new[] { eventName }, result)
 {
 }
コード例 #19
0
ファイル: AssemblyLoader.cs プロジェクト: gabrc52/SMAPI
        /// <summary>Process the result from an instruction handler.</summary>
        /// <param name="mod">The mod being analysed.</param>
        /// <param name="handler">The instruction handler.</param>
        /// <param name="result">The result returned by the handler.</param>
        /// <param name="loggedMessages">The messages already logged for the current mod.</param>
        /// <param name="assumeCompatible">Assume the mod is compatible, even if incompatible code is detected.</param>
        /// <param name="logPrefix">A string to prefix to log messages.</param>
        /// <param name="filename">The assembly filename for log messages.</param>
        private void ProcessInstructionHandleResult(IModMetadata mod, IInstructionHandler handler, InstructionHandleResult result, HashSet <string> loggedMessages, string logPrefix, bool assumeCompatible, string filename)
        {
            switch (result)
            {
            case InstructionHandleResult.Rewritten:
                this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Rewrote {filename} to fix {handler.NounPhrase}...");
                break;

            case InstructionHandleResult.NotCompatible:
                this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Broken code in {filename}: {handler.NounPhrase}.");
                if (!assumeCompatible)
                {
                    throw new IncompatibleInstructionException(handler.NounPhrase, $"Found an incompatible CIL instruction ({handler.NounPhrase}) while loading assembly {filename}.");
                }
                mod.SetWarning(ModWarning.BrokenCodeLoaded);
                break;

            case InstructionHandleResult.DetectedGamePatch:
                this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Detected game patcher ({handler.NounPhrase}) in assembly {filename}.");
                mod.SetWarning(ModWarning.PatchesGame);
                break;

            case InstructionHandleResult.DetectedSaveSerialiser:
                this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Detected possible save serialiser change ({handler.NounPhrase}) in assembly {filename}.");
                mod.SetWarning(ModWarning.ChangesSaveSerialiser);
                break;

            case InstructionHandleResult.DetectedUnvalidatedUpdateTick:
                this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Detected reference to {handler.NounPhrase} in assembly {filename}.");
                mod.SetWarning(ModWarning.UsesUnvalidatedUpdateTick);
                break;

            case InstructionHandleResult.DetectedDynamic:
                this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Detected 'dynamic' keyword ({handler.NounPhrase}) in assembly {filename}.");
                mod.SetWarning(ModWarning.UsesDynamic);
                break;

            case InstructionHandleResult.DetectedFilesystemAccess:
                this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Detected filesystem access ({handler.NounPhrase}) in assembly {filename}.");
                mod.SetWarning(ModWarning.AccessesFilesystem);
                break;

            case InstructionHandleResult.DetectedShellAccess:
                this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Detected shell or process access ({handler.NounPhrase}) in assembly {filename}.");
                mod.SetWarning(ModWarning.AccessesShell);
                break;

            case InstructionHandleResult.None:
                break;

            default:
                throw new NotSupportedException($"Unrecognised instruction handler result '{result}'.");
            }
        }
コード例 #20
0
ファイル: AssemblyLoader.cs プロジェクト: gabrc52/SMAPI
        /****
        ** Assembly rewriting
        ****/
        /// <summary>Rewrite the types referenced by an assembly.</summary>
        /// <param name="mod">The mod for which the assembly is being loaded.</param>
        /// <param name="assembly">The assembly to rewrite.</param>
        /// <param name="assumeCompatible">Assume the mod is compatible, even if incompatible code is detected.</param>
        /// <param name="loggedMessages">The messages that have already been logged for this mod.</param>
        /// <param name="logPrefix">A string to prefix to log messages.</param>
        /// <returns>Returns whether the assembly was modified.</returns>
        /// <exception cref="IncompatibleInstructionException">An incompatible CIL instruction was found while rewriting the assembly.</exception>
        private bool RewriteAssembly(IModMetadata mod, AssemblyDefinition assembly, bool assumeCompatible, HashSet <string> loggedMessages, string logPrefix)
        {
            ModuleDefinition module   = assembly.MainModule;
            string           filename = $"{assembly.Name.Name}.dll";

            // swap assembly references if needed (e.g. XNA => MonoGame)
            bool platformChanged = false;

            for (int i = 0; i < module.AssemblyReferences.Count; i++)
            {
                // remove old assembly reference
                if (this.AssemblyMap.RemoveNames.Any(name => module.AssemblyReferences[i].Name == name))
                {
                    this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Rewriting {filename} for OS...");
                    platformChanged = true;
                    module.AssemblyReferences.RemoveAt(i);
                    i--;
                }
            }
            if (platformChanged)
            {
                // add target assembly references
                foreach (AssemblyNameReference target in this.AssemblyMap.TargetReferences.Values)
                {
                    module.AssemblyReferences.Add(target);
                }

                // rewrite type scopes to use target assemblies
                IEnumerable <TypeReference> typeReferences = module.GetTypeReferences().OrderBy(p => p.FullName);
                foreach (TypeReference type in typeReferences)
                {
                    this.ChangeTypeScope(type);
                }
            }

            // find (and optionally rewrite) incompatible instructions
            bool anyRewritten = false;

            IInstructionHandler[] handlers = new InstructionMetadata().GetHandlers(this.ParanoidMode).ToArray();
            foreach (MethodDefinition method in this.GetMethods(module))
            {
                // check method definition
                foreach (IInstructionHandler handler in handlers)
                {
                    InstructionHandleResult result = handler.Handle(module, method, this.AssemblyMap, platformChanged);
                    this.ProcessInstructionHandleResult(mod, handler, result, loggedMessages, logPrefix, assumeCompatible, filename);
                    if (result == InstructionHandleResult.Rewritten)
                    {
                        anyRewritten = true;
                    }
                }

                // check CIL instructions
                ILProcessor cil          = method.Body.GetILProcessor();
                var         instructions = cil.Body.Instructions;
                // ReSharper disable once ForCanBeConvertedToForeach -- deliberate access by index so each handler sees replacements from previous handlers
                for (int offset = 0; offset < instructions.Count; offset++)
                {
                    foreach (IInstructionHandler handler in handlers)
                    {
                        Instruction             instruction = instructions[offset];
                        InstructionHandleResult result      = handler.Handle(module, cil, instruction, this.AssemblyMap, platformChanged);
                        this.ProcessInstructionHandleResult(mod, handler, result, loggedMessages, logPrefix, assumeCompatible, filename);
                        if (result == InstructionHandleResult.Rewritten)
                        {
                            anyRewritten = true;
                        }
                    }
                }
            }

            return(platformChanged || anyRewritten);
        }
コード例 #21
0
        /// <summary>Process the result from an instruction handler.</summary>
        /// <param name="mod">The mod being analysed.</param>
        /// <param name="handler">The instruction handler.</param>
        /// <param name="result">The result returned by the handler.</param>
        /// <param name="loggedMessages">The messages already logged for the current mod.</param>
        /// <param name="assumeCompatible">Assume the mod is compatible, even if incompatible code is detected.</param>
        /// <param name="logPrefix">A string to prefix to log messages.</param>
        /// <param name="filename">The assembly filename for log messages.</param>
        private void ProcessInstructionHandleResult(IModMetadata mod, IInstructionHandler handler, InstructionHandleResult result, HashSet <string> loggedMessages, string logPrefix, bool assumeCompatible, string filename)
        {
            switch (result)
            {
            case InstructionHandleResult.Rewritten:
                this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Rewrote {filename} to fix {handler.NounPhrase}...");
                break;

            case InstructionHandleResult.NotCompatible:
                if (!assumeCompatible)
                {
                    throw new IncompatibleInstructionException(handler.NounPhrase, $"Found an incompatible CIL instruction ({handler.NounPhrase}) while loading assembly {filename}.");
                }
                this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Found an incompatible CIL instruction ({handler.NounPhrase}) while loading assembly {filename}, but SMAPI is configured to allow it anyway. The mod may crash or behave unexpectedly.", LogLevel.Warn);
                break;

            case InstructionHandleResult.DetectedGamePatch:
                this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Detected game patcher ({handler.NounPhrase}) in assembly {filename}.");
                this.Monitor.LogOnce(loggedMessages, $"{mod.DisplayName} patches the game, which may impact game stability. If you encounter problems, try removing this mod first.", LogLevel.Warn);
                break;

            case InstructionHandleResult.DetectedSaveSerialiser:
                this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Detected possible save serialiser change ({handler.NounPhrase}) in assembly {filename}.");
                this.Monitor.LogOnce(loggedMessages, $"{mod.DisplayName} seems to change the save serialiser. It may change your saves in such a way that they won't work without this mod in the future.", LogLevel.Warn);
                break;

            case InstructionHandleResult.DetectedDynamic:
                this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Detected 'dynamic' keyword ({handler.NounPhrase}) in assembly {filename}.");
                this.Monitor.LogOnce(loggedMessages, $"{mod.DisplayName} uses the 'dynamic' keyword, which isn't compatible with Stardew Valley on Linux or Mac.",
#if SMAPI_FOR_WINDOWS
                                     this.IsDeveloperMode ? LogLevel.Warn : LogLevel.Debug
#else
                                     LogLevel.Warn
#endif
                                     );
                break;

            case InstructionHandleResult.None:
                break;

            default:
                throw new NotSupportedException($"Unrecognised instruction handler result '{result}'.");
            }
        }
コード例 #22
0
        /// <summary>Process the result from an instruction handler.</summary>
        /// <param name="mod">The mod being analyzed.</param>
        /// <param name="handler">The instruction handler.</param>
        /// <param name="result">The result returned by the handler.</param>
        /// <param name="loggedMessages">The messages already logged for the current mod.</param>
        /// <param name="logPrefix">A string to prefix to log messages.</param>
        /// <param name="filename">The assembly filename for log messages.</param>
        private void ProcessInstructionHandleResult(IModMetadata mod, IInstructionHandler handler, InstructionHandleResult result, HashSet <string> loggedMessages, string logPrefix, string filename)
        {
            // get message template
            // ($phrase is replaced with the noun phrase or messages)
            string template = null;

            switch (result)
            {
            case InstructionHandleResult.Rewritten:
                template = $"{logPrefix}Rewrote {filename} to fix $phrase...";
                break;

            case InstructionHandleResult.NotCompatible:
                template = $"{logPrefix}Broken code in {filename}: $phrase.";
                mod.SetWarning(ModWarning.BrokenCodeLoaded);
                break;

            case InstructionHandleResult.DetectedGamePatch:
                template = $"{logPrefix}Detected game patcher ($phrase) in assembly {filename}.";
                mod.SetWarning(ModWarning.PatchesGame);
                break;

            case InstructionHandleResult.DetectedSaveSerializer:
                template = $"{logPrefix}Detected possible save serializer change ($phrase) in assembly {filename}.";
                mod.SetWarning(ModWarning.ChangesSaveSerializer);
                break;

            case InstructionHandleResult.DetectedUnvalidatedUpdateTick:
                template = $"{logPrefix}Detected reference to $phrase in assembly {filename}.";
                mod.SetWarning(ModWarning.UsesUnvalidatedUpdateTick);
                break;

            case InstructionHandleResult.DetectedDynamic:
                template = $"{logPrefix}Detected 'dynamic' keyword ($phrase) in assembly {filename}.";
                mod.SetWarning(ModWarning.UsesDynamic);
                break;

            case InstructionHandleResult.DetectedConsoleAccess:
                template = $"{logPrefix}Detected direct console access ($phrase) in assembly {filename}.";
                mod.SetWarning(ModWarning.AccessesConsole);
                break;

            case InstructionHandleResult.DetectedFilesystemAccess:
                template = $"{logPrefix}Detected filesystem access ($phrase) in assembly {filename}.";
                mod.SetWarning(ModWarning.AccessesFilesystem);
                break;

            case InstructionHandleResult.DetectedShellAccess:
                template = $"{logPrefix}Detected shell or process access ($phrase) in assembly {filename}.";
                mod.SetWarning(ModWarning.AccessesShell);
                break;

            case InstructionHandleResult.None:
                break;

            default:
                throw new NotSupportedException($"Unrecognized instruction handler result '{result}'.");
            }
            if (template == null)
            {
                return;
            }

            // format messages
            if (handler.Phrases.Any())
            {
                foreach (string message in handler.Phrases)
                {
                    this.Monitor.LogOnce(loggedMessages, template.Replace("$phrase", message));
                }
            }
            else
            {
                this.Monitor.LogOnce(loggedMessages, template.Replace("$phrase", handler.DefaultPhrase ?? handler.GetType().Name));
            }
        }
コード例 #23
0
ファイル: FieldFinder.cs プロジェクト: kurumushi/SMAPI
 /// <summary>Construct an instance.</summary>
 /// <param name="fullTypeName">The full type name for which to find references.</param>
 /// <param name="fieldName">The field name for which to find references.</param>
 /// <param name="result">The result to return for matching instructions.</param>
 public FieldFinder(string fullTypeName, string fieldName, InstructionHandleResult result)
     : this(fullTypeName, new[] { fieldName }, result)
 {
 }