Skip to content

ILens 11

Latest

Choose a tag to compare

@tadis174 tadis174 released this 31 Oct 11:17

ILens 11

  • The new tool search_code searches the decompiled C# of a type, a namespace or the whole assembly for lines that match a regular expression, and lists each member that holds a match with its matching lines beneath it
  • The pattern is a .NET regular expression, case-sensitive and applied to one line at a time; signatures, attributes and field initializers are searched as well as method bodies, and a lambda's lines stand under the member it is written in
  • The first search decompiles what it covers and keeps the text, so that later searches of the same assembly answer in under a second; on an assembly of 16 MB the first search over everything takes about 50 s
  • A search_code call decompiles for at most 45 s and then answers how far it came, as in Not finished: 7945 of 9220 types decompiled. Repeat the call to continue; what is done is kept.; the new startup option --max-search-seconds <n> sets the limit
  • Generated types are searched only with include_compiler_generated, or when type_name names one
  • The kept text takes at most a quarter of the memory budget; a search whose text does not fit ends in an error that points to type_name, namespace and --max-total-size
  • The server instructions name search_code
  • find_harmony_dependencies now lists the methods, properties and constructors of other assemblies' classes that a mod looks up by reflection, with the member that looks each up; such a line starts with Method, Property or Member and names the lookup call in brackets, as in Method System.Text.StringBuilder.Append(string) <- Mod.Helper.Call [AccessToolsMethod]. A lookup of another assembly's static constructor made to call it, through Type.TypeInitializer, is listed as in Method Shop.Store..cctor <- Mod.Hooks.Attach [TypeInitializer], Type.GetConstructors with static binding flags and AccessTools.GetDeclaredConstructors with searchForStatic true name the static constructor, and a parameter that a mod pins as passed by reference, with MakeByRefType, reads ref T in such a line
  • A lookup whose result the list already shows as a patch target adds no line of its own, and neither does a lookup of a member of a class the scanned assembly defines
  • A patch target a mod finds with Type.GetConstructor or with one of AccessTools' indexer lookups is now read, where the list showed ?
  • The description of find_harmony_dependencies and the server instructions now say that the list includes these lookups
  • find_harmony_dependencies no longer lists (none) ? <- Namespace.Container [Attribute] for a class that carries [HarmonyPatch] but from which Harmony patches nothing, such as a container of nested patch classes, so a ? again stands only for a target the scan could not read; over 391 library files of real mods, 20 such lines in 9 classes are gone
  • find_harmony_dependencies now lists a patch that Harmony never applies with the word Unapplied in place of the patch kind, as in Unapplied Shop.Till.Open <- Patches.TillPatch.OpenPostfix [Attribute]: a method whose [HarmonyPatch] names a target but whose name is no patch type, such as OpenPostfix, and which carries no patch attribute, and a class whose own [HarmonyPatch] or TargetMethod names a target but which has no patch method. Such a method showed nowhere, or only as a (none) ? line of its class, and such a class had (none) in place of the patch kind; over the same files, 3 methods in 6 files name a target that Harmony never patches
  • find_harmony_dependencies no longer prints the parameter types that an attribute gives a patch on a static constructor, a finalizer, or a getter, setter or event accessor named by its property or event, which Harmony ignores for these kinds: such a target now reads Shop.Till..cctor where it read Shop.Till..cctor(int), and Shop.Till.set_Limit where it read Shop.Till.set_Limit(string) for a setter that takes an int, so it names the method Harmony patches and can be passed on to decompile and analyze as it stands; an empty type list, which printed (), is left out the same way
  • find_harmony_dependencies names a generic method a mod patches or looks up with the type arguments it is made for, as in Postfix Shop.Widgets.NumberField<int>, from MakeGenericMethod, AccessTools.Method's generics argument and a SymbolExtensions.GetMethodInfo lambda, with ? for one not read, where it named the method alone; over 386 library files of real mods, 13 lines in 7 files now name such type arguments
  • find_harmony_dependencies writes a parameter type named by text, in a patch's attribute that also gives a MethodType or through Type.GetType, as the other lines do: int[,] where it wrote Int32[,], and an array of a generic type such as List<int>[] where it wrote a broken name holding the assembly's qualifier
  • find_harmony_dependencies lists the events of other assemblies' classes that an assembly looks up by reflection with Type.GetEvent, as lines that start with Event, as in Event Shop.Store.Opened <- Mod.Hooks.Attach [GetEvent], where it left them out, and an enumeration of a class's events with Type.GetEvents as Event Shop.Store.? <- Mod.Hooks.Attach [GetEvents]. Its description names Event among the first words of a line. Of the 381 assemblies of a game's installed mods, 8 files of 2 mods gain such a line, and no other answer changes
  • find_harmony_dependencies names an event's add or remove method that code takes right at the lookup, as in Method Shop.Store.add_Opened <- Mod.Hooks.Attach [GetEvent], and lists a Harmony patch of such a method with the method as its target, as in Postfix Shop.Store.remove_Opened <- Mod.Hooks.After [PatchCall in Mod.Hooks.Apply], where the target was ?
  • find_harmony_dependencies reads the lookups Harmony 2.4 adds to AccessTools. A patch whose TargetMethod or Harmony.Patch call finds its target with EventAdder, EventRemover, Finalizer or a Declared form of them, by typeof, as text or in Harmony's extension spelling, names that target, as in Prefix Shop.Store.add_Opened <- Mod.Hooks.Before [TargetMethod] and Postfix Shop.Cache.Finalize <- Mod.Hooks.After [PatchCall in Mod.Hooks.Apply], where the target was ?, and such a lookup made to call what it finds is listed, as in Method Shop.Store.remove_Opened <- Mod.Hooks.Attach [EventRemover] and Method Shop.Cache.Finalize <- Mod.Hooks.Attach [AccessToolsFinalizer], where it was left out
  • find_harmony_dependencies lists Harmony 2.4's AccessTools.Event and DeclaredEvent as Event lines, as in Event Shop.Store.Opened <- Mod.Hooks.Attach [AccessToolsEvent], and names an add or remove method taken from them, in a Method line and as a patch target, as it does for .NET's Type.GetEvent, where it left the lookup out and printed ? for the target
  • find_harmony_dependencies lists a method looked up with AccessTools.GetMethodByModuleAndToken as Method ? <- Mod.Hooks.Attach [GetMethodByModuleAndToken], where it left the lookup out; a module's ID and a metadata token name a method only in one build of that module
  • find_harmony_dependencies reads the lookups the release candidates of Harmony 3.0 add, of a method's lambdas, of a local function and of a state machine's MoveNext, and names the method looked up with the kind after it, as in Prefix Shop.Store.Restock (Lambdas) <- Mod.Hooks.Before [TargetMethods], Shop.Store.Restock (LocalFunction Count) and Shop.Store.Load (StateMachine), where the target was ?. Of a game's installed mods, 24 library files are built against Harmony 2.4 and 2 against a release candidate of Harmony 3.0; none of them calls these lookups yet, and none of the 381 assemblies compared changes its answer
  • find_harmony_dependencies names a class's static constructor where a mod looks it up through .NET's Type.GetConstructor with binding flags that hold Static and not Instance, in each overload and also from a field, or through Type.TypeInitializer, as in Prefix Shop.Store..cctor <- Mod.Hooks.Before [TargetMethod] and Postfix Shop.Store..cctor <- Mod.Hooks.After [PatchCall in Mod.Hooks.Apply], where it printed ?
  • find_harmony_dependencies reads AccessTools' searchForStatic from a field or a parameter as well, where it read only a literal true and named the instance constructor otherwise, and names a lookup for the static constructor with parameter types with those types, as in Shop.Store..cctor(int), a constructor no class has, since a static constructor takes none, where it named the static constructor, which such a lookup does not find
  • Where find_harmony_dependencies cannot read a constructor lookup's flag, it names both constructors the lookup may find, as in Prefix Shop.Store..ctor() and Prefix Shop.Store..cctor with DynamicTargetMethod, where it named the instance constructor alone for AccessTools' lookups and printed ? for Type.GetConstructor; with parameter types it names the instance constructor, the only one that takes them. AccessTools.all and allDeclared, which hold Static and Instance, ask for an instance constructor. None of the 381 library files of a game's installed mods looks up a static constructor in any of these ways, and none of their answers changes
  • find_harmony_dependencies lists the fields and methods a transpiler names to Harmony's CodeInstruction by class and name, through Call, also with "Namespace.Type:Method" text, and through LoadField and StoreField, as in Field Shop.Item.price <- Mod.Patches.Transpiler [LoadField] and Method Shop.Store.Restock <- Mod.Patches.Transpiler [Call], where it listed none. A method of the scanned assembly's own class gives no line, as for the lookups listed before, and a field of it does. Of the 381 library files of a game's installed mods, 37 gain lines: 133 field loads, 27 field stores, 54 calls and 4 call matches
  • find_harmony_dependencies lists the method a lambda names to CodeInstruction.Call, CodeMatch.Calls and new CodeMatch, and the field a lambda names to Harmony 2.4's SymbolExtensions.GetFieldInfo and CodeMatch's LoadsField and StoresField, as in Method Shop.Store.Count(string) <- Mod.Patches.Matches [Calls] and Field Shop.Store.label <- Mod.Patches.Matches [LoadsField], where it listed none
  • A lambda that makes a delegate from a method group, as in () => Helper, names the method, for those helpers and for SymbolExtensions.GetMethodInfo, as in Prefix Shop.Store.Count(string) <- Mod.Hooks.Before [TargetMethod], where it printed Prefix ? <- Mod.Hooks.Before [DynamicTargetMethod]
  • Where the decompiler writes such a lambda as the construction of its expression tree, as it does for a mod built against .NET 10 read beside a game's older System.Runtime, find_harmony_dependencies names the same members, where a SymbolExtensions.GetMethodInfo line printed ?
  • find_harmony_dependencies' description says that it lists the fields and methods named in a transpiler
  • find_harmony_dependencies lists the method Harmony hands a prefix, postfix or finalizer as a delegate, where the patch method takes a parameter of a delegate type the mod marks with Harmony's [HarmonyDelegate] or [HarmonyPatch], as in Method Shop.Store.Restock(int) <- Mod.Patches.Before [Injected], where it listed none, also where a Harmony.Patch call names the patch method's class as text. A method of the scanned assembly's own class gives no line, as for the lookups listed before, and a marked delegate type that no patch method takes gives none, since Harmony then looks nothing up. Of the 381 library files of a game's installed mods, 2 gain such a line, a mod's library and a copy of it
  • find_harmony_dependencies lists the method AccessTools.HarmonyDelegate<T>() looks up from the attributes of its delegate type, as in Method Shop.Store.Restock <- Mod.Hooks.Attach [HarmonyDelegate], and Method ? <- Mod.Hooks.Make [HarmonyDelegate] where that type is a type parameter, where it listed none
  • A delegate type marked with [HarmonyPatch] no longer gives a line marked (none) in find_harmony_dependencies, since Harmony reads its attributes for the delegate alone
  • find_harmony_dependencies' description says that it lists the methods a patch takes as delegate parameters
  • find_harmony_dependencies prints a line once, however many lookups or Harmony.Patch calls give it, where it printed the same line once for each of them, as for a reflection helper that tries four searches for every field it is given. Of the 381 library files of a game's installed mods, 34 answers lose 764 repeated lines, 657 of them in one mod's five builds
  • A line of find_harmony_dependencies with a ? for a part it could not read ends with how often it was found, as in Postfix ? <- Mod.Hooks.After [PatchCall in Mod.Hooks.Apply] ×2, since what those calls name may differ; the ? of a nullable value type, as in int?, is no such part
  • find_harmony_dependencies' description says what ? and ×N mean
  • find_harmony_dependencies answers on Harmony's own library, which some mods ship beside their assembly, in 3 to 10 seconds; Release 10 took 16 to 25 seconds there and listed fewer lines
  • find_harmony_dependencies names the parameter type of a method a mod looks up in a class nested in a class it names as text, as in Postfix Shop.Outer.Part.Join(Part), where it printed Join(?), and the same where the enclosing class comes from typeof but its library is not loaded or lacks the nested class. No answer changes over the 381 library files of a game's installed mods and the 20 builds of Harmony's library
  • find_harmony_dependencies lists the fields a prefix, postfix or finalizer takes as ___name parameters where a Harmony.Patch call names the patch method's class as text, through AccessTools.TypeByName or AccessTools.Method's "Type:Method" text, where it listed none
  • find_harmony_dependencies writes a nested class as a parameter type by its own name, as in Join(Part) and ..ctor(Part), where it wrote Join(Outer.Part) for a class a mod names as text with + and ..ctor(Outer.Part) for one a constructor patch's attribute names
  • find_harmony_dependencies writes a parameter of a patch target that a mod pins as passed by reference, with MakeByRefType, as ref T, as in Prefix System.Int32.TryParse(string, ref int), where it wrote ?. Harmony binds such a parameter whether the method declares it ref, out or in, and ref T passes back as parameter_types to the other tools
  • find_harmony_dependencies writes a generic class a mod names by its reflection name without type arguments as ILens writes it everywhere else, in C#'s unbound form, as in Postfix Shop.Box<>.Run and System.Collections.Generic.Dictionary<,>.Enumerator.MoveNext, where it wrote Shop.Box`1.Run and Dictionary`2.Enumerator, so that the name passes back to the other tools; and as a parameter type by its own name. This holds on every route that reads a class from text, AccessTools.Field's "Type:field" and HarmonyPatch's string target among them, and for a typeof a patch attribute's bytes hold
  • find_harmony_dependencies keeps a class nested in a generic class with type arguments where it reads the class from a patch attribute's bytes or from text, as a target and as a parameter type, as in ..ctor(Dictionary<int, string>.Enumerator) where it wrote ..ctor(Dictionary<int, string>), another type, and Shop.Crate<int>.Slot<string>.Fill where it wrote Shop.Crate<System.Int32, System.String>.Fill. No answer changes over the 381 library files of a game's installed mods and the 20 builds of Harmony's library
  • find_harmony_dependencies refuses a reference assembly the same way as analyze, where it listed every patch class of a compiler's reference assembly as patching nothing
  • analyze with read_by or assigned_by on a constant or an enum value now answers with an error saying that the compiler copies its value into each place that uses it, where it answered No readers or No writers whether or not anything used the constant; for an enum value the error points to used_by on the enum type
  • A decimal constant gets the same error, where assigned_by named its static constructor as its one writer
  • A constant of a type from another assembly gets the same error once ILens reads it from the assembly that declares it; when no assembly ILens found defines the type, the empty answer about a member the inspected assembly does not reference ends with a line saying that a constant cannot be ruled out
  • analyze with applied_to on an attribute class whose base attribute class another assembly defines now lists where the attribute is applied, where it answered with an error saying the class does not derive from System.Attribute
  • applied_to on a class whose base class ILens cannot load now answers the question, where it refused every such class; when nothing is marked with the class, the answer ends with a line saying it was not checked, as in Not checked: whether Mod.Helper is an attribute; its base class Lib.Base was not read, since ILens could not load Lib, Version=1.0.0.0
  • analyze with implements now finds the members of interfaces that another assembly or the core library defines, where it answered No interface members implemented for them; ILens reads them from the assemblies it can load beside the inspected one
  • implements now also takes a property and an event, and an accessor such as get_Name is answered with the interface's accessor, where it answered with nothing
  • When a base class or interface of the member's class cannot be loaded, the answer to implements ends with a line naming it, for a member that can be bound through a base type, one that is public and not static or that overrides, as in Not checked: what Lib.IShape and its base types declare; ILens could not load Lib, Version=1.0.0.0
  • implements no longer lists an interface member that another member of the class implements explicitly, one whose return type differs, or one the class has only through its base class while its member hides the base class's member instead of overriding it
  • The error for implements on a member of a type from another assembly now says that it needs the class that declares the member, where it said that it reads the member's body
  • analyze now names the assembly or the module where an attribute sits on one of them, as in assembly Tools or module Tools.dll, where used_by and applied_to printed ICSharpCode.Decompiler.TypeSystem.MetadataModule; the line appeared for every assembly, since the compiler puts attributes on each
  • An attribute on the assembly and one on the module are now two lines, where both were the one line
  • used_by on a type the assembly defines now lists the assembly or the module when an attribute there names the type beside a value of another assembly's enum, or is given a value of the enum asked about, where both were left out
  • used_by on a type from another assembly now finds an attribute that names it after a value of an enum the inspected assembly defines with an underlying type other than int, passed as a named argument or as an object, where the answer was No users
  • analyze with used_by on an enum the assembly defines now lists every type and member that carries an attribute given a value of it, where the answer named only the members whose signatures use the enum
  • used_by on a type the assembly defines now lists a type or member whose attribute names it by a typeof inside an array, or as a type argument as in typeof(List<T>), where both were left out
  • used_by on a type the assembly defines now lists a type or member whose attribute names it beside a value of an enum from another assembly, the framework's included, where such an attribute was skipped whole
  • used_by on an attribute class the assembly defines now lists every place the attribute is applied, also where it is given a value of an enum from another assembly, as applied_to does
  • analyze with used_by now reads an attribute that is given a value of another assembly's enum that does not take four bytes, such as a byte or a long enum, where such an attribute was not read at all: a type the attribute names by typeof is answered with what carries the attribute, whether or not the enum's assembly can be loaded
  • used_by on such an enum now lists everything that carries a value of it, as in assembly System.Core for System.Security.SecurityRuleSet, where the answer was No users or named only the members whose signatures use the enum; an enum given a value in the same attribute is answered in full as well
  • used_by on a type from another assembly now lists what carries an attribute class with type parameters that names it, when the attribute's constructor takes a value of a later type parameter and an earlier type argument is a constructed type such as List<int>; the answer left it out
  • Where an attribute's bytes can be read with two sizes of such an enum and only one reading names the type asked about, the answer does not list what carries it and ends with a line that starts Not checked:, which names the carrier, the attribute class and the enum
  • analyze with used_by on a type now answers for an assembly that applies an attribute class with type parameters whose constructor takes a later type parameter after a type argument that is a class, a struct, an enum or a constructed type, as in [Ordered<List<int>, int>(5)], where one such attribute ended the question for every type of the assembly in AggregateException: One or more errors occurred. (Format of the executable (.exe) or library (.dll) is invalid.)
  • analyze now says when an answer is empty only because compiler-generated results were left out: under the No ... line it counts them and names the setting that lists them, as in Not shown: 2 compiler-generated; include_compiler_generated lists them, where No implementations of System.Runtime.CompilerServices.IAsyncStateMachine read as if the assembly had no async method
  • analyze on a class or member the compiler generated, such as the class behind a lambda, now lists compiler-generated results without include_compiler_generated, as list_members, find_methods and search_code do for such a class; who instantiates a lambda's class answered No symbols, where its static constructor does
  • analyze with used_by on a type no longer lists the type's own method, property or event as a user when its code sits in a lambda, an iterator or an async method; such a member was listed while the type's other members were left out, so a type nothing else uses could answer 1 user
  • used_by on a type with include_compiler_generated no longer lists the fields and methods of the classes the compiler generated inside that type for its lambdas, iterators and async methods
  • analyze now reports a use under the method it is written in when it sits in a local function of a generic class or struct, in a lambda inside an iterator or async method of a generic class or a generic method, or in a lambda of a generic method that uses nothing but this; used_by, read_by, assigned_by and instantiated_by left such a use out, call_sites listed it under a generated name, and uses on the method left out what that code uses
  • analyze no longer reports a use inside a lambda that captures nothing under another method of the same generic class; it named the first method of the class that has such a lambda, and uses listed the symbols of all those lambdas for that method and none of them for the others
  • uses on a method of a generic class or on a generic method with a capturing lambda no longer lists System.Object..ctor(), and with include_compiler_generated the constructor of a closure class in a generic type is listed under its own name, both as for other types
  • analyze with uses now writes a type parameter by its name where it printed a number: for a method of a generic class or a generic method it lists List<T>.Add(T) and Shelf<T>._index, where it listed List<!0>.Add(!0) and Shelf<!0>._index
  • uses no longer lists a member twice that a generic method and a lambda, iterator or async body inside it both use, such as Crate<!!0>.Make() beside Crate<!0>.Make(); the count above the list was one too high for each such member
  • uses on a property of a generic class whose getter calls its own setter no longer lists the property's own accessor, as for a class without type parameters
  • analyze with uses now lists a type the method casts to, tests for with is or as, creates an array of, or takes the default of, where it listed a type only for a typeof; a method whose code did nothing else with the type answered No symbols used by ...
  • uses now lists a type also when the type's assembly does not lie beside the inspected one, by its name, as it lists the members of such a type; before, such a type got no line even for a typeof
  • The description of analyze now says that uses does not list a type that is only a local variable's or stands in the signature of a member the method uses, and that used_by on the type is the full test
  • analyze with uses on an async method now lists what the method's author wrote and leaves out what the compiler added to make it asynchronous: the calls on the task builder, the calls that ask an awaiter whether it is done and fetch its result, the GetAwaiter call an await compiles to, and the awaiter's type; a method with one await and three uses of its own listed about fifteen symbols, and include_compiler_generated still lists them all
  • uses on an async method whose own code uses nothing now answers No symbols used by ... with the line Not shown: N compiler-generated; include_compiler_generated lists them under it
  • analyze with uses now writes the keyword a called method declares for a parameter passed by reference, out, in or ref readonly, when the method's library lies in another allowed folder and not beside the inspected assembly; it wrote ref for each, where decompile of the same method showed out
  • uses on a method that calls into a library ILens cannot read now ends with a line that says so: Not checked: whether ref on a method of Lib.Store is out, in or ref readonly; ILens could not load Lib, Version=1.0.0.0, or ... Lib.Store as loaded declares no such method when the library found is another version; before, the answer wrote ref with nothing to show that the keyword was unknown
  • uses now answers about seven times as fast on a large assembly: 4,387 questions took 8.5 s where Release 10 took 62 to 64 s, in two runs of each build on one machine
  • analyze with used_by on an attribute's constructor now lists where the attribute is applied through that constructor, beside the code that calls it: the class or member that carries the attribute, the method for one on a parameter or a return value, and the assembly or the module; before, it listed calls in code alone and answered No users for a constructor that only attributes use, for an attribute class the assembly defines as for one of another assembly
  • used_by on such a constructor tells its overloads apart, where applied_to on the attribute class lists every place whatever the constructor
  • analyze with call_sites on an attribute's constructor now ends with a line that says how many attributes go through it, Not shown: 9 uses as an attribute; used_by lists them, also under No calls to ...; before, the answer gave no sign that the constructor is used at all
  • The error for call_sites on a member of another assembly's type now says that used_by lists what uses the member, where it said the methods that call it
  • analyze with assigned_by on a field or property of an attribute class now lists where an attribute sets it by name, as in [Label(Text = "x")], beside the code that sets it: the class or member that carries the attribute, the method for one on a parameter or a return value, and the assembly or the module; before, it listed code alone and answered No writers for a property only attributes set
  • used_by on such a property and on its setter lists the same places, and call_sites on the setter ends with a line that says how many attributes set the property, Not shown: 9 uses as an attribute; used_by lists them
  • The same holds for an attribute class of another assembly, where the answer said that the assembly references no such member; the line that names the members the assembly references now names those that attributes set by name
  • An attribute of a class that inherits the field or property counts for the class that declares it; where ILens cannot load the library that says whether a class inherits it, the answer ends with a line that says so: Not checked: whether Lib.SubAttribute inherits Kind from Lib.BaseAttribute; ILens could not load Lib, Version=1.0.0.0
  • analyze with used_by on a method now lists what names the method itself, and no longer the code that uses only a base class's member the method hides. A class that declares a member of its base class anew, with or without new, was answered with the users of both members: a getter declared anew in a subclass listed 25 users where 7 use it, and 19 answers on four assemblies named users where there is none
  • The same holds for used_by, read_by and assigned_by on a property, an event or an indexer that hides one, which are answered through their accessors
  • used_by on a method now answers for an assembly in which the code of a method breaks off inside an instruction, as tools that guard an assembly against decompiling write it; before, the question ended in BadImageFormatException: Read out of bounds. for every method of such an assembly. call_sites now lists a call that stands before such a break, where it left the calling method out
  • used_by on a method, a property or an event now answers about nine times as fast as Release 10 on a large assembly: 1,511 questions about its methods took 82 s where Release 10 took 710 s, in one run of each build on one machine
  • analyze with read_by or assigned_by no longer lists a method as one that may write a field when it passes the field by in or ref readonly to a virtual or interface method
  • analyze with used_by on a method of another assembly's class now names the method with the keyword of each parameter passed by reference, 1 user of Lib.Store.TryGet(int, out string): where it wrote TryGet(int, string), and so do the lists of overloads in its errors; where ILens cannot read the method's declaration it writes ref, and the answer ends with Not checked: whether ref on a method of Lib.Store is out, in or ref readonly; ILens could not load Lib, Version=1.0.0.0
  • analyze with uses now writes ref for a read-only parameter of a virtual method whose library ILens cannot find, where it wrote the parameter as passed by value and listed two overloads that differ only in it as one
  • analyze with call_sites now names each calling member once and lists its calls beneath it, each indented by two spaces, the form search_code lists its matches in: Shop.Ringers.Twice(Bell) on one line and bell.Ring(2) and bell.Ring() on the next two; before, each call stood on a line of its own that began with the member's full name, Shop.Ringers.Twice(Bell): bell.Ring(2). With parameter_name the arguments stand beneath the member in the same way. limit goes on counting calls, and the header is unchanged
  • The list is smaller by this, 5 to 14 % summed over the calls to the methods of two assemblies and more for a method with many calls; an answer in which every member calls the method once grows by one character for each member
  • analyze with call_sites now lists a call that C# writes without call syntax, with what it passes, where it only counted it as not shown: an operator and a compound assignment (a + b, a += b), a conversion, written or not, the unwritten one as the cast that calls it ((Coin)5), the calls of a foreach (foreach (Coin item in till)), the Add call behind an entry of a collection initializer ({ a }), the getter a property pattern calls ({ Empty: false }), the Deconstruct call of a deconstruction (var (count, empty) = till) and the base constructor call a constructor does not write (: base()). The header counts these calls, limit cuts them and parameter_name shows their arguments like any others. An answer about one conversion operator of a large assembly was 3,154 lines that said Not shown and 432,057 characters whatever the limit; it now starts 7451 calls to and is 11,148 characters at the default limit
  • An initializer that fills what a property holds, Spare = { a, b }, is now listed as a call of the property's getter for each entry
  • For a method that overrides, such a call counts as a call to the override when the type the pattern tests, the initializer fills, the foreach runs over or the deconstruction takes apart is the override's class or derives from it
  • The calls that are still not read, the creation of a delegate, the true and false operators of a condition and a property read through a pointer, now stand after the calls under one sentence with their sum and the members beneath it: Not shown: 3 calls in 2 members, which the decompiled C# does not show as a call:, then Shop.Rates.One() and Shop.Rates.Two(): 2 of 2, a member's count written unless it is its one call. Before, every member had a line of its own with the whole sentence, Not shown: 2 of 2 calls in Shop.Rates.Two(), which the decompiled C# does not show as a call, and limit did not cut these lines. limit now cuts the members of each such list on its own, and the sentence with the sum stays also with a limit of 0
  • The members that failed to decompile and the members whose decompiled C# shows more calls than their IL holds take the same form: Not shown: 1 call in 1 member, which failed to decompile: with Shop.Counter.Reset() (InvalidCastException) beneath it, and Check: 1 member shows more calls in the decompiled C# than its IL holds: with Shop.Counter.Tally(): 2 where the IL holds 1
  • analyze with used_by now lists a method that calls its base class's method through base. inside a lambda that captures a variable, an iterator or an async method; such a method was missing from the list, and with include_compiler_generated the answer named a method the compiler adds for the call, Shop.Counter.<>n__0(), in its place. In one large assembly 445 such uses were missing, and one base method lacked 68 of its users
  • analyze with uses now lists the base method for such a member, where it had no line for it
  • analyze with call_sites now lists such a call under the member it is written in, Shop.Counter.Open(int) with base.Open(width) beneath it, once for each call written; it listed the call under the method the compiler adds, and for a member that also made the call outside the lambda it listed the call twice and ended with a Check: line. The same for a call made through a local function that only passes it on
  • used_by and uses answer the same way for a base class another assembly defines, and used_by on the base class itself lists such a member
  • analyze with call_sites on a method that overrides now lists a call the compiled code makes to that override directly once, where it listed the call and also ended with a Not shown: line and a Check: line for the same call: a call through base. to an override the calling class does not override itself, a call of a generic method on an object of a class that overrides it, and a call on a value of a core library type such as int
  • analyze with used_by on a virtual method now lists every member that calls the method on an object of a subclass where the compiled call names that subclass's override; such a member was missing from the list, and nothing said so. A C# compiler writes such a call for a method with a type parameter of its own, relay.Send(1) on an object of a class that overrides Send<T>, and a VB or an F# compiler for any virtual method, so that a method of an assembly from those compilers could answer No users while it was called. A delegate made from the method on such an object counts in the same way. In one library written in VB, 116 of 21,624 answers about a method gained users, 668 lines in all
  • analyze with call_sites on such a method now lists those calls, 8 calls to Net.Relay.Send(T):, where it listed only the calls made on an object of the class that declares the method
  • analyze with call_sites on a method that overrides now lists a call made on an object of a class further down that overrides the method again, as the last line of the answer says it does; in code that names the override in the call, that call was missing. For a property's getter in such code the answer listed the call and also ended with a Not shown: line and a Check: line for the same call
  • A call that names an override and that the decompiled C# does not show now stands under the sentence that names the declaration, Not shown: 1 call to Net.Relay.Send(T) in 1 member, which the decompiled C# does not show as a call:
  • The line under an answer about a method that overrides now reads Override: code can call Net.Relay.Send(T) instead, where it read code calls: the list above the line can hold calls that name the override itself
  • analyze with overridden_by now lists an override with a more specific return type under every member it overrides; it was left out, so that an abstract method whose only override returns a more specific type answered No overrides
  • analyze with used_by and call_sites on a virtual member now lists the uses made on an object of a subclass that overrides the member with a more specific return type, where the compiled call names that subclass's override; those uses were missing, and nothing said so. The answers about such an override now end with the line Override: code can call Paper.Sheet.Copy() instead, as for any other override
  • analyze with overridden_by no longer lists an override of a member that a subclass declares anew with new virtual under the base class's member which that one hides and which it does not override
  • analyze with overridden_by on an accessor, get_Count, now lists the accessors that override it, where it answered No overrides
  • analyze with used_by on an event that overrides now answers, with the line that names the event it overrides; the question never returned, and the server went on working on it
  • analyze with implemented_by on a member of an interface now lists the members that implement it, where it listed every member of a class below the interface that shares its name and parameters: a member that hides the implementing one, one with another return type, one that is not public, an ordinary member beside an explicit implementation, an override of any of those, and a member that a derived interface declares anew or makes abstract again. In 1,647 library files, 2,231 of 35,369 answers held such a line
  • implemented_by now lists a member that a class inherits from a base class without the interface, as in class Stall : Counter, IShop { } where Counter declares Open; it was missing, so that the answer could be No implementations for a member every class implements that way. Such a line names the classes it holds for, Shop.Counter.Open() (for Shop.Kiosk, Shop.Stall, Shop.Stand and 2 more), and the member can belong to a class of another assembly. Where a base class could not be read, the answer ends with a line that starts Not checked:
  • implemented_by on an accessor, get_Name, now lists the accessors that implement it, where it answered No implementations
  • implemented_by on a member that belongs to no interface is now an error that names the class declaring it and, for a virtual member, points to overridden_by; it answered No implementations
  • analyze with implements now names the interface member for a member that a class below inherits and binds, with the classes it holds for, Shop.IShop.Open() (for Shop.Stall)
  • Both answers now count a member that implements through its metadata alone, whatever it is called: the MoveNext of a class the compiler generates for an iterator or an async method, a member that implements under a name of its own in an assembly from the VB compiler, and the accessor methods the F# compiler writes for an interface's property, which the question about the property lists. In one VB-compiled library, implemented_by on an interface's property listed 33 properties that do not implement it and none from the 196 classes that do
  • analyze with implemented_by no longer lists a member of a class under an interface's member when the class names only an interface derived from that interface and its base class implements the member already, and implements no longer names the interface member for it. A VB compiler writes such a class without a row for the base interface, and the runtime keeps the base class's implementation for it; the answers took the class's public member of the same name for the implementing one. For a class from a C# compiler, which names every base interface, nothing changes
  • analyze with used_by, read_by, assigned_by and call_sites on a member that implements an interface member now ends with a line that names the interface member code can call in its place, Implementation: code can call Shop.IShop.Open() instead; ask about that member for its uses.; the answer lists only what names the member itself, and nothing in it said that code reaches the member through the interface, so that No users read as proof that a method such as public void Dispose() is never called. In seven libraries, 2,485 of 3,809 public members that implement an interface member answered No users without such a line
  • The line also stands for a member that implements through its metadata alone, whatever it is called: the MoveNext of a class the compiler generates for an iterator or an async method, and a member that implements under a name of its own in an assembly from the VB compiler
  • The line replaces Explicit implementation: code reaches it only through ...; ask about that member for its uses., which only an explicit implementation had; that line said only also under an answer that lists a user, as for the generic GetEnumerator of an iterator's class, which the class's own code calls by name
  • A member that implements and also overrides now has both lines, the one that names the interface member before the line that starts Override:; it had the second alone
  • Where ILens could not read a library that decides whether a member implements, these answers now end with Not checked: whether it implements an interface member; ILens could not load Lib, Version=1.0.0.0
  • analyze with extension_methods lists, for a type the assembly defines and for one of another assembly, the extension methods of every class whose receiver is the type, by value or by reference, where it left out those of a Visual Basic module and every receiver passed by reference, and for another assembly's type a receiver of IntPtr or UIntPtr; over a game's 106 libraries, the .NET runtime and the SDK's Visual Basic libraries, 105 lists of a type the assembly defines and 192 of another assembly's type gain methods
  • analyze with extension_methods lists an extension method whose receiver is a type parameter under each type its constraints name, and one whose constraints name no type under System.ValueType where it asks for a struct and under System.Object otherwise, where no type listed it
  • analyze's uses names the event where a method reads or writes the field that holds its subscribers, as Shop.Bell.Changed for a Visual Basic method that raises the event, where it named the field Shop.Bell.ChangedEvent
  • analyze leaves the other fields a compiler adds out of its answers unless include_compiler_generated is set, as it leaves out an auto-property's backing field: in uses, a line that names the field of a Visual Basic auto-implemented property, a field F# adds or an older C# compiler's cached lambda, and in used_by on a delegate type, the field of a Visual Basic event
  • analyze's used_by on a Visual Basic event lists the methods that raise it, as it does for a C# event, where it listed only the code that subscribes, or No users for an event only its own class raises. 5 events of .NET 10's Visual Basic library for Windows Forms gain the methods that raise them
  • call_sites writes the default an omitted argument takes as list_members writes it, as Pace.Fast (default), where it wrote the enum's number, and its header with non_default_only names that default, as passes pace other than its default Pace.Fast
  • analyze used_by on an enum's underlying type names the enum, as Shop.Width among the users of System.Byte, where it named the enum's field, Shop.Width.value__; exposed_by on that type no longer lists the field
  • call_sites names a parameter as list_members writes it, as in argument for @switch and passes @default other than its default 3, and so do its errors that name a method's parameters
  • analyze answers a question that reads code, used_by, instantiated_by, read_by, assigned_by, call_sites or uses, asked of a reference assembly, such as a .NET reference pack, a .NET Framework reference assembly, a publicized copy of an assembly in a build folder or a compiler's reference assembly, with an error that says that the assembly holds a library's declarations without their code, to ask the build the library runs as, and which kinds read declarations alone and answer there, where it answered from the placeholder bodies that nothing uses, calls, creates, reads or writes the member. On a publicized copy of a game's main assembly, used_by on a logging method answered No users where the game's own build names 200
  • compare no longer ends in the error An item with the same key has already been added when a type has two members whose types print alike: classes of one name in two namespaces or in two enclosing classes, an array of one dimension beside one of two, a method's type parameter named like its class's, or properties with parameters that are not indexers, as VB declares them. Without type_name the error ended the list for the whole assembly; 13 of 1,722 library files measured gave it
  • compare now lists a member whose parameter, return or field type gave way to a type of the same name in another namespace or enclosing class, or to an array of another rank, as removed and added. It took the two for the same member and listed it as changed, or not at all where its body was unchanged: across 326 pairs of versions of real libraries 268 members changed that way, and 157 of them were not listed
  • compare no longer lists a method whose type parameter was renamed as removed and added
  • compare pairs two members of one type that share their whole signature, which an obfuscator can leave, in the order they stand in the type, where it ended in that error
  • compare asked for one method by name now says when the name finds different methods in A and in B, as the comparison of their type counts them, and names both: // Shop.Till.Open() → object in A and Shop.Till.Open() → string in B are different methods — bodies are identical in both assemblies. It named A's method alone, so that the answer read as unchanged for a method whose return type, parameter types or type parameters changed, or that moved to a base class or gave way to an extension method or an explicit implementation. Asked for each name that the comparison of a type lists as removed and added, in 18 pairs of versions of real libraries, 466 answers compared two different methods, 31 of them as identical
  • Where the two bodies differ, that line stands before them, also with format il, whose bodies do not show the methods' signatures
  • compare asked for one method by name now says where it found a method the asked type does not declare, [inherited from Shop.Counter] or [extension method on Shop.Till], as decompile does
  • compare now shows a constant or an enum member whose value changed between the two builds, as ~ public const int Limit = 5 → 6, and names the kind values in the list of changed types, as in ~ Shop.Color [values]. It showed no such change, and a type whose constants alone changed was not listed: between two versions of a game's main assembly, 86 enum members and 42 constants changed value, most enum members moved up by a member inserted before them, of which the member list showed the added member alone, and 2 enums that were only renumbered were missing from the list. The compiler copies such a value into the code built against the library, which keeps the old one. compare writes a constant's values as list_members does, as ~ public const bool Open = false → true, and a constant of an enum type that names the same member after the enum's members were numbered anew is no change, as // Shop.Picks.Usual — Pace.Slow in both assemblies, while the new numbers show at the enum's own members
  • compare asked for one constant or enum member by name now answers with its value in each build, // Shop.Limits.Size — 5 in A, 6 in B., where it answered that a field has no body. The name means the constant where the nearest class that has the name declares one, also where it hides a base class's method of that name, as an enum member named Equals does. The values of two constants of one name are written in full where their enums are two classes of one short name
  • compare asked for one method by name now answers two bodies that differ with a unified diff of them, the changed lines with three unchanged lines around each change under --- A: path and +++ B: path, where it printed both bodies in full under // === A: path === and // === B: path ===. Over 8,952 such answers from real version pairs, in C# and in IL, the answers came to 27 % of their characters, and an answer of over 10,000 characters to 3 to 17 % in the median
  • compare takes the parameter context, the number of unchanged lines shown around each change; a number larger than the method shows it whole with each change marked, which is how a whole IL body stays reachable
  • compare with format il shows only the instructions that changed: it reads the IL without its offsets, so an instruction inserted early does not mark the instructions after it, whose offsets moved, and a jump that lands on another instruction is marked although its text without offsets is the same
  • A hunk inside the code the compiler generated for the method, such as a lambda or a local function, names that part after its @@
  • compare asked for a field by name now names the constructors that set it in each build, as in Shop.Till.Limit is a field, which has no body; Shop.Till..cctor() sets it., where it pointed to the instance constructor .ctor for every field. A static field's initializer is in the static constructor, so that pointer led to not found on a static class, or to an answer about a constructor that holds none of the field's code, which read bodies are identical where the initializer had changed; in real libraries 45 to 48 % of the fields a constructor initializes are static
  • Where no constructor sets the field, the answer says so and names the question that finds what does: no constructor of Shop.Till sets it, and assigned_by lists what does.
  • Where the constructors that set the field differ between the builds, the answer names them for each build. It also says which build alone has a field, names a base class's constructors for a field a class inherits, and says that a constructor may set it where it passes the field's address on to code ILens cannot follow
  • compare asked for a field, a property or an event by name now answers for one that only the second build has, where it answered Member 'Label' not found on Shop.Till, its base types, or as an extension method. although the type's member list showed it as added. Over 2,972 pairs of versions of real libraries, 11,729 of 11,865 such questions got that answer
  • A property or an event that only one build has is answered with its code, as a method that only one build has is: // Shop.Till.Label — only B has it. and below it the property as decompile shows it, or with format il the IL of each accessor under a line that names it. One that only the first build has pointed to its accessors, so the code took up to two more calls, and for an event declared without accessors of its own they showed the code the compiler generated, where decompile shows one line
  • A field that only the second build has is answered as one that only the first build has: Shop.Till.Limit is a field, which has no body, and only B has it; Shop.Till..ctor() sets it.
  • compare now shows a member that keeps its name, parameter types and return type and whose declaration changed, such as a method made private, static or virtual, a field made readonly or turned into a constant, a property whose setter became internal or init, or a renamed parameter. Its line in a type's member list holds both declarations, ~ private bool Check(int a) → internal static bool Check(int a), and the type list names the kind declarations, as in ~ Shop.Till [declarations]. Before, such a member showed nowhere unless its code changed as well: over 2,993 pairs of versions of real libraries, 871 of 1,490 such members showed nowhere
  • A member whose declaration and method body both changed ends its line with [body], and a property or an event names the accessors whose bodies changed, as for a body alone. Before, such a member was listed with the first build's declaration alone, which read as a change of its code only
  • compare asked for one member by name now names both declarations first where they differ, // public void Open(int count) → private void Open(int count), before the verdict or the diff, in C# and in IL. It answered bodies are identical in both assemblies. for a method made private or one whose parameter was renamed, which breaks a Harmony patch that binds that parameter by name. An accessor is named with its property's or event's declaration, and a constant's values follow the line
  • compare asked for a field whose declaration changed now says so: Shop.Till.Limit is a field, which has no body, and its declaration changed: public int Limit → public readonly int Limit; Shop.Till..ctor() sets it.
  • compare asked by name for a name that the two builds have as members of different kinds, such as a field, a method or a constant that became a property, a property that became a method, or a property that gave way to an extension method, now names both, as in // Shop.Till.Limit is a field in A and a property in B: public int Limit → public int Limit { get; set; }, and below that shows the code of each member that has code under a line that names its build, // B: Shop.Till.Limit, in C# or, with format il, in IL. It described one build's member alone: a field that became a property answered is a field, which has no body; an initializer it has is in the constructors, .ctor., a property that became a field or a constant pointed to accessors only the first build has, and a method that became a property, or the reverse, was shown as the one build's method under only A has it or only B has it. Over 3,028 pairs of versions of real libraries, 113 names changed their kind
  • compare asked by name for a property or an event that both builds have now names both declarations where they differ, and the accessors of both builds, marking each that only one build has, as in Shop.Till.Depth is a property, and its declaration changed: public int Depth { get; } → public int Depth { get; set; }; compare its accessors, get_Depth, and set_Depth, which only B has. It named the first build's accessors alone and no declaration, so a setter the second build gained went unmentioned and a property made virtual or given an internal setter read as unchanged; of 384 properties of real version pairs whose declaration changed, 383 now show it, and 130 of them gained or lost an accessor
  • compare asked by name for a field that is a constant in one build now says so for that build, as in in A Shop.Kinds..cctor() sets it, and in B it is a constant., where it said that an initializer it has is in the constructors, .ctor
  • compare asked by name for a field, a property or an event that both builds have under the name as two different members, such as a field or a property whose type changed, now says so and names both declarations, as in Shop.Till.Limit is a field, which has no body, and A and B have different fields of that name: public int Limit → public long Limit; Shop.Till..ctor() sets it. and Shop.Till.Depth is a property, and A and B have different properties of that name: public int Depth { get; } → public long Depth { get; set; }; compare its accessors, get_Depth, and set_Depth, which only B has. It answered as if both builds had one member, without a word that the type changed, although code built against the first build no longer finds such a member; over 3,154 pairs of versions of real libraries, 889 fields and 430 properties changed their type that way
  • The same holds for a field or a property that one build declares and the other inherits from a base class, such as a property the second build now overrides or a field that moved to the base class: a declaration found on the base class is marked with it, as in public virtual string Label { get; } [inherited from Shop.Store] → public override string Label { get; }, and the answer names the type asked about, where it named the base class's member when the first build inherits it; over 3,056 of those pairs, 364 names changed that way
  • compare reports a method that becomes an extension method or stops being one as a changed declaration, as in ~ public static void Promote(Host host) → public static void Promote(this Host host), with its type listed under declarations, where it reported no change; between three versions of a game's main assembly, 3 methods became extension methods
  • compare, asked through another library's type where one build finds an extension method of the assembly and in the other the type has a method of that name, as when each build lies beside its own version of the library, says only A finds it through Lib.Kettle; in B the type has a member of that name, which C# takes first
  • compare, asked through a type that meets the constraints of an extension method whose receiver is a type parameter in one build alone while both declare the method, says — only A finds it through Shop.Box; in B Shop.Box does not meet the method's constraints.
  • compare names the library another build takes a type from where one build defines the type and the other does not, as after the type moved to a library: the list of changed types writes - Shop.Kettle, which in B comes from Lib, Version=1.0.0.0, the comparison of the type 3 member changes in Shop.Kettle, which only A has; in B it comes from Lib, Version=1.0.0.0:, and each answer that only one build has a member ends with ; in B Shop.Kettle comes from Lib, Version=1.0.0.0, where all three read as if the type had been deleted. A class nested in such a type is named with the library of the class enclosing it. Between consecutive builds of the .NET SDK, of the .NET runtimes from 8.0 to 10.0 and of mod libraries, 453 types that one build defines are taken by the other from another assembly
  • Where the other build forwards the type to another assembly, the same three answers name that assembly, as - Shop.Kettle, which B forwards to Lib, Version=1.0.0.0, where the list wrote - Shop.Kettle
  • compare with member_name answers a method that one build declares on a type and the other, which takes the type from a library or forwards it there, declares as an extension method for that type with both methods and the comparison of their bodies, as // Shop.Kettle.Boil() → void in A and Shop.KettleExtensions.Boil(Kettle) → void [extension method on Shop.Kettle] in B are different methods — bodies are identical in both assemblies., where it answered // Shop.Kettle.Boil() → void — only A has it. Where ILens cannot load that library, the answer starts with Not checked: whether Shop.Kettle or its base types have a member Boil, which C# would take first; ILens could not load Lib, Version=1.0.0.0
  • compare reports a parameter that gains or loses params on a collection type as a changed declaration, as ~ public static ImmutableArray<T> Create<T>(ReadOnlySpan<T> items) → public static ImmutableArray<T> Create<T>(params ReadOnlySpan<T> items) [body], where it listed the member as changed in its body alone or answered No member changes, and its comparison of such a member starts with the line that names both declarations. Between versions of the .NET SDK and of NuGet libraries, 92 members gain params on such a parameter
  • compare reports a method whose conditions changed as a changed declaration, as ~ public static T CompareExchange<T>(ref T location1, T value, T comparand) where T : class → public static T CompareExchange<T>(ref T location1, T value, T comparand) [body], where it listed the method as changed in its body alone or not at all, and its comparison of such a method starts with the line that names both declarations. Where a condition names a class that gave way to one of the same name in another namespace, both declarations are written in full. Between .NET 8 and .NET 10, 75 methods in 6 libraries of the runtime change their conditions
  • compare lists a type whose own type parameters changed their conditions with the kind metadata, as ~ System.Func<,> [metadata] between .NET 8 and .NET 10, where it did not list the type; a type parameter renamed alone or a where clause reordered does not count. Between those versions, 61 types change their conditions, 49 of them in the runtime's core library
  • compare no longer lists the field that holds an event's subscribers beside the event, as - private EventHandler Closed beside - public event EventHandler Closed, nor the other fields a compiler adds, unless include_compiler_generated is set. A public field that becomes an event reads - public EventHandler Changed beside + public event EventHandler Changed, where only the event was listed. Between .NET 8 and .NET 10, 4 member lists of the core library and of the Visual Basic library for Windows Forms lose 7 such lines
  • compare lists a type whose type parameter gains, loses or swaps its in or out with the kind metadata, as ~ Shop.ISource<> [metadata], where it did not list a type whose in or out alone changed. A class whose name does not count its type parameters, as F# names the classes it generates, and that gains or loses one under that name is listed with the kind metadata as well
  • compare, asked with type_name about a type the list of changed types marks metadata, ends its answer with the type's two declarations, as in Declaration: public class Shop.Mover : IOwner<int> → public class Shop.Mover : IOwner<string>, where it answered No member changes in Shop.Mover alone or listed the changed members alone. The declarations read as list_members writes a type's heading, with its base types, the conditions on its type parameters and their in or out, and are written with every type's namespace where they would read alike, or where a class gave way to one of the same name in another namespace. Between .NET 8 and .NET 10, 125 types in 24 libraries end their answer so, 63 of which answered No member changes alone. compare's description says that type_name answers what changed in that type
  • compare lists a struct that becomes readonly or a ref struct, or stops being one, with the kind metadata, as ~ Shop.Cursor [metadata], where it listed it only for a member that changed as well or not at all, and its type view ends with both declarations, as Declaration: public struct Shop.Cursor → public readonly struct Shop.Cursor. Between .NET 8 and .NET 10, 8 structs in 4 libraries of the runtime gain or lose readonly or ref, among them the public System.Text.Json.JsonReaderState, which becomes readonly; 3 of them were not listed at all
  • compare lists a class or struct that becomes a record or stops being one with the kind metadata beside members, as ~ Shop.Order [metadata,members], and its type view ends with Declaration: public class Shop.Order → public record Shop.Order : IEquatable<Order>, where it listed only the members a record brings
  • compare lists an enum whose underlying type changes with its members kept with the kind metadata alone, as ~ Shop.Width [metadata], and its type view answers No member changes in Shop.Width with the line Declaration: public enum Shop.Width : byte → public enum Shop.Width : short, where it showed the change only as the enum's value__ field removed and added, - public byte value__ and + public short value__
  • compare reads the lines of one member's name in a type's list of changes together and writes each with every type's namespace that names a class another of them names by the same short name, as - public void Swap(Shop.Alpha.Badge badge, int count) beside + public void Swap(Shop.Beta.Badge badge, long count), where both read Badge. A line that names two different classes of one short name on its own is written in full as well. Between .NET SDK 10.0.303 and 10.0.402, MSBuild's NtQueryInformationProcess takes a PROCESS_BASIC_INFORMATION from another namespace while it loses two parameters, which both lines showed as the same type
  • compare lists a method, constructor or indexer whose parameter's default changed, was gained or was lost with the kind declarations, as ~ public void Open(int width = 5) → public void Open(int width = 6), and its member view begins with that line, where it answered that the two bodies are identical. Between .NET 8 and .NET 10, 9 types in 6 libraries of the runtime gain such lines, as ~ public static void Assert(bool condition, string message) → public static void Assert(bool condition, string message = null)
  • compare counts two defaults as the same where they store the same value or read alike: a default of an enum type that names the same member after the enum's members were numbered anew is no change, while the new numbers show at the enum's own members, and a default one build writes as (Glaze)2 because it did not find the enum's library is the same as Glaze.Frosted in the other
  • compare, asked with type_name for a type its list of changed types names as one build's, answers that only that build has it also where the other build has a class of that name with other type parameters, where it compared the two classes as one: a class that gains or loses type parameters, as System.Buffers.AsciiByteSearchValues between .NET 8 and .NET 10, a static class beside a generic class of its name, as System.Collections.ObjectModel.ReadOnlyCollection, and a class nested in a class whose name the other build gives to a generic class, as System.Security.Cryptography.PemEnumerator.Enumerator, which answers 5 member changes in System.Security.Cryptography.PemEnumerator.Enumerator, which only A has: where it answered 8 member changes in System.Security.Cryptography.PemEnumerator.Enumerator: with the members of .NET 10's PemEnumerator<TChar>.Enumerator. A type whose name stands for two generic classes in the other build is answered so as well, where the answer was an error that the name matches 2 types. With member_name, compare answers that only that build has the method, where it compared it with the other class's method. Of the 7,609 types the lists of 766 pairs of libraries name as one build's, .NET 8 against .NET 10 and three .NET SDKs among them, 14 answered wrongly
  • compare, asked for a type name without brackets that fits no class of either build as written and finds a different class in each, answers with an error that names both, as Type name 'System.Buffers.AsciiCharSearchValues' matches System.Buffers.AsciiCharSearchValues<> in A and System.Buffers.AsciiCharSearchValues<,> in B. Name one as listed., where it compared the two classes as one
  • compare asked about one method that has no body in either build says so and why for each build, as in Shop.Lid.Open() → void — neither assembly has a body for it: abstract in both., or extern, or provided by the runtime for a delegate's methods, where it said that the bodies are identical in both assemblies
  • compare reads no method body of a reference assembly, such as a .NET reference pack, a .NET Framework reference assembly or a publicized copy whose bodies a tool removed, whose methods have no body, an empty one or one that throws null. Asked about one method, it says A and B are reference assemblies, which hold no code to compare., or that one build holds no code to compare with the other's and then shows the other build's code, where it compared the stubs, said that the bodies are identical or showed a stub beside the other build's code. A member only such a build has is shown by its declaration in place of its stub, and the error for a field names no constructor of such a build as setting it or not
  • compare's list of changed types and its list of a type's member changes mark no body as changed where a build is a reference assembly, and say beneath the list Not compared: bodies; A is a reference assembly, which holds no code. A publicized copy of a game's main assembly against the full build of its version listed 9,106 types as changed, 3,096 of them for their bodies alone
  • list_members and find_methods now list a compiler-generated type that type_name names, whatever include_compiler_generated says, where they answered No members and No methods
  • list_members with name_contains now finds a constructor by the name of its type, under which it is listed, as well as by .ctor; a static constructor is found the same way
  • list_members and find_methods no longer list a parameterless constructor for a struct or an enum that declares none; the assembly holds no such constructor, and asking for it ended in definitions contains null element or Specified cast is not valid
  • list_members now writes the methods, properties and events of an interface by one rule, as C# writes them: a member the implementing type supplies has no modifier, where a property and an event carried abstract and a method did not
  • A member of an interface with a default implementation is now written with virtual, a sealed one with sealed, and a static one with static, static abstract or static virtual; before, a method with a default implementation looked like one without, and a static abstract or static virtual member looked like a plain static one
  • A member with which an interface implements a member of its base interface is no longer written with sealed override, and one that makes the base member abstract again is written with abstract
  • compare writes an added or removed member of an interface the same way
  • list_members with include_inherited now writes ref readonly on a parameter of a member inherited from another assembly's class, where it wrote ref
  • list_members and compare now write a method or a read-only property that overrides with a more specific return type as the override it is, public override Letter Copy(), and a sealed one as public sealed override Seal Copy(); they wrote public virtual Letter Copy(), and the sealed one without any modifier, as a method that is not virtual. C# allows such an override since version 9 on .NET 5 and later, and the compiler writes one for the copy method of a record that inherits from a record
  • list_members lists every member of a type, also a generic method beside a method of its name and parameters and conversion operators that differ in their return type alone, where it listed one of each such group; with include_inherited it leaves out a base class's member only where a class below hides it, so a base class's Find<T>(string) is listed beside a derived class's Find(string); over a game's main assembly, a core library and two builds of a patching library, 38 types gain 241 members
  • list_members and compare write this before an extension method's first parameter, as C# and the decompiled code do, as in public static void Run(this Host host) and public static void Bump(this ref Counter counter), where they wrote such a method like any static method; find_methods and the names an answer passes on keep their form without it. Over a game's 106 libraries, 2,963 extension methods in 399 classes are listed this way
  • list_members writes params before a parameter that takes any number of arguments through a collection type, which C# 13 allows, as in public static ImmutableArray<T> Create<T>(params ReadOnlySpan<T> items), where it wrote the parameter without params. The .NET 10 shared framework holds 82 such parameters in 5 libraries, and the .NET SDK's top folder 25
  • list_members writes the conditions on a generic method's type parameters after its parameters, as C# writes them, as in public static T Tag<T>(this T value) where T : class, where it wrote the method without them, and those on a generic type's own type parameters after its base types in the heading, as in 2 members of public struct System.Nullable<T> where T : struct:. The conditions read class, struct, unmanaged or notnull, then the types, then new(), and last allows ref struct, which C# 13 allows and decompile does not show, as in public static int SizeOf<T>() where T : allows ref struct. An override and an explicit interface implementation take the conditions of the member they override or implement and write none, as the decompiled code does. Of the 2,586 types of the .NET 10 core library, 255 gain conditions in their list_members answer
  • list_members no longer lists the fields a compiler adds beside the member they belong to, as it already left out an auto-property's backing field: the private field that holds the subscribers of an event declared the short way, as private EventHandler Changed beside public event EventHandler Changed with accessibility all, the fields Visual Basic adds for an event and for an auto-implemented property, as private EventHandler ChangedEvent and private int _Size, the fields F# adds for records, unions and closures, as internal string Escaped@, and those an older C# compiler added for the lambdas it caches. include_compiler_generated lists them, and the Not shown line counts them as compiler-generated. With accessibility all, 8 of the 2,586 types of the .NET 10 core library lose such fields from their list, 5 of the 9,644 types of a game's main library and 113 of the 190 types of the F# build tasks library
  • list_members writes in or out before each variant type parameter of an interface or a delegate in its heading, as C# declares them: 1 member of public interface System.Collections.Generic.IEnumerable<out T> : IEnumerable where T : allows ref struct: and 4 members of public delegate System.Func<in T, out TResult> where T : allows ref struct where TResult : allows ref struct:, where it wrote each type parameter by its name alone. A type nested in a variant interface writes the in or out of its own type parameters, as C# declares them there. 66 types of the .NET 10 shared framework gain in or out in their heading, 55 of them in its core library
  • list_members writes a parameter's default in a declaration as C# writes it, as public void Open(int width = 5) and public static Task<byte[]> ReadAllBytesAsync(string path, CancellationToken cancellationToken = default), an enum's value by its member's name, as public void Go(Pace pace = Pace.Fast), a combination of flags as Edges.Left | Edges.Top, and the number with the enum's name, as (Pace)7, where no member has it or the enum's library was not found. In the .NET 10 core library, the C# compiler library and a game's main library, 4,053 declaration lines of 11,174 types gain defaults
  • list_members writes a constant's value as C# writes it: a bool as true or false, where it wrote True and False; a number with the suffix C# requires, as 1.5m, 0.5f and 5000000000L, in the same form whatever the number format of the machine ILens runs on; a special value by its name, as int.MaxValue, but a constant of the type that declares it by its value, as public const int MaxValue = 2147483647; and a constant of an enum type by its member's name, as public const Pace Usual = Pace.Slow, where it wrote the number
  • list_members leaves out the field value__ an enum keeps its value in, which no source declares and the decompiled code never shows, and counts the enum's members alone, as 3 members of public enum Shop.Pace: and 3 fields: for an enum of three values, where it listed public int value__ and counted it as a fourth member. An enum without members answers No members of public enum Shop.Blank with Not shown: 1 compiler-generated; include_compiler_generated lists it, and include_compiler_generated lists the field again. Each of the 19,667 enums of .NET 10, the .NET SDK, a game's libraries and its mods had that line
  • compare leaves the field out as well; the type view of an enum only one build has lists its members alone
  • list_members writes a name a declaration declares with the @ C# needs before a reserved keyword, as the decompiled code does: a parameter, as in public Func(object @object, nint method) and public virtual IAsyncResult BeginInvoke(T arg, AsyncCallback callback, object @object), which every delegate's constructor and BeginInvoke write, where it wrote object object, and a field, a method, a property, an event and a type parameter, as in public int @base. A word that is a keyword only in a query or an async method, such as from or await, takes none, as C# accepts it in a declaration. In .NET 10 and a game's libraries, 1,743 names in the declarations of 901 types gain the @
  • compare writes its member lines and declaration lines the same way, as in ~ public void Send(object obj) → public void Send(object @object), and lists the same members as changed
  • list_members writes abstract override for an abstract method, property or event that overrides a member of a base class, as C# and the decompiled code do, as in public abstract override string ToString() and protected abstract override bool IsArrayImpl(), where it wrote public abstract string ToString() as for a new member that hides the base class's. That holds for one that returns a more specific type and for a generic one as well; a member declared abstract anew keeps abstract alone. In .NET 10, an SDK's top folder and a game's libraries, 283 lines of 85 types gain override
  • compare writes its lines the same way, and lists a member that is declared abstract anew in one build and an abstract override in the other as a changed declaration, as in ~ public abstract void Start() → public abstract override void Start(), where it found no change. A changed member that is an abstract override reads as one, as in ~ public override bool CanRead { get; } → public abstract override bool CanRead { get; } [get_CanRead]
  • decompile now answers for a field of a struct that declares no parameterless constructor, where it ended in definitions contains null element
  • find_types, find_methods and list_members now say when their accessibility filter left matches out: an answer with results ends with a line such as Not shown: 15 internal or private, and an empty answer also names the setting that lists them, as in No methods followed by Not shown: 2 internal or private; accessibility all lists them
  • An empty answer from these tools also counts the matches left out as compiler-generated and names every setting needed to list them, as in Not shown: 39 compiler-generated; include_compiler_generated with accessibility all lists them
  • A member name that a class and its base class use for different kinds of member, such as a field where the base class has a method, now means the member of the more derived class, as in C#, where decompile and analyze answered that the name is ambiguous and the derived class's member could not be reached at all; a method the name hides stays reachable with parameter_count or parameter_types, or through the base class
  • A field beside an extension method of its name is now answered with the field, where the name was ambiguous
  • A constructor can now be asked for by the name of its type, as list_members prints it, in decompile, analyze and compare, where the answer was Member 'Widget' not found; .ctor works as before, and a static constructor is still .cctor
  • .ctor on a struct with one declared constructor now answers without parameter_count or parameter_types, where the answer named two overloads
  • .ctor or the type's name on a struct or enum that declares no constructor is now answered with a not-found error that says so and, for a struct, points to instantiated_by; compare answered bodies are identical in both assemblies for it
  • A constructor is now looked for on the asked type alone: .ctor with a hint that no constructor of the class satisfies is answered with the class's own constructors, where the answer was a base class's constructor marked as inherited
  • list_members, find_methods, decompile and analyze now write in and ref readonly on a parameter of a virtual, abstract or interface method, of a delegate and of an interface's indexer; they wrote such a parameter as if it were passed by value, Peek(Big) for Peek(in Big), while the same parameter of a method that is not virtual had its keyword
  • Two overloads that differ only in such a keyword are now told apart in the lists and in the error that asks to choose between them, and compare answers for a class that declares two of them, where it ended in An item with the same key has already been added
  • A parameter of a C++/CLI library that is a reference inside a modifier is now written with ref
  • compare, decompile and analyze asked by name for a field, a property or an event answer in a few milliseconds, since they no longer go through every class of the assembly for extension methods of that name at each question: on a game's main assembly compare answers in 3 to 4 ms where Release 10 took about 70 ms, and decompile in about 4 ms where it took about 45 ms
  • Every tool writes a generic method's type parameters after its name, as in Shop.Till.Get<T>(string), and a method made for type arguments with those arguments, as in Shop.Till.Get<int>(string) in what analyze lists for uses; a method and a generic method of one name and parameters printed alike, so a list, an overload error or a uses answer named two methods with one line, and uses merged a generic method's calls with other type arguments into one
  • member_name reads type parameters in brackets after a method's name, Get<T>, Get<int> or Get<>, in decompile, analyze and compare, so such a method can be asked for apart from its namesake; a name without them means the method without type parameters where the name has both, and with parameter types that only a generic method of the name has, that generic method
  • A type argument is written as a parameter type is, by its keyword or short name, as in System.Collections.Generic.List<int> where System.Collections.Generic.List<System.Int32> stood beside parameter types like int, in find_harmony_dependencies, in analyze's uses and implements answers and in their Implementation and Override lines; over every tenth type of a game's main assembly about a third of the uses answers hold such a line
  • decompile, analyze and compare take return_type, a pattern for the return type as find_methods has it, so that one of several conversion operators that share their name and parameter type can be asked for on its own, as op_Explicit with return_type int on a number type that declares one for each type it converts to; such an operator could not be reached, and the overload error listed its namesakes in lines that read alike. Over a game's 106 libraries and the .NET 10 runtime, 448 conversion operators share their name and parameter types with another
  • A parameter type written without a keyword means the overload that takes the value, where a method beside one that takes the value by reference, as an out parameter or as a nullable value answered with an overload error: Truncate with parameter_types decimal means Truncate(decimal) beside Truncate(ref decimal), int means int and not int?, and a name without type arguments means the type without them. A base class's method that fits the parameter types as written is found before a derived class's method they fit only loosely, so asking Task<TResult> for ContinueWith with Action<Task> answers with Task's ContinueWith(Action<Task>)
  • Type parameter names in brackets select a generic method where the count leaves several: Take<U> means Take<U>(T) beside Take<T>(T) in a class with a type parameter T
  • Every line that names a conversion operator writes its return type after it, as in Shop.Coin.op_Explicit(Coin) → int, in analyze's answers and headers, call_sites, search_code and the overload errors; operators that differ in their return type alone stood as one line in a uses or used_by answer. find_methods lists such operators by their return type
  • A line that would read alike another line of its answer is written in full, every type with its namespace and enclosing classes, as public void Take(Shop.Alpha.Badge badge) beside public void Take(Shop.Beta.Badge badge), in list_members, find_methods, analyze, call_sites, search_code, the overload errors and compare's member list and member view, and so is a header that names the member asked about where a namesake reads alike; list_members printed such a pair as the same line twice
  • parameter_types and return_type read a ? after a type as C# does, in find_methods, decompile, analyze and compare, also inside a type argument or an array's element: int? means the nullable type alone, string? the type string and T? a type parameter's nullable form or the type parameter itself; such a pattern matched nothing, so a method whose parameter types were copied from the decompiled code, as Enumerable.Sum(IEnumerable<int?>), answered No overload ... matches or No methods
  • Every answer writes a nullable value type as C# and the decompiled code do, int? where it wrote Nullable<int> and T? for Nullable<T>, in list_members, find_methods, analyze, compare, the overload errors and find_harmony_dependencies; a nullable type before a member's name keeps its name, as in System.Nullable<int>.GetValueOrDefault(). Over a game's main assembly the member lists of 610 of 9,644 types change
  • Every answer writes an array of two or more dimensions as C# and the decompiled code do, int[,] and int[,,] where it wrote int[], in list_members, find_methods, the headers of decompile, analyze and compare, compare's member lists, the overload errors and find_harmony_dependencies; overloads that differ in an array's dimensions alone, which printed as one line, print apart, and uses lists a call of each where it merged them into one line. An array of arrays is written with the outermost array's brackets first, int[][,] for an array of two-dimensional arrays, also in a uses line that names an array's own member, which wrote System.Int32[][,] for a two-dimensional array of arrays. Over a game's 106 libraries, 66 of 456,814 members hold such an array
  • parameter_types and return_type read an array's dimensions in find_methods, decompile, analyze and compare: int[,] means a two-dimensional array, and an array of arrays is read outermost first; held exactly, as a hint is first, int[] means an array of one dimension, so a method beside one that takes a two-dimensional array can be asked for on its own, where both answered with an overload error saying they cannot be narrowed further and int[,] matched nothing
  • decompile, analyze and compare name an extension method found from the type it extends under the class that declares it, and its mark names the type it extends: // Shop.TillExtensions.Open(Till) → void [extension method on Shop.Till], where decompile wrote // Shop.Till.Open(Till) → void [extension method on Shop.TillExtensions], naming a method Shop.Till does not have, and analyze's mark named the class that declares the method
  • decompile, analyze and compare find an extension method that a Visual Basic module declares from the type it extends, as they find one of a C# static class: asked for Wave on Shop.Gizmo, decompile answers with // Shop.GizmoModule.Wave(Gizmo) → void [extension method on Shop.Gizmo] where it answered that the type has no such member, also not as an extension method. Every static method with ExtensionAttribute counts, whatever its class, by the rule list_members writes this by; six Visual Basic libraries of the .NET SDK hold 1,632 such methods
  • They find an extension method whose receiver is passed by ref, in or ref readonly from the type the receiver refers to, as Bump(this ref Counter counter) from Counter, and one whose receiver is IntPtr or UIntPtr, which the decompiled code writes as nint or nuint, from System.IntPtr or System.UIntPtr, where they answered that the type has no such member; a game's 106 libraries hold 267 extension methods whose receiver is passed by reference and is a type of their own library
  • decompile, analyze and compare find an extension method that the inspected assembly declares for another library's type, or for a base class or interface of it, when asked through that type, as through a type the assembly defines: decompile shows its code under // Shop.KettleExtensions.Boil(Kettle) → void [extension method on Lib.Kettle] where it answered that Lib.Kettle is not defined in this assembly, compare compares its bodies where it answered that neither assembly defines the type, and analyze answers every kind for it, call_sites and uses where they were refused and used_by where it answered No users. A game's libraries, mod libraries and the .NET runtime hold 931, 2,835 and 1,061 extension methods for another library's type; asked through it with their parameter types, 930, 2,831 and 1,057 are now answered
  • A name the other library's type has as a public instance member keeps the answer about that member, as does an internal one where the library grants the inspected assembly its internals through InternalsVisibleTo and, through an interface, a member its base interface declares, and parameter types that fit only the extension method select it; a protected, private or static member of the name, or an internal one the library does not grant, leaves the extension method to answer, as C# binds a call on a value outside the type's derived classes, while a hint that fits a static or protected method, such as parameter_count 0, answers about that method, also where the extension method takes the same parameters. call_sites and compare, which look for a method, find the extension method also where the type has a property or a field of the name, while decompile and used_by answer about the property or the field
  • Where ILens cannot load the other library, an extension method for the type itself is found unless the assembly's references name a member of that name on the type, and the answer says what was not read: Not checked: whether Lib.Kettle or its base types have a member Boil, which C# would take first; ILens could not load Lib, Version=1.0.0.0; the same line names a base interface of the type that cannot be read
  • decompile, analyze and compare find an extension method whose receiver is a type parameter through every type that fits the method's constraints, as C# calls it there: asked for Corners through a class that implements the interface the type parameter asks for, decompile shows its code under // Shop.ShapeExtensions.Corners<T>(T) → int [extension method on T where T : Shop.IShape] where it answered that the type has no such member, also not as an extension method. Through another library's type the same holds where ILens reads that library, and where it cannot, only such a method without any constraint is found. A game's libraries, mods, the .NET runtimes and the SDK hold 236, 84, 55 and 553 such methods
  • decompile and analyze answer with the property, field or event of a name where several extension methods of that name apply to the type as well, where they answered with an overload error naming the extension methods; the .NET runtime and the SDK hold 275 such pairs of a type and a name
  • An answer that names an extension method found through a type whose base class ILens cannot load ends with Not checked: whether Lib.Widget or its base types have a member Polish, which C# would take first; ILens could not load Lib, Version=1.0.0.0, since a member of that class would be what C# calls
  • decompile, analyze, list_members, find_methods and compare read a type name without type parameters as the type without them that the inspected assembly references, where the assembly defines only generic types of that name, as C# reads the name in the assembly's code: in an assembly that defines Shop.Marker<T> and Shop.Marker<T, U> and uses another library's Shop.Marker, analyze with used_by on Shop.Marker lists the users of that library's Shop.Marker, where every tool answered that the name matches the two generic types and left out the type it means. A game's 106 libraries hold 2 such names, the mods, the .NET runtimes and the SDK none
  • compare answers such a name with Type 'Shop.Marker' is not defined in either assembly; it comes from Lib, Version=1.0.0.0., and decompile, list_members, find_methods and search_code with Shop.Marker is not defined in this assembly; it comes from Lib, Version=1.0.0.0. This assembly defines Shop.Marker<>, Shop.Marker<,>., which names the generic types a caller may have meant
  • An assembly that defines one generic type of such a name no longer has the name answered about that type: analyze with used_by on Shop.Gadget answered No users of Shop.Gadget<> for an assembly whose code uses another library's Shop.Gadget, and now lists that code
  • A name that generic types of the assembly and generic types it references share is an error naming both groups, Type name 'Shop.Crate' matches 2 types: this assembly defines Shop.Crate<,>; it references Shop.Crate<> from Lib, Version=1.0.0.0. Name one as listed., where the name was answered about the assembly's one type of that name or the error listed its own types alone; Type not found for a count of type parameters no type of the name has lists them in the same two groups. A game's libraries hold 2 such names and the .NET Windows Desktop runtime 1
  • decompile, analyze and compare take an array type as type_name, written as C# writes it, such as byte[], System.Byte[], Shop.Part[], int[,] or int[][], to reach the extension methods code can call on such an array, which no question reached before but through the class that declares them: analyze with extension_methods lists the methods for exactly that array and for an array of a type parameter the array fits, as 3 extension methods for System.Byte[]:, and decompile, analyze and compare find one by name through the array, as C# binds a call on it, also one for an interface the array implements, System.Array or object, under a header marked [extension method on System.Byte[]] or [extension method on T[] where T : struct]. Before, decompile answered Type not found: byte[]. and analyze No extension methods for byte[] with This assembly neither defines nor references a type of that name. A game's libraries, the .NET 10 runtimes and the .NET SDK hold 475 extension methods for an array
  • A public instance member of System.Array or System.Object of the name comes first and is answered as asked through that class, as C# binds it, while a static member of System.Array such as Reverse or Resize does not keep an extension method of its name out; for call_sites and compare, which look for a method, a property of System.Array such as Length does not keep it out either
  • Every other question answers an array type with byte[] is an array type, which only analyze with kind 'extension_methods' and decompile, analyze and compare with member_name take, to find the extension methods code can call on it., where list_members answered Type not found: byte[].
  • Where ILens cannot load the library of the array's element, the answers say what was not read, as Not checked: whether more extension methods apply to Lib.Beam[] through what Lib.Beam derives from; ILens could not load Lib, Version=1.0.0.0
  • decompile, list_members and find_methods answer a question about a type the inspected assembly forwards to another assembly with that assembly, as System.Net.ServicePoint is not defined in this assembly, which forwards it to System.Net.Requests, Version=10.0.0.0., where they answered Type not found: System.Net.ServicePoint. 100 of the 172 libraries of the .NET 10 shared framework forward types, System.Runtime 936, mscorlib 1,386 and netstandard 2,611. A type the assembly both references and forwards is answered with the forward as well, where the answer said it comes from ...
  • A name without type parameters that several generic types the assembly forwards share is an error that lists them, as Type name 'System.Func' matches 17 types this assembly forwards: System.Func<>, System.Func<,>, ... Name one as listed., where every tool answered Type not found, and beside types of that name the assembly defines or references they stand in a group of their own, as this assembly defines Shop.Dial<>; it forwards Shop.Dial<,> to Core, Version=3.0.0.0. A name without type parameters means the forwarded type without them, as it means a referenced one
  • analyze, asked about a type the assembly forwards, ends its empty answer with This assembly forwards it to System.Private.CoreLib, Version=10.0.0.0., and asked about a member of such a type with This assembly forwards System.Object to System.Private.CoreLib, Version=10.0.0.0., where both said This assembly neither defines nor references a type of that name.
  • compare, asked about a type neither build defines, names where each build that forwards it forwards it, as Type 'System.Net.ServicePoint' is not defined in either assembly; A and B forward it to System.Net.Requests, Version=10.0.0.0., where it answered Type 'System.Net.ServicePoint' not found in either assembly.; beside a build that only references the type it reads ...; in B it comes from Lib, Version=1.0.0.0; A forwards it to Core, Version=3.0.0.0.
  • Through a class of the inspected assembly whose base class from another library has a private member of a name, or an internal one the library does not grant, decompile, analyze and compare answer with the assembly's extension method of that name, as C# binds it, where they answered App.OwnShelf inherits Lib.Shelf.Dust() from a base class this assembly does not define; .... Where nothing else answers to the name, that error stays, and where a class further up has a member of the name the class can reach, the error names that member, as System.Delegate's HasSingleTarget in place of System.MulticastDelegate's, which a delegate type cannot reach
  • decompile, list_members and find_methods, asked for a type by a name that finds no type, name the types of that name in another namespace, as Type not found: Mall.Basket. Types named Basket: Shop.Basket., where they answered Type not found: Mall.Basket. alone. Where no type differs only in the namespace, every type of the name is named, one nested in another class included, so a name that leaves out the class a type is nested in finds it too. A type the assembly references or forwards is named with the library it comes from or goes to, and at most 40 types are named, the rest counted. Of 676 such answers for names taken from a game's main library and a mod, 669 name exactly one type
  • analyze, asked about a type nothing in the assembly names, ends its empty answer with the same list after This assembly neither defines nor references a type of that name., so a name under the wrong namespace no longer reads like an unused type
  • compare, asked about a type neither build has, names the types of that name either build has, as Type 'Mall.Basket' not found in either assembly. Types named Basket: Shop.Basket., and where the builds differ, which build has them, as In B, types named Banner: Shop.Banner.. It also names the types that share the name with another count of type parameters, as the other tools do, where it named none
  • The mark of an extension method found through a type, whose receiver is a type parameter, writes allows ref struct among its conditions, as [extension method on T where T : allows ref struct]
  • decompile, analyze and compare take a member name written with the @ C# needs before a reserved keyword, as member_name @params for a field the decompiled code writes public TypeArray @params;, where the answer was Member '@params' not found, and analyze takes parameter_name @switch as switch, where it answered that no overload has a parameter named @switch. An @ before a word that is no keyword stays part of the name, since F# begins names with it
  • An answer to a call with limit 0 no longer has an empty line between its header and the line for what the limit cut, in find_types, find_methods, find_harmony_dependencies, compare and every kind of analyze except call_sites
  • A list asked with a negative limit now answers like one asked with limit 0, with its header and the count of what was left out, where the last line named more results than the list holds, as in 21 types: followed by ... (24 more; raise limit to see all) for limit -3; list_members answered this way already
  • A value a parameter does not take is now answered with an error that names the parameter and what it takes, as in Invalid value 7 for argument 'accessibility'. Valid values: public, public_protected, all. or Invalid value 1.5 for argument 'limit'. Expected an integer., where the answer was a JsonException that named an internal type such as ILens.AccessibilityFilter and a byte position, and named no parameter
  • A call with several such values now gets one answer that names each of them, and a value is repeated up to its first 40 characters
  • A number or several comma-separated names for a parameter with listed values are now refused with that error, where they were answered as one of the listed values: list_members with kinds "field,method" listed the fields alone, with kinds [99] it answered No members, and find_types with accessibility 99 answered as with all
  • A list of parameter types that holds null is now refused, where find_methods answered No methods
  • A listed value in another case is now taken with or without its underscores, as in Public_Protected, where only the form without them, PublicProtected, was
  • A whole number with a zero fraction, such as 1.0, the text "true" or "false" for a boolean, and null for any optional parameter are now taken, null as the parameter left out; each was an error, null for limit, for a boolean and for a parameter with listed values
  • A required parameter that is left out or given as null is now answered with Missing required argument 'kind'., where one left out was answered with a sentence that ended in (Parameter 'arguments')
  • parameter_types now reads several types written as one text, "string, int", as the list of them, on its own or as an element of a list, where find_methods answered No methods and decompile, analyze and compare answered that no overload matches parameter types (string, int) while listing an overload with exactly those; a comma inside angle or square brackets, as in Dictionary<int, string>, stays in its type
  • The error for get_Item, set_Item or Item on a type whose indexer has another name now ends with that name and its accessors, as in its indexer is named Chars: System.String.get_Chars(int), where it said only that no such member exists
  • The overload error says to write the type parameters after the name where the candidates differ in them alone, where it pointed to parameter_types, which cannot tell them apart, and a name whose count of type parameters fits no method is answered with the methods of that name
  • The overload error says what tells the candidates apart: return_type where they differ in their return type alone, the type parameters after the name, or the parameter types written as listed where those given fit more than one
  • An allowed folder given through a junction or a symbolic link now admits the assemblies in it, addressed through the link or by their real path, where every request ended in Path resolves (via a symbolic link or junction) to a location outside any allowed root
  • References in such a folder are now found when decompiling, where they stayed unresolved
  • The link of such a folder is resolved once, when the server starts; when the link is moved to another folder while the server runs, requests through it are refused until a restart, and the folder it pointed to stays allowed
  • The two refusals that list the allowed roots now add a root's resolved path where it differs from the one given, as in Allowed roots: C:\Link (resolved: D:\Target)
  • The server now ends at startup with a line naming the folder when it cannot resolve an allowed folder, such as one below a folder it may not list, where it started and answered every request for that folder with an error
  • An answer that leaves something out for want of a class of another assembly now says what was found for the class's library. list_members with include_inherited and the error for a member that was not found ended such an answer with ILens could not load Lib, Version=1.0.0.0 also when a library of that name was loaded without the class. They, and the Not checked: lines of analyze with applied_to, implements, uses, used_by and assigned_by, now name the file and its version: Lib as loaded from C:\libs\Lib.dll, Version=2.0.0.0, does not hold Lib.Widget; referenced is Version=1.0.0.0. An assembly built against an older version of a library gets this line, and one whose framework classes are read from another .NET than the one it runs on
  • A file of the library's name that holds another assembly is named with what it holds: ILens could not load Lib, Version=1.0.0.0: the file found, C:\libs\Lib.dll, holds the assembly Other
  • A class the library forwards to another library is followed there, and the answer names the library the class is missing in or that could not be loaded
  • An assembly that names one class through two libraries, of which one holds it, gets the library the class is missing in
  • ILens now runs on .NET 10.0.14 instead of 10.0.12, with the .NET security fixes of October 2026
  • The user guide describes search_code, the startup option --max-search-seconds, and every tool's parameters, answers and errors as this release gives them
  • The README names search_code among the tools whose output is C#
  • The token-cost table in the README and the user guide is measured on 2026-10-31