diff --git a/Blazor.sln b/Blazor.sln
index 1d4ce2ecff..2e6a44c313 100644
--- a/Blazor.sln
+++ b/Blazor.sln
@@ -8,9 +8,6 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{B867E038-B3CE-43E3-9292-61568C46CDEB}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "mono", "mono", "{7B5CAAB1-A3EB-44F7-87E3-A13ED89FC17D}"
- ProjectSection(SolutionItems) = preProject
- src\mono\mono.targets = src\mono\mono.targets
- EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoSanity", "samples\MonoSanity\MonoSanity.csproj", "{7C53BB6B-5906-4753-B507-C9FCC2F7E5B7}"
EndProject
@@ -125,6 +122,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "modules", "modules", "{F380
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.JSInterop.Test", "modules\jsinterop\test\Microsoft.JSInterop.Test\Microsoft.JSInterop.Test.csproj", "{ECF02708-4CA4-44B3-B23F-274F4B417FA5}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "mono", "src\mono\mono.csproj", "{DEEAE26E-D00C-4A3C-BE12-7A51A63CE55E}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -456,6 +455,14 @@ Global
{ECF02708-4CA4-44B3-B23F-274F4B417FA5}.Release|Any CPU.Build.0 = Release|Any CPU
{ECF02708-4CA4-44B3-B23F-274F4B417FA5}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{ECF02708-4CA4-44B3-B23F-274F4B417FA5}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
+ {DEEAE26E-D00C-4A3C-BE12-7A51A63CE55E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DEEAE26E-D00C-4A3C-BE12-7A51A63CE55E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DEEAE26E-D00C-4A3C-BE12-7A51A63CE55E}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
+ {DEEAE26E-D00C-4A3C-BE12-7A51A63CE55E}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
+ {DEEAE26E-D00C-4A3C-BE12-7A51A63CE55E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DEEAE26E-D00C-4A3C-BE12-7A51A63CE55E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DEEAE26E-D00C-4A3C-BE12-7A51A63CE55E}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
+ {DEEAE26E-D00C-4A3C-BE12-7A51A63CE55E}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -512,6 +519,7 @@ Global
{4A5D7F9D-9CED-44C1-983E-054D8E99A292} = {1386F99B-3862-40C2-B24D-796C07DC7921}
{1386F99B-3862-40C2-B24D-796C07DC7921} = {F380B6B6-9486-42BC-9B24-C388F8BF13A3}
{ECF02708-4CA4-44B3-B23F-274F4B417FA5} = {ADA3AE29-F6DE-49F6-8C7C-B321508CAE8E}
+ {DEEAE26E-D00C-4A3C-BE12-7A51A63CE55E} = {7B5CAAB1-A3EB-44F7-87E3-A13ED89FC17D}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {504DA352-6788-4DC0-8705-82167E72A4D3}
diff --git a/src/Microsoft.AspNetCore.Blazor.Browser.JS/Microsoft.AspNetCore.Blazor.Browser.JS.csproj b/src/Microsoft.AspNetCore.Blazor.Browser.JS/Microsoft.AspNetCore.Blazor.Browser.JS.csproj
index 89d89ff11f..8f689bc3ed 100644
--- a/src/Microsoft.AspNetCore.Blazor.Browser.JS/Microsoft.AspNetCore.Blazor.Browser.JS.csproj
+++ b/src/Microsoft.AspNetCore.Blazor.Browser.JS/Microsoft.AspNetCore.Blazor.Browser.JS.csproj
@@ -5,18 +5,20 @@
true
Latest
${DefaultItemExcludes};node_modules\**
+ false
true
+
-
+
diff --git a/src/Microsoft.AspNetCore.Blazor.Build/ReferenceFromSource.props b/src/Microsoft.AspNetCore.Blazor.Build/ReferenceFromSource.props
index ef30bab8fc..c56ef232e7 100644
--- a/src/Microsoft.AspNetCore.Blazor.Build/ReferenceFromSource.props
+++ b/src/Microsoft.AspNetCore.Blazor.Build/ReferenceFromSource.props
@@ -21,9 +21,6 @@
-
-
-
@@ -37,9 +34,11 @@
-
+
+
diff --git a/src/Microsoft.AspNetCore.Blazor.Build/targets/Blazor.MonoRuntime.props b/src/Microsoft.AspNetCore.Blazor.Build/targets/Blazor.MonoRuntime.props
index 07dcf437b4..47932fe42f 100644
--- a/src/Microsoft.AspNetCore.Blazor.Build/targets/Blazor.MonoRuntime.props
+++ b/src/Microsoft.AspNetCore.Blazor.Build/targets/Blazor.MonoRuntime.props
@@ -25,13 +25,4 @@
$(BaseBlazorRuntimeOutputPath)$(BlazorBootJsonName)
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Blazor.Build/targets/Blazor.MonoRuntime.targets b/src/Microsoft.AspNetCore.Blazor.Build/targets/Blazor.MonoRuntime.targets
index 3fec481cf8..95fd6c10b4 100644
--- a/src/Microsoft.AspNetCore.Blazor.Build/targets/Blazor.MonoRuntime.targets
+++ b/src/Microsoft.AspNetCore.Blazor.Build/targets/Blazor.MonoRuntime.targets
@@ -157,6 +157,9 @@
+
+
+
$(TargetDir)$(BaseBlazorRuntimeAsmjsOutputPath)%(FileName)%(Extension)
AsmJs
@@ -424,9 +427,10 @@
4) Add the file we just created to the list of file writes, to support incremental builds.
-->
+ <_MonoBaseClassLibraryFolder Include="$(MonoBaseClassLibraryPath);$(MonoBaseClassLibraryFacadesPath)" />
<_BlazorAssembliesToLink Include="@(_BlazorDependencyInput->'-a "%(Identity)"')" />
<_BlazorAssembliesToLink Include="@(IntermediateAssembly->'-a "%(FullPath)"')" />
- <_BlazorFolderLookupPaths Include="@(MonoBaseClassLibraryFolder->'-d "%(Identity)"')" />
+ <_BlazorFolderLookupPaths Include="@(_MonoBaseClassLibraryFolder->'-d "%(Identity)"')" />
<_BlazorAssemblyDescriptorFiles
Include="@(BlazorLinkerDescriptor->'-x "%(FullPath)"')" Condition="'@(BlazorLinkerDescriptor)' != ''" />
diff --git a/src/Microsoft.AspNetCore.Blazor.BuildTools/BuildToolsExe.props b/src/Microsoft.AspNetCore.Blazor.BuildTools/BuildToolsExe.props
new file mode 100644
index 0000000000..414ce1387d
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Blazor.BuildTools/BuildToolsExe.props
@@ -0,0 +1,5 @@
+
+
+ dotnet "$(ArtifactsBinDir)Microsoft.AspNetCore.Blazor.BuildTools/netcoreapp2.1/Microsoft.AspNetCore.Blazor.BuildTools.dll"
+
+
diff --git a/src/Microsoft.AspNetCore.Blazor.BuildTools/Cli/Commands/ILWipeCommand.cs b/src/Microsoft.AspNetCore.Blazor.BuildTools/Cli/Commands/ILWipeCommand.cs
new file mode 100644
index 0000000000..e2eccef17d
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Blazor.BuildTools/Cli/Commands/ILWipeCommand.cs
@@ -0,0 +1,110 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.IO;
+using System.Linq;
+using Microsoft.AspNetCore.Blazor.BuildTools.Core.ILWipe;
+using Microsoft.Extensions.CommandLineUtils;
+
+namespace Microsoft.AspNetCore.Blazor.BuildTools.Cli.Commands
+{
+ static class ILWipeCommand
+ {
+ public static void Command(CommandLineApplication command)
+ {
+ command.Description = "Wipes code from .NET assemblies.";
+ command.HelpOption("-?|-h|--help");
+
+ var inputDirOption = command.Option(
+ "-i|--input",
+ "The directory containing assemblies from which code should be wiped.",
+ CommandOptionType.SingleValue);
+
+ var specFileOption = command.Option(
+ "-s|--spec",
+ "The directory containing spec files describing which members to wipe from the assemblies.",
+ CommandOptionType.SingleValue);
+
+ var verboseOption = command.Option(
+ "-v|--verbose",
+ "If set, logs additional information to the console.",
+ CommandOptionType.NoValue);
+
+ var listOption = command.Option(
+ "-l|--list",
+ "If set, just writes lists the assembly contents to disk.",
+ CommandOptionType.NoValue);
+
+ var outputOption = command.Option(
+ "-o|--output",
+ "The directory to which the wiped assembly files should be written.",
+ CommandOptionType.SingleValue);
+
+ command.OnExecute(() =>
+ {
+ var inputDir = GetRequiredOptionValue(inputDirOption);
+ var outputDir = GetRequiredOptionValue(outputOption);
+ var specDir = GetRequiredOptionValue(specFileOption);
+
+ var specFiles = Directory.EnumerateFiles(
+ specDir, "*.txt",
+ new EnumerationOptions { RecurseSubdirectories = true });
+
+ foreach (var specFilePath in specFiles)
+ {
+ var specFileRelativePath = Path.GetRelativePath(specDir, specFilePath);
+ var assemblyRelativePath = Path.ChangeExtension(specFileRelativePath, ".dll");
+ var inputAssemblyPath = Path.Combine(inputDir, assemblyRelativePath);
+ var outputAssemblyPath = Path.Combine(outputDir, assemblyRelativePath);
+ var inputAssemblySize = new FileInfo(inputAssemblyPath).Length;
+
+ if (listOption.HasValue())
+ {
+ var outputList = AssemblyItem
+ .ListContents(inputAssemblyPath)
+ .Select(item => item.ToString());
+ File.WriteAllLines(
+ Path.ChangeExtension(outputAssemblyPath, ".txt"),
+ outputList);
+ }
+ else
+ {
+ WipeAssembly.Exec(
+ inputAssemblyPath,
+ outputAssemblyPath,
+ specFilePath,
+ verboseOption.HasValue());
+
+ Console.WriteLine(
+ $"{assemblyRelativePath.PadRight(40)} " +
+ $"{FormatFileSize(inputAssemblySize)} ==> " +
+ $"{FormatFileSize(outputAssemblyPath)}");
+ }
+ }
+
+ return 0;
+ });
+ }
+
+ private static string FormatFileSize(string path)
+ {
+ return FormatFileSize(new FileInfo(path).Length);
+ }
+
+ private static string FormatFileSize(long length)
+ {
+ return string.Format("{0:0.000} MB", ((double)length) / (1024*1024));
+ }
+
+ private static string GetRequiredOptionValue(CommandOption option)
+ {
+ if (!option.HasValue())
+ {
+ throw new InvalidOperationException($"Missing value for required option '{option.LongName}'.");
+ }
+
+ return option.Value();
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Blazor.BuildTools/Cli/Program.cs b/src/Microsoft.AspNetCore.Blazor.BuildTools/Cli/Program.cs
index cfd630096d..661701ac16 100644
--- a/src/Microsoft.AspNetCore.Blazor.BuildTools/Cli/Program.cs
+++ b/src/Microsoft.AspNetCore.Blazor.BuildTools/Cli/Program.cs
@@ -1,4 +1,4 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Blazor.BuildTools.Cli.Commands;
@@ -17,6 +17,7 @@ namespace Microsoft.AspNetCore.Blazor.BuildTools
app.HelpOption("-?|-h|--help");
app.Command("checknodejs", CheckNodeJsInstalledCommand.Command);
+ app.Command("ilwipe", ILWipeCommand.Command);
if (args.Length > 0)
{
diff --git a/src/Microsoft.AspNetCore.Blazor.BuildTools/Core/ILWipe/AssemblyItem.cs b/src/Microsoft.AspNetCore.Blazor.BuildTools/Core/ILWipe/AssemblyItem.cs
new file mode 100644
index 0000000000..efdaa5f8fd
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Blazor.BuildTools/Core/ILWipe/AssemblyItem.cs
@@ -0,0 +1,80 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using Mono.Cecil;
+using System.Collections.Generic;
+using System.Linq;
+using Mono.Cecil.Cil;
+
+namespace Microsoft.AspNetCore.Blazor.BuildTools.Core.ILWipe
+{
+ class AssemblyItem
+ {
+ public static IEnumerable ListContents(string assemblyPath)
+ {
+ var moduleDefinition = ModuleDefinition.ReadModule(assemblyPath);
+ return ListContents(moduleDefinition);
+ }
+
+ public static IEnumerable ListContents(ModuleDefinition moduleDefinition)
+ {
+ return moduleDefinition.Types
+ .SelectMany(GetNestedTypesRecursive)
+ .SelectMany(type => type.Methods)
+ .Select(method => new AssemblyItem(method))
+ .OrderBy(item => item.ToString(), StringComparer.Ordinal);
+ }
+
+ public MethodDefinition Method { get; }
+
+ public AssemblyItem(MethodDefinition method)
+ {
+ Method = method ?? throw new ArgumentNullException(nameof(method));
+ }
+
+ public void WipeFromAssembly(MethodDefinition createMethodWipedException)
+ {
+ if (!Method.HasBody)
+ {
+ return; // Nothing to do
+ }
+
+ // We don't want to actually remove the method definition from the assembly, because
+ // then you'd have an assembly that was invalid (it could contain calls to the method
+ // that no longer exists). Instead, remove all the instructions from its body, and
+ // replace it with "throw CreateMethodWipedException()". Then:
+ // [1] The method body is very short, while still definitely being valid (still OK for
+ // it to have any return type)
+ // [2] We've removed its references to other methods/types, so they are more likely
+ // to be actually removed fully by a subsequent IL linker pass
+ // [3] If the method is actually invoked at runtime, the stack trace will make clear
+ // which method is being excessively wiped
+ var il = Method.Body.GetILProcessor();
+ il.Body.Instructions.Clear();
+ il.Body.Variables.Clear();
+ il.Body.ExceptionHandlers.Clear();
+ il.Append(il.Create(OpCodes.Call, createMethodWipedException));
+ il.Append(il.Create(OpCodes.Throw));
+ }
+
+ public override string ToString()
+ {
+ var result = Method.ToString();
+ return result.Substring(result.IndexOf(' ') + 1);
+ }
+
+ public int CodeSize
+ => Method.Body?.CodeSize ?? 0;
+
+ private static IEnumerable GetNestedTypesRecursive(TypeDefinition type)
+ {
+ yield return type;
+
+ foreach (var descendant in type.NestedTypes.SelectMany(GetNestedTypesRecursive))
+ {
+ yield return descendant;
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Blazor.BuildTools/Core/ILWipe/MethodWipedExceptionMethod.cs b/src/Microsoft.AspNetCore.Blazor.BuildTools/Core/ILWipe/MethodWipedExceptionMethod.cs
new file mode 100644
index 0000000000..0634e77032
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Blazor.BuildTools/Core/ILWipe/MethodWipedExceptionMethod.cs
@@ -0,0 +1,74 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+using System;
+
+namespace Microsoft.AspNetCore.Blazor.BuildTools.Core.ILWipe
+{
+ static class MethodWipedExceptionMethod
+ {
+ public static MethodDefinition AddToAssembly(ModuleDefinition moduleDefinition)
+ {
+ // Adds the following method to the assembly:
+ // namespace ILWipe
+ // {
+ // internal static class ILWipeHelpers
+ // {
+ // public static Exception CreateMethodWipedException()
+ // {
+ // return new NotImplementedException("Cannot call method because it was wiped. See stack trace for details.");
+ // }
+ // }
+ // }
+ var ilWipeHelpersType = new TypeDefinition("ILWipe", "ILWipeHelpers",
+ TypeAttributes.NotPublic | TypeAttributes.Abstract | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit);
+ moduleDefinition.Types.Add(ilWipeHelpersType);
+
+ var methodAttributes =
+ MethodAttributes.Public |
+ MethodAttributes.HideBySig |
+ MethodAttributes.Static;
+ var createMethodWipedExceptionMethod = new MethodDefinition(
+ "CreateMethodWipedException",
+ methodAttributes,
+ ImportEquivalentTypeFromMscorlib(moduleDefinition, typeof(Exception)));
+ ilWipeHelpersType.Methods.Add(createMethodWipedExceptionMethod);
+
+ var notImplExceptionType = ImportEquivalentTypeFromMscorlib(moduleDefinition, typeof(NotImplementedException));
+ var notImplExceptionCtor = new MethodReference(".ctor", moduleDefinition.TypeSystem.Void, notImplExceptionType);
+ notImplExceptionCtor.Parameters.Add(new ParameterDefinition(moduleDefinition.TypeSystem.String));
+
+ var il = createMethodWipedExceptionMethod.Body.GetILProcessor();
+ il.Append(il.Create(OpCodes.Ldstr, "Cannot invoke method because it was wiped. See stack trace for details."));
+ il.Append(il.Create(OpCodes.Newobj, notImplExceptionCtor));
+ il.Append(il.Create(OpCodes.Ret));
+
+ return createMethodWipedExceptionMethod;
+ }
+
+ static TypeReference ImportEquivalentTypeFromMscorlib(ModuleDefinition module, Type type)
+ {
+ // We have to do this instead of module.ImportReference(type), because the latter
+ // would try to reference it in System.Private.CoreLib because this tool itself
+ // compiles to target netcoreapp rather than netstandard
+ IMetadataScope mscorlibScope;
+ if (module.TryGetTypeReference(typeof(object).FullName, out var objectRef))
+ {
+ mscorlibScope = objectRef.Scope;
+ }
+ else if (module.Name == "mscorlib.dll")
+ {
+ mscorlibScope = module;
+ }
+ else
+ {
+ throw new InvalidOperationException($"Could not resolve System.Object type within '{module.FileName}'.");
+ }
+
+ var typeRef = new TypeReference(type.Namespace, type.Name, module, mscorlibScope, type.IsValueType);
+ return module.ImportReference(typeRef);
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Blazor.BuildTools/Core/ILWipe/SpecList.cs b/src/Microsoft.AspNetCore.Blazor.BuildTools/Core/ILWipe/SpecList.cs
new file mode 100644
index 0000000000..f49e5e1ba7
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Blazor.BuildTools/Core/ILWipe/SpecList.cs
@@ -0,0 +1,33 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Microsoft.AspNetCore.Blazor.BuildTools.Core.ILWipe
+{
+ class SpecList
+ {
+ private List _itemSpecs;
+
+ public SpecList(string[] fromSpecLines)
+ {
+ var linesToUse = fromSpecLines.Where(
+ line => !string.IsNullOrWhiteSpace(line) && !line.StartsWith('#'));
+ _itemSpecs = new List(
+ linesToUse.Select(line => new SpecListEntry(line)));
+ }
+
+ public bool IsEmpty
+ {
+ get => _itemSpecs.Count == 0;
+ }
+
+ public bool Match(AssemblyItem item)
+ {
+ // If this needs to be faster, consider implementing some kind of matching tree.
+ var lastMatchingSpec = _itemSpecs.LastOrDefault(spec => spec.IsMatch(item));
+ return lastMatchingSpec == null ? false : !lastMatchingSpec.Negated;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Blazor.BuildTools/Core/ILWipe/SpecListEntry.cs b/src/Microsoft.AspNetCore.Blazor.BuildTools/Core/ILWipe/SpecListEntry.cs
new file mode 100644
index 0000000000..98da3b7f41
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Blazor.BuildTools/Core/ILWipe/SpecListEntry.cs
@@ -0,0 +1,121 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+
+namespace Microsoft.AspNetCore.Blazor.BuildTools.Core.ILWipe
+{
+ class SpecListEntry
+ {
+ public bool Negated { get; }
+ public string TypeName { get; }
+ public string MethodName { get; }
+ public string Args { get; }
+
+ public SpecListEntry(string parseSpecLine)
+ {
+ parseSpecLine = parseSpecLine.Trim();
+
+ if (parseSpecLine.StartsWith('!'))
+ {
+ Negated = true;
+ parseSpecLine = parseSpecLine.Substring(1);
+ }
+
+ var colonsPos = parseSpecLine.IndexOf("::");
+ if (colonsPos < 0)
+ {
+ TypeName = parseSpecLine;
+ }
+ else
+ {
+ TypeName = parseSpecLine.Substring(0, colonsPos);
+ parseSpecLine = parseSpecLine.Substring(colonsPos + 2);
+
+ var bracketPos = parseSpecLine.IndexOf('(');
+ if (bracketPos < 0)
+ {
+ MethodName = parseSpecLine;
+ }
+ else
+ {
+ MethodName = parseSpecLine.Substring(0, bracketPos);
+ Args = parseSpecLine.Substring(bracketPos + 1, parseSpecLine.Length - bracketPos - 2);
+ }
+ }
+ }
+
+ public bool IsMatch(AssemblyItem item)
+ {
+ return MatchesType(item)
+ && MatchesMethod(item)
+ && MatchesArgs(item);
+ }
+
+ private bool MatchesArgs(AssemblyItem item)
+ {
+ if (Args == null)
+ {
+ return true;
+ }
+ else
+ {
+ var methodString = item.Method.ToString();
+ var bracketPos = methodString.IndexOf('(');
+ var argsString = methodString.Substring(bracketPos + 1, methodString.Length - bracketPos - 2);
+ return string.Equals(argsString, Args, StringComparison.Ordinal);
+ }
+ }
+
+ private bool MatchesMethod(AssemblyItem item)
+ {
+ if (MethodName == null)
+ {
+ return true;
+ }
+ else if (MethodName.EndsWith('*'))
+ {
+ return item.Method.Name.StartsWith(
+ MethodName.Substring(0, MethodName.Length - 1),
+ StringComparison.Ordinal);
+ }
+ else
+ {
+ return string.Equals(item.Method.Name, MethodName, StringComparison.Ordinal);
+ }
+ }
+
+ private bool MatchesType(AssemblyItem item)
+ {
+ var declaringTypeFullName = item.Method.DeclaringType.FullName;
+ if (TypeName.EndsWith('*'))
+ {
+ // Wildcard match
+ return declaringTypeFullName.StartsWith(
+ TypeName.Substring(0, TypeName.Length - 1),
+ StringComparison.Ordinal);
+ }
+ else
+ {
+ // Exact match
+ if (string.Equals(
+ item.Method.DeclaringType.FullName,
+ TypeName,
+ StringComparison.Ordinal))
+ {
+ return true;
+ }
+
+ // If we're matching all members of the type, include nested types
+ if (MethodName == null && declaringTypeFullName.StartsWith(
+ $"{TypeName}/",
+ StringComparison.Ordinal))
+ {
+ return true;
+ }
+
+ return false;
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Blazor.BuildTools/Core/ILWipe/WipeAssembly.cs b/src/Microsoft.AspNetCore.Blazor.BuildTools/Core/ILWipe/WipeAssembly.cs
new file mode 100644
index 0000000000..3adf8f601a
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Blazor.BuildTools/Core/ILWipe/WipeAssembly.cs
@@ -0,0 +1,49 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.IO;
+using Mono.Cecil;
+using System.Linq;
+
+namespace Microsoft.AspNetCore.Blazor.BuildTools.Core.ILWipe
+{
+ static class WipeAssembly
+ {
+ public static void Exec(string inputPath, string outputPath, string specFilePath, bool logVerbose)
+ {
+ if (string.IsNullOrEmpty(outputPath))
+ {
+ outputPath = Path.ChangeExtension(inputPath, ".wiped" + Path.GetExtension(inputPath));
+ }
+
+ var specLines = File.ReadAllLines(specFilePath);
+ var wipeSpecList = new SpecList(specLines);
+ var moduleDefinition = ModuleDefinition.ReadModule(inputPath);
+
+ if (!wipeSpecList.IsEmpty)
+ {
+ var createMethodWipedException = MethodWipedExceptionMethod.AddToAssembly(moduleDefinition);
+
+ var contents = AssemblyItem.ListContents(moduleDefinition).ToList();
+ foreach (var contentItem in contents)
+ {
+ var shouldWipe = wipeSpecList.Match(contentItem)
+ && contentItem.Method != createMethodWipedException;
+
+ if (logVerbose)
+ {
+ Console.WriteLine($"{(shouldWipe ? "Wiping" : "Retaining")}: {contentItem}");
+ }
+
+ if (shouldWipe)
+ {
+ contentItem.WipeFromAssembly(createMethodWipedException);
+ }
+ }
+ }
+
+ moduleDefinition.Write(outputPath);
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Blazor.BuildTools/Microsoft.AspNetCore.Blazor.BuildTools.csproj b/src/Microsoft.AspNetCore.Blazor.BuildTools/Microsoft.AspNetCore.Blazor.BuildTools.csproj
index 30c0b4f127..dfc69d032e 100644
--- a/src/Microsoft.AspNetCore.Blazor.BuildTools/Microsoft.AspNetCore.Blazor.BuildTools.csproj
+++ b/src/Microsoft.AspNetCore.Blazor.BuildTools/Microsoft.AspNetCore.Blazor.BuildTools.csproj
@@ -1,4 +1,4 @@
-
+
netcoreapp2.1
@@ -6,12 +6,9 @@
false
-
-
-
-
+
diff --git a/src/Microsoft.AspNetCore.Blazor.BuildTools/ReferenceFromSource.props b/src/Microsoft.AspNetCore.Blazor.BuildTools/ReferenceFromSource.props
deleted file mode 100644
index 15d03741d6..0000000000
--- a/src/Microsoft.AspNetCore.Blazor.BuildTools/ReferenceFromSource.props
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
- dotnet "$(ArtifactsBinDir)Microsoft.AspNetCore.Blazor.BuildTools/netcoreapp2.1/Microsoft.AspNetCore.Blazor.BuildTools.dll"
-
-
-
-
-
-
-