-
Notifications
You must be signed in to change notification settings - Fork 128
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for single file analysis #1691
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -246,6 +246,11 @@ enum IntrinsicId | |
AppDomain_CreateInstanceFrom, | ||
AppDomain_CreateInstanceFromAndUnwrap, | ||
Assembly_CreateInstance, | ||
Assembly_Location, | ||
Assembly_CodeBase, | ||
Assembly_EscapedCodeBase, | ||
Assembly_GetFile, | ||
Assembly_GetFiles, | ||
RuntimeReflectionExtensions_GetRuntimeEvent, | ||
RuntimeReflectionExtensions_GetRuntimeField, | ||
RuntimeReflectionExtensions_GetRuntimeMethod, | ||
|
@@ -481,6 +486,31 @@ static IntrinsicId GetIntrinsicIdForMethod (MethodDefinition calledMethod) | |
&& calledMethod.HasParameterOfType (0, "System", "String") | ||
=> IntrinsicId.Assembly_CreateInstance, | ||
|
||
// System.Reflection.Assembly.Location () | ||
"get_Location" when calledMethod.IsDeclaredOnType ("System.Reflection", "Assembly") | ||
&& calledMethod.Parameters.Count == 0 | ||
=> IntrinsicId.Assembly_Location, | ||
|
||
// System.Reflection.Assembly.CodeBase () | ||
"get_CodeBase" when calledMethod.IsDeclaredOnType ("System.Reflection", "AssemblyName") | ||
&& calledMethod.Parameters.Count == 0 | ||
=> IntrinsicId.Assembly_CodeBase, | ||
|
||
// System.Reflection.Assembly.EscapedCodeBase () | ||
"get_EscapedCodeBase" when calledMethod.IsDeclaredOnType ("System.Reflection", "AssemblyName") | ||
&& calledMethod.Parameters.Count == 0 | ||
=> IntrinsicId.Assembly_EscapedCodeBase, | ||
|
||
// System.Reflection.Assembly.GetFile (string) | ||
"GetFile" when calledMethod.IsDeclaredOnType ("System.Reflection", "Assembly") | ||
&& calledMethod.HasParameterOfType (0, "System", "String") | ||
=> IntrinsicId.Assembly_GetFile, | ||
|
||
// System.Reflection.Assembly.GetFiles () | ||
"GetFiles" when calledMethod.IsDeclaredOnType ("System.Reflection", "Assembly") | ||
&& calledMethod.Parameters.Count == 0 | ||
=> IntrinsicId.Assembly_GetFiles, | ||
|
||
// System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor (RuntimeTypeHandle type) | ||
"RunClassConstructor" when calledMethod.IsDeclaredOnType ("System.Runtime.CompilerServices", "RuntimeHelpers") | ||
&& calledMethod.HasParameterOfType (0, "System", "RuntimeTypeHandle") | ||
|
@@ -1227,6 +1257,52 @@ methodParams[argsParam] is ArrayValue arrayValue && | |
reflectionContext.RecordUnrecognizedPattern (2058, $"Parameters passed to method '{calledMethodDefinition.GetDisplayName ()}' cannot be analyzed. Consider using methods 'System.Type.GetType' and `System.Activator.CreateInstance` instead."); | ||
break; | ||
|
||
// | ||
// System.Reflection.Assembly | ||
// | ||
// Location () | ||
// | ||
case IntrinsicId.Assembly_Location when _context.SingleFileAnalysis == true: | ||
_context.LogWarning ($"'{calledMethodDefinition.GetDisplayName ()}' always returns an empty string for assemblies embedded in a single-file app. If the path to the app directory is needed, consider calling 'System.AppContext.BaseDirectory'.", 3000, callingMethodDefinition); | ||
break; | ||
|
||
// | ||
// System.Reflection.Assembly | ||
// | ||
// CodeBase () | ||
// | ||
case IntrinsicId.Assembly_CodeBase when _context.SingleFileAnalysis == true: | ||
_context.LogWarning ($"'{calledMethodDefinition.GetDisplayName ()}' always returns an empty string for assemblies embedded in a single-file app. If the path to the app directory is needed, consider calling 'System.AppContext.BaseDirectory'.", 3000, callingMethodDefinition); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This actually throws, not return an empty string. I think we should add new warnings for this and other APIs (other than Assembly.Location and Assembly.GetFiles). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And now that we have these warnings being produced by the linker, we should |
||
break; | ||
|
||
// | ||
// System.Reflection.Assembly | ||
// | ||
// EscapedCodeBase () | ||
// | ||
case IntrinsicId.Assembly_EscapedCodeBase when _context.SingleFileAnalysis == true: | ||
_context.LogWarning ($"'{calledMethodDefinition.GetDisplayName ()}' always returns an empty string for assemblies embedded in a single-file app. If the path to the app directory is needed, consider calling 'System.AppContext.BaseDirectory'.", 3000, callingMethodDefinition); | ||
break; | ||
|
||
// | ||
// System.Reflection.Assembly | ||
// | ||
// GetFile (string fileName) | ||
// | ||
case IntrinsicId.Assembly_GetFile when _context.SingleFileAnalysis == true: | ||
_context.LogWarning ($"Assemblies embedded in a single-file app cannot have additional files in the manifest.", 3001, callingMethodDefinition); | ||
break; | ||
|
||
// | ||
// System.Reflection.Assembly | ||
// | ||
// GetFiles () | ||
// | ||
case IntrinsicId.Assembly_GetFiles when _context.SingleFileAnalysis == true: | ||
_context.LogWarning ($"Assemblies embedded in a single-file app cannot have additional files in the manifest.", 3001, callingMethodDefinition); | ||
break; | ||
|
||
|
||
// | ||
// System.Runtime.CompilerServices.RuntimeHelpers | ||
// | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -54,6 +54,7 @@ public class LinkContext : IDisposable | |
readonly Dictionary<string, string> _parameters; | ||
bool _linkSymbols; | ||
bool _keepTypeForwarderOnlyAssemblies; | ||
bool _singleFileAnalysis; | ||
bool _ignoreUnresolved; | ||
|
||
readonly AssemblyResolver _resolver; | ||
|
@@ -106,6 +107,11 @@ public bool KeepTypeForwarderOnlyAssemblies { | |
set { _keepTypeForwarderOnlyAssemblies = value; } | ||
} | ||
|
||
public bool SingleFileAnalysis { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: similar methods in LinkContext tend to be named IsSomething, maybe name this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, why expand the getter/settter. public bool SingleFileAnalysis { get; set; } |
||
get { return _singleFileAnalysis; } | ||
set { _singleFileAnalysis = value; } | ||
} | ||
|
||
#if FEATURE_ILLINK | ||
public readonly bool KeepMembersForDebugger = true; | ||
#else | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for more information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Linq; | ||
using System.Reflection; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using Mono.Linker.Tests.Cases.Expectations.Assertions; | ||
using Mono.Linker.Tests.Cases.Expectations.Metadata; | ||
|
||
namespace Mono.Linker.Tests.Cases.SingleFileAnalysis | ||
{ | ||
public class SingleFileAnalysisIsNotSet | ||
{ | ||
public static void Main () | ||
{ | ||
AssemblyLocationDoesNotWarn (); | ||
AssemblyGetFileDoesNotWarn (); | ||
} | ||
|
||
[Kept] | ||
[LogDoesNotContain ("IL3000")] | ||
static string AssemblyLocationDoesNotWarn () => Assembly.GetExecutingAssembly ().Location; | ||
|
||
[Kept] | ||
[LogDoesNotContain ("IL3001")] | ||
static void AssemblyGetFileDoesNotWarn () | ||
{ | ||
var a = Assembly.GetExecutingAssembly (); | ||
_ = a.GetFile ("/some/file/path"); | ||
_ = a.GetFiles (); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for more information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Linq; | ||
using System.Reflection; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using Mono.Linker.Tests.Cases.Expectations.Assertions; | ||
using Mono.Linker.Tests.Cases.Expectations.Metadata; | ||
|
||
namespace Mono.Linker.Tests.Cases.SingleFileAnalysis | ||
{ | ||
[SetupLinkerArgument ("--single-file-analysis", "true")] | ||
public class WarnOnSingleFileDangerousPatterns | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we move this tests here, can we remove them from ILLink.RoslynAnalyzer.Tests and call them in the analyzer with RunTest? Similar to what we already have for RequiresCapability tests. |
||
{ | ||
public static void Main () | ||
{ | ||
GetExecutingAssemblyLocation (); | ||
AssemblyProperties (); | ||
AssemblyMethods (); | ||
AssemblyNameAttributes (); | ||
FalsePositive (); | ||
} | ||
|
||
[Kept] | ||
[ExpectedWarning ("IL3000", | ||
"'System.Reflection.Assembly.Location.get' always returns an empty string for assemblies embedded " + | ||
"in a single-file app. If the path to the app directory is needed, consider calling 'System.AppContext.BaseDirectory'")] | ||
static string GetExecutingAssemblyLocation () => Assembly.GetExecutingAssembly ().Location; | ||
|
||
[Kept] | ||
[ExpectedWarning ("IL3000", | ||
"'System.Reflection.Assembly.Location.get' always returns an empty string for assemblies embedded " + | ||
"in a single-file app. If the path to the app directory is needed, consider calling 'System.AppContext.BaseDirectory'")] | ||
static void AssemblyProperties () | ||
{ | ||
var a = Assembly.GetExecutingAssembly (); | ||
_ = a.Location; | ||
// below methods are marked as obsolete in 5.0 | ||
// _ = a.CodeBase; | ||
// _ = a.EscapedCodeBase; | ||
} | ||
|
||
[Kept] | ||
[ExpectedWarning ("IL3001", | ||
"Assemblies embedded in a single-file app cannot have additional files in the manifest.")] | ||
[ExpectedWarning ("IL3001", | ||
"Assemblies embedded in a single-file app cannot have additional files in the manifest.")] | ||
static void AssemblyMethods () | ||
{ | ||
var a = Assembly.GetExecutingAssembly (); | ||
_ = a.GetFile ("/some/file/path"); | ||
_ = a.GetFiles (); | ||
} | ||
|
||
[Kept] | ||
[ExpectedWarning ("IL3000", | ||
"'System.Reflection.AssemblyName.CodeBase.get' always returns an empty string for assemblies embedded " + | ||
"in a single-file app. If the path to the app directory is needed, consider calling 'System.AppContext.BaseDirectory'.")] | ||
[ExpectedWarning ("IL3000", | ||
"'System.Reflection.AssemblyName.EscapedCodeBase.get' always returns an empty string for assemblies embedded " + | ||
"in a single-file app. If the path to the app directory is needed, consider calling 'System.AppContext.BaseDirectory'.")] | ||
|
||
static void AssemblyNameAttributes () | ||
{ | ||
var a = Assembly.GetExecutingAssembly ().GetName (); | ||
_ = a.CodeBase; | ||
_ = a.EscapedCodeBase; | ||
} | ||
|
||
// This is an OK use of Location and GetFile since these assemblies were loaded from | ||
// a file, but the linker is conservative | ||
[Kept] | ||
[ExpectedWarning ("IL3000", | ||
"'System.Reflection.Assembly.Location.get' always returns an empty string for assemblies embedded " + | ||
"in a single-file app. If the path to the app directory is needed, consider calling 'System.AppContext.BaseDirectory'")] | ||
[ExpectedWarning ("IL3001", | ||
"Assemblies embedded in a single-file app cannot have additional files in the manifest.")] | ||
static void FalsePositive () | ||
{ | ||
var a = Assembly.LoadFrom ("/some/path/not/in/bundle"); | ||
_ = a.Location; | ||
_ = a.GetFiles (); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we also add AssemblyName_CodeBase, AssemblyName_EscapedCodeBase, Module_Name, and Module_FullyQualifiedName?