-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
Tracking issue for remaining AssemblyBuilder.Save work in .NET 9 #92975
Comments
Tagging subscribers to this area: @dotnet/area-system-reflection-emit Issue DetailsSee #62956 for what have done for adding
|
An important part of |
Look the last bullet of the list. |
Oops! Not sure how I missed that. Good to see it got included. |
This comment was marked as off-topic.
This comment was marked as off-topic.
We are making a good progress, planning to finish the main functionality (excluding entry point and PDB) and make related APIs public in preview 1. Now it would be great to test the implementation with a real-life scenario. I would really appreciate if you could:
Thank you in advance! |
Here's my real-life scenario: https://github.com/boo-lang/boo This is not a "simple app" but rather a thorough, comprehensive test of the system: a compiler that uses Last I checked, all the tests pass under .NET Framework. So seeing how many pass under this project is a good benchmark for how correct your implementation is. |
So when is preview 4 expected to be available? |
Around 21-May |
One bit of API surface that's still noticeably missing is resources. |
The recommended replacement is to pass the resource blobs as |
@jkotas Sure, but where do you get "the resource blobs" from? There doesn't seem to be much information out there on the subject. Googling "c# create resource blobs" gives you a bunch of results about using Azure blob storage, and "c# create resources" gives you results about working with RESX files. |
Creating resource blobs in the context of .NET typically refers to embedding resources directly into your assembly as binary data. This could include images, XML files, localization strings, or any other data that you want to include with your application. |
@buyaa-n Could you please look into creating a sample for this? |
@jkotas Thanks, that's very helpful. And what about the unmanaged resources? It's not at all clear from the API or documentation how |
If you have the raw bytes, you can just write them out. Here is how it is done in Roslyn: https://github.com/dotnet/roslyn/blob/7a96b1530744f6637ad6bb6bf5b8dfa33ea15b81/src/Compilers/Core/Portable/PEWriter/PeWriter.cs#L431-L447 . The two offsets are for cases where the native resources have relocs. |
@jkotas OK, thanks! |
Added with dotnet/docs#40735 |
@masonwheeler and others, have you tried the PDB generation with the new PersistedAssemblyBuilder? How was it and any feedback so far? |
I gave it a quick shot on the first preview it came out, but was hoping an extension for the Save method would be added to behave like .NET Desktop. Not sure if something was added or if someone has made one. |
Are you talking about Instead that we added public sealed class PersistedAssemblyBuilder : AssemblyBuilder
{
public MetadataBuilder GenerateMetadata(out BlobBuilder ilStream, out BlobBuilder mappedFieldData);
public MetadataBuilder GenerateMetadata(out BlobBuilder ilStream, out BlobBuilder mappedFieldData, out MetadataBuilder pdbBuilder) { }
} See examples here: https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-reflection-emit-persistedassemblybuilder#emit-symbols-and-generate-pdb |
That is seriously complicated :D I was hoping for Hoping for too much? |
Well, we could add an API that cover the basic scenarios by defaulting assembly and PDB generation options. The question is which options should be defaulted and which options would be available for the user, somebody should file an issue for that as a start. Meanwhile you could create and use a helper method that does what that public static void Save(PersistedAssemblyBuilder ab, string assemblyFileName, bool emitDebugInfo)
{
MetadataBuilder metadataBuilder = ab.GenerateMetadata(out BlobBuilder ilStream, out _, out MetadataBuilder pdbBuilder);
BlobBuilder portablePdbBlob = new BlobBuilder();
PortablePdbBuilder portablePdbBuilder = new PortablePdbBuilder(pdbBuilder, metadataBuilder.GetRowCounts(), entryPoint: default);
BlobContentId pdbContentId = portablePdbBuilder.Serialize(portablePdbBlob);
using FileStream pdbFileStream = new FileStream($"{assemblyFileName}.pdb", FileMode.Create, FileAccess.Write);
portablePdbBlob.WriteContentTo(pdbFileStream);
DebugDirectoryBuilder debugDirectoryBuilder = new DebugDirectoryBuilder();
debugDirectoryBuilder.AddCodeViewEntry($"{assemblyFileName}.pdb", pdbContentId, portablePdbBuilder.FormatVersion);
ManagedPEBuilder peBuilder = new ManagedPEBuilder(
header: new PEHeaderBuilder(imageCharacteristics: Characteristics.ExecutableImage | Characteristics.Dll),
metadataRootBuilder: new MetadataRootBuilder(metadataBuilder),
ilStream: ilStream,
debugDirectoryBuilder: debugDirectoryBuilder);
BlobBuilder peBlob = new BlobBuilder();
peBuilder.Serialize(peBlob);
using var dllFileStream = new FileStream($"{assemblyFileName}.dll", FileMode.Create, FileAccess.Write);
peBlob.WriteContentTo(dllFileStream);
} |
Thanks, helped a lot. There were 2 issues, but seems to work from a small testcase.
Here is my current version's body (got some other stuff around it):
|
@buyaa-n Is there anything planned for embedded/manifest resources? Or is this something we need to add manually too with the PE builder? Not a deal breaker for me as I can just put it in initialized data. |
Nothing planned for now as user can add them manually, there is an instruction above: #92975 (comment) And a sample added in doc: https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-reflection-emit-persistedassemblybuilder#add-resources-with-persistedassemblybuilder |
@buyaa-n I think I have found an issue, but unfortunately I dont have a small repro. Steps for repro:
B references a MethodBuilderImpl in A. The module scopename refers to the correct filename, but Name says This scenario works correctly on .NET 2/4. And if I rememebr correctly it even allows for cyclic references where A and B can both reference each other. |
Thanks @leppie, there is no valid token populated until the assembly is saved, could you try:
With current design probably we could not support cyclic references |
Why not? #92975 (comment) sounds like a bug to me. The ref tokens are separate from the def token. We should be able to populated the ref token independently on the def token to allow creating cycles between assemblies. |
I will see what I can for fix. |
Not sure if this is related, but I feel it might be. This time I am trying to emit a cast to a type in a referenced assembly (same scenario as above).
From what @jkotas said, this should also be a ref and not a def token I think. Update: Debugged some more and it seems all my issues are related. Seems the modulebuilder only tracks assembly references from 'files' and anything referenced from another assemblybuilder is leading to unexpected behavior, ie exceptions, doing something wrong, etc. Hope you can fix this before final release. |
Can confirm there is no more outstanding issues for me. @buyaa-n if you interested in the result of your work: https://github.com/IronScheme/IronScheme/releases/tag/1.0.385 |
See #62956 for work done in .NET 8 for adding
AssemblyBuilder.Save(string/stream)
support. Remaining work include:ILGenerator
implementationConstructorBuilderImpl
and save it to file/stream (this should includeEmit(OpCode opcode, ConstructorInfo con)
implementation)PropertyBuilderImp
l and save it to file/streamEventBuildterImpl
and save it to file/streamAssemblyBuilder
/ModuleBuilder
(CreateGlobalFuntions
,DefineGlobalMethod
etc)MethodBuilder
/TypeBuilder
/FieldBuilder
(likeSignatureCallingConvention
,*RequiredCustomModifiers
,*OptionalCustomModifiers
)The text was updated successfully, but these errors were encountered: