Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -69,19 +69,33 @@ public override event ModuleResolveEventHandler? ModuleResolve
}

[DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)]
private static extern void GetCodeBase(QCallAssembly assembly,
bool copiedName,
private static extern bool GetCodeBase(QCallAssembly assembly,
StringHandleOnStack retString);

internal string? GetCodeBase(bool copiedName)
internal string? GetCodeBase()
{
string? codeBase = null;
RuntimeAssembly runtimeAssembly = this;
GetCodeBase(new QCallAssembly(ref runtimeAssembly), copiedName, new StringHandleOnStack(ref codeBase));
return codeBase;
if (GetCodeBase(new QCallAssembly(ref runtimeAssembly), new StringHandleOnStack(ref codeBase)))
{
return codeBase;
}
return null;
}

public override string? CodeBase => GetCodeBase(false);
public override string? CodeBase
{
get
{
var codeBase = GetCodeBase();
if (codeBase is null)
{
// Not supported if the assembly was loaded from memory
throw new NotSupportedException(SR.NotSupported_CodeBase);
}
return codeBase;
}
}

internal RuntimeAssembly GetNativeHandle() => this;

Expand All @@ -90,7 +104,7 @@ private static extern void GetCodeBase(QCallAssembly assembly,
// is returned.
public override AssemblyName GetName(bool copiedName)
{
string? codeBase = GetCodeBase(copiedName);
string? codeBase = GetCodeBase();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean that if I have an Assembly asm loaded from the bundle, asm.CodeBase will throw, but asm.GetName().CodeBase will be null?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup. I couldn't see a good way of making the behavior match. I don't think there's anything wrong with fetching an AssemblyName, so I don't think that should throw, but since the AssemblyName doesn't have a pointer back to the Assembly there's no way to hook just the CodeBase API, aside from doing something drastic like subclassing AssemblyName just for this scenario.

Open to alternatives though.


var an = new AssemblyName(GetSimpleName(),
GetPublicKey(),
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/src/vm/assembly.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ class Assembly
}
#endif // DACCESS_COMPILE

void GetCodeBase(SString &result)
BOOL GetCodeBase(SString &result)
{
WRAPPER_NO_CONTRACT;

Expand Down
9 changes: 6 additions & 3 deletions src/coreclr/src/vm/assemblynative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -562,21 +562,24 @@ void QCALLTYPE AssemblyNative::GetLocale(QCall::AssemblyHandle pAssembly, QCall:
END_QCALL;
}

void QCALLTYPE AssemblyNative::GetCodeBase(QCall::AssemblyHandle pAssembly, BOOL fCopiedName, QCall::StringHandleOnStack retString)
BOOL QCALLTYPE AssemblyNative::GetCodeBase(QCall::AssemblyHandle pAssembly, QCall::StringHandleOnStack retString)
{
QCALL_CONTRACT;

BOOL ret = TRUE;

BEGIN_QCALL;

StackSString codebase;

{
pAssembly->GetFile()->GetCodeBase(codebase);
ret = pAssembly->GetFile()->GetCodeBase(codebase);
}

retString.Set(codebase);

END_QCALL;

return ret;
}

INT32 QCALLTYPE AssemblyNative::GetHashAlgorithm(QCall::AssemblyHandle pAssembly)
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/src/vm/assemblynative.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class AssemblyNative
void QCALLTYPE GetLocation(QCall::AssemblyHandle pAssembly, QCall::StringHandleOnStack retString);

static
void QCALLTYPE GetCodeBase(QCall::AssemblyHandle pAssembly, BOOL fCopiedName, QCall::StringHandleOnStack retString);
BOOL QCALLTYPE GetCodeBase(QCall::AssemblyHandle pAssembly, QCall::StringHandleOnStack retString);

static
BYTE * QCALLTYPE GetResource(QCall::AssemblyHandle pAssembly, LPCWSTR wszName, DWORD * length);
Expand Down
23 changes: 16 additions & 7 deletions src/coreclr/src/vm/pefile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2128,9 +2128,8 @@ const SString &PEAssembly::GetEffectivePath()
// Codebase is the fusion codebase or path for the assembly. It is in URL format.
// Note this may be obtained from the parent PEFile if we don't have a path or fusion
// assembly.
//
// fCopiedName means to get the "shadow copied" path rather than the original path, if applicable
void PEAssembly::GetCodeBase(SString &result, BOOL fCopiedName/*=FALSE*/)
// Returns false if the assembly was loaded from a bundle, true otherwise
BOOL PEAssembly::GetCodeBase(SString &result)
{
CONTRACTL
{
Expand All @@ -2142,10 +2141,20 @@ void PEAssembly::GetCodeBase(SString &result, BOOL fCopiedName/*=FALSE*/)
}
CONTRACTL_END;

// All other cases use the file path.
result.Set(GetEffectivePath());
if (!result.IsEmpty())
PathToUrl(result);
auto ilImage = GetILimage();
if (ilImage == nullptr || !ilImage->IsInBundle())
{
// All other cases use the file path.
result.Set(GetEffectivePath());
if (!result.IsEmpty())
PathToUrl(result);
return TRUE;
}
else
{
result.Set(SString::Empty());
return FALSE;
}
}

/* static */
Expand Down
4 changes: 1 addition & 3 deletions src/coreclr/src/vm/pefile.h
Original file line number Diff line number Diff line change
Expand Up @@ -663,9 +663,7 @@ class PEAssembly : public PEFile
// Codebase is the fusion codebase or path for the assembly. It is in URL format.
// Note this may be obtained from the parent PEFile if we don't have a path or fusion
// assembly.
//
// fCopiedName means to get the "shadow copied" path rather than the original path, if applicable
void GetCodeBase(SString &result, BOOL fCopiedName = FALSE);
BOOL GetCodeBase(SString &result);

// Display name is the fusion binding name for an assembly
void GetDisplayName(SString &result, DWORD flags = 0);
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System;

namespace SingleFileApiTests
{
public class Program
{
public static void Main(string[] args)
{
switch (args[0])
{
case "fullyqualifiedname":
var module = typeof(object).Assembly.GetModules()[0];
Console.WriteLine("FullyQualifiedName: " + module.FullyQualifiedName);
Console.WriteLine("Name: " + module.Name);
return;

case "cmdlineargs":
Console.WriteLine(Environment.GetCommandLineArgs()[0]);
return;

case "codebase":
try
{
#pragma warning disable SYSLIB0012
_ = typeof(Program).Assembly.CodeBase;
#pragma warning restore SYSLIB0012
}
catch (NotSupportedException)
{
Console.WriteLine("CodeBase NotSupported");
return;
}
break;
}

Console.WriteLine("test failure");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,49 @@

namespace AppHost.Bundle.Tests
{
public class BundleEnvironmentGetCommandLineArgs : IClassFixture<BundleEnvironmentGetCommandLineArgs.SharedTestState>
public class SingleFileApiTests : IClassFixture<SingleFileApiTests.SharedTestState>
{
private SharedTestState sharedTestState;

public BundleEnvironmentGetCommandLineArgs(SharedTestState fixture)
public SingleFileApiTests(SharedTestState fixture)
{
sharedTestState = fixture;
}

[Fact]
public void FullyQualifiedName()
{
var fixture = sharedTestState.TestFixture.Copy();
var singleFile = BundleHelper.BundleApp(fixture);

Command.Create(singleFile, "fullyqualifiedname")
.CaptureStdErr()
.CaptureStdOut()
.Execute()
.Should()
.Pass()
.And
.HaveStdOutContaining("FullyQualifiedName: <Unknown>" +
Environment.NewLine +
"Name: <Unknown>");
}

[Fact]
public void CodeBaseThrows()
{
var fixture = sharedTestState.TestFixture.Copy();
var singleFile = BundleHelper.BundleApp(fixture);

Command.Create(singleFile, "codebase")
.CaptureStdErr()
.CaptureStdOut()
.Execute()
.Should()
.Pass()
.And
.HaveStdOutContaining("CodeBase NotSupported");
}

[Fact]
public void GetEnvironmentArgs_0_Returns_Bundled_Executable_Path()
{
Expand All @@ -23,7 +57,7 @@ public void GetEnvironmentArgs_0_Returns_Bundled_Executable_Path()

// For single-file, Environment.GetCommandLineArgs[0]
// should return the file path of the host.
Command.Create(singleFile)
Command.Create(singleFile, "cmdlineargs")
.CaptureStdErr()
.CaptureStdOut()
.Execute()
Expand All @@ -42,7 +76,7 @@ public void GetEnvironmentArgs_0_Non_Bundled_App()

// For non single-file apps, Environment.GetCommandLineArgs[0]
// should return the file path of the managed entrypoint.
dotnet.Exec(appPath)
dotnet.Exec(appPath, "cmdlineargs")
.CaptureStdErr()
.CaptureStdOut()
.Execute()
Expand All @@ -60,7 +94,7 @@ public class SharedTestState : IDisposable
public SharedTestState()
{
RepoDirectories = new RepoDirectoriesProvider();
TestFixture = new TestProjectFixture("EnvironmentGetCommandLineArgs", RepoDirectories);
TestFixture = new TestProjectFixture("SingleFileApiTests", RepoDirectories);
TestFixture
.EnsureRestoredForRid(TestFixture.CurrentRid, RepoDirectories.CorehostPackages)
.PublishProject(runtime: TestFixture.CurrentRid, outputDirectory: BundleHelper.GetPublishPath(TestFixture));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3775,4 +3775,7 @@
<data name="BinaryFormatter_SerializationDisallowed" xml:space="preserve">
<value>BinaryFormatter serialization and deserialization are disabled within this application. See https://aka.ms/binaryformatter for more information.</value>
</data>
<data name="NotSupported_CodeBase" xml:space="preserve">
<value>CodeBase is not supported on assemblies loaded from a single-file bundle.</value>
</data>
</root>
11 changes: 11 additions & 0 deletions src/libraries/System.Reflection/tests/AssemblyTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,17 @@ public void GetFile_InMemory()
Assert.Throws<FileNotFoundException>(() => asm.GetFiles(getResourceModules: false));
}

[Fact]
public void CodeBaseInMemory()
{
var inMemBlob = File.ReadAllBytes(SourceTestAssemblyPath);
var asm = Assembly.Load(inMemBlob);
// Should not throw
#pragma warning disable SYSLIB0012
_ = asm.CodeBase;
#pragma warning restore SYSLIB0012
}

[Fact]
public void GetFiles()
{
Expand Down