diff --git a/src/ChibiRuby.Compiler/CompilationResult.cs b/src/ChibiRuby.Compiler/CompilationResult.cs index fc44290..65c48b5 100644 --- a/src/ChibiRuby.Compiler/CompilationResult.cs +++ b/src/ChibiRuby.Compiler/CompilationResult.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; -namespace ChibiRuby.Compiler -{ +namespace ChibiRuby.Compiler; + public class CompilationResult : IDisposable { public IReadOnlyList Diagnostics { get; } @@ -10,7 +10,6 @@ public class CompilationResult : IDisposable public bool HasError => contextHandle.HasError; readonly MRubyState mrb; - readonly MrbStateHandle stateHandle; readonly MrcCContextHandle contextHandle; readonly IntPtr bytecodeDataPtr; readonly int bytecodeLength; @@ -18,26 +17,20 @@ public class CompilationResult : IDisposable internal CompilationResult( MRubyState mrb, - MrbStateHandle stateHandle, MrcCContextHandle contextHandle, IntPtr bytecodeDataPtr, int bytecodeLength) { this.mrb = mrb; - this.stateHandle = stateHandle; this.contextHandle = contextHandle; this.bytecodeDataPtr = bytecodeDataPtr; this.bytecodeLength = bytecodeLength; Diagnostics = contextHandle.GetDiagnostics(); } - internal CompilationResult( - MRubyState mrb, - MrbStateHandle stateHandle, - MrcCContextHandle contextHandle) + internal CompilationResult(MRubyState mrb, MrcCContextHandle contextHandle) { this.mrb = mrb; - this.stateHandle = stateHandle; this.contextHandle = contextHandle; Diagnostics = contextHandle.GetDiagnostics(); } @@ -89,5 +82,4 @@ unsafe void Dispose(bool disposing) contextHandle.Dispose(); disposed = true; } -} -} +} \ No newline at end of file diff --git a/src/ChibiRuby.Compiler/MRubyCompiler.cs b/src/ChibiRuby.Compiler/MRubyCompiler.cs index a787387..fce2543 100644 --- a/src/ChibiRuby.Compiler/MRubyCompiler.cs +++ b/src/ChibiRuby.Compiler/MRubyCompiler.cs @@ -11,6 +11,8 @@ public class MRubyCompileException(string message) : Exception(message); public record MRubyCompileOptions { public static MRubyCompileOptions Default { get; set; } = new(); + + public bool EnableDebugInfo { get; set; } = true; } public class MRubyCompiler : IDisposable @@ -21,19 +23,19 @@ public static MRubyCompiler Create(MRubyState mrb, MRubyCompileOptions? options return new MRubyCompiler(mrb, compilerStateHandle, options); } - public MRubyState State => mruby; + public MRubyState State => mrubyState; - readonly MRubyState mruby; + readonly MRubyState mrubyState; readonly MrbStateHandle compileStateHandle; readonly MRubyCompileOptions options; bool disposed; MRubyCompiler( - MRubyState mruby, + MRubyState mrubyState, MrbStateHandle compileStateHandle, MRubyCompileOptions? options = null) { - this.mruby = mruby; + this.mrubyState = mrubyState; this.compileStateHandle = compileStateHandle; this.options = options ?? MRubyCompileOptions.Default; } @@ -46,19 +48,22 @@ public static MRubyCompiler Create(MRubyState mrb, MRubyCompileOptions? options public MRubyValue LoadSourceCodeFile(string path) { using var compilation = CompileFile(path); - return mruby.LoadBytecode(compilation.AsBytecode()); + return mrubyState.LoadBytecode(compilation.AsBytecode()); } - public async Task LoadSourceCodeFileAsync(string path, CancellationToken cancellationToken = default) + public async Task LoadSourceCodeFileAsync( + string path, + MRubyCompileOptions? options = null, + CancellationToken? cancellationToken = default) { - using var compilation = await CompileFileAsync(path, cancellationToken); - return mruby.LoadBytecode(compilation.AsBytecode()); + using var compilation = await CompileFileAsync(path, options, cancellationToken.GetValueOrDefault()); + return mrubyState.LoadBytecode(compilation.AsBytecode()); } public MRubyValue LoadSourceCode(ReadOnlySpan utf8Source) { using var compilation = Compile(utf8Source); - return mruby.LoadBytecode(compilation.AsBytecode()); + return mrubyState.LoadBytecode(compilation.AsBytecode()); } public MRubyValue LoadSourceCode(string source) @@ -70,8 +75,8 @@ public MRubyValue LoadSourceCode(string source) public RFiber LoadSourceCodeAsFiber(ReadOnlySpan utf8Source) { using var compilation = Compile(utf8Source); - var proc = mruby.CreateProc(compilation.ToIrep()); - return mruby.CreateFiber(proc); + var proc = mrubyState.CreateProc(compilation.ToIrep()); + return mrubyState.CreateFiber(proc); } public RFiber LoadSourceCodeAsFiber(string source) @@ -80,50 +85,41 @@ public RFiber LoadSourceCodeAsFiber(string source) return LoadSourceCodeAsFiber(utf8Source); } - public CompilationResult CompileFile(string filePath, bool debugInfo = true) + public CompilationResult CompileFile(string filePath, MRubyCompileOptions? options = null) { + options ??= this.options; var bytes = File.ReadAllBytes(filePath); - return Compile(bytes, - filename: Path.GetFullPath(filePath), - debugInfo: debugInfo); + return Compile(bytes, filename: Path.GetFullPath(filePath), options); } - public async Task CompileFileAsync(string filePath, CancellationToken cancellationToken = default, bool debugInfo = true) + public async Task CompileFileAsync( + string filePath, + MRubyCompileOptions? options = null, + CancellationToken cancellationToken = default) { + options ??= this.options; var bytes = await File.ReadAllBytesAsync(filePath, cancellationToken); - return Compile(bytes, - filename: Path.GetFullPath(filePath), - debugInfo: debugInfo); + return Compile(bytes, filename: Path.GetFullPath(filePath), options); } - public CompilationResult Compile(string sourceCode, string? filename = null, bool debugInfo = true) => - Compile(Encoding.UTF8.GetBytes(sourceCode), filename, debugInfo); + public CompilationResult Compile(string sourceCode, string? filename = null, MRubyCompileOptions? options = null) => + Compile(Encoding.UTF8.GetBytes(sourceCode), filename, options); /// /// Compile Ruby source to .mrb bytecode. /// - /// UTF-8 encoded source bytes. - /// - /// Optional source file name to record in the bytecode's DBG section. When non-null - /// (and is true), tools like the debugger / Backtrace - /// resolve pc back to filename:line. - /// - /// - /// When true (default), the bytecode includes a DBG section with line numbers. Set - /// to false to produce smaller bytecode for production distribution. - /// public unsafe CompilationResult Compile( ReadOnlySpan utf8Source, string? filename = null, - bool debugInfo = true) + MRubyCompileOptions? options = null) { // Workaround for the crash that occurs when passing a blank to mrc if (utf8Source.IsEmpty) { Span fallback = stackalloc byte[1]; fallback[0] = (byte)' '; - return Compile(fallback, filename, debugInfo); + return Compile(fallback, filename, options); } if (BomHelper.TryDetectEncoding(utf8Source, out var encoding)) @@ -158,9 +154,10 @@ public unsafe CompilationResult Compile( } } + options ??= this.options; // MRB_DUMP_DEBUG_INFO == 1; include the DBG section in the serialized .mrb so // the C# RiteParser can recover (file, line) info for each pc. - var dumpFlags = (byte)(debugInfo ? 1 : 0); + var dumpFlags = (byte)(options.EnableDebugInfo ? 1 : 0); fixed (byte* sourcePtr = utf8Source) { @@ -168,11 +165,11 @@ public unsafe CompilationResult Compile( if (irepPtr == null || context.HasError) { // error - return new CompilationResult(mruby, compileStateHandle, context); + return new CompilationResult(mrubyState, context); } NativeMethods.MrcDumpIrep(context.DangerousGetPtr(), irepPtr, dumpFlags, &bin, &binLength); NativeMethods.MrcIrepFree(context.DangerousGetPtr(), irepPtr); - return new CompilationResult(mruby, compileStateHandle, context, (IntPtr)bin, (int)binLength); + return new CompilationResult(mrubyState, context, (IntPtr)bin, (int)binLength); } } @@ -190,4 +187,4 @@ public void Dispose() GC.SuppressFinalize(this); } } -} \ No newline at end of file +} diff --git a/src/ChibiRuby.Compiler/MRubyStateExtensions.cs b/src/ChibiRuby.Compiler/MRubyStateExtensions.cs new file mode 100644 index 0000000..dd196aa --- /dev/null +++ b/src/ChibiRuby.Compiler/MRubyStateExtensions.cs @@ -0,0 +1,6 @@ +namespace ChibiRuby.Compiler; + +public static class MRubyStateExtensions +{ + +} \ No newline at end of file diff --git a/src/ChibiRuby.Compiler/MrbStateHandle.cs b/src/ChibiRuby.Compiler/MrbStateHandle.cs index 3aa18c3..8ed86dc 100644 --- a/src/ChibiRuby.Compiler/MrbStateHandle.cs +++ b/src/ChibiRuby.Compiler/MrbStateHandle.cs @@ -1,14 +1,10 @@ using System; using System.Runtime.InteropServices; -namespace ChibiRuby.Compiler -{ -class MrbStateHandle : SafeHandle -{ - public MrbStateHandle(IntPtr invalidHandleValue) : base(invalidHandleValue, true) - { - } +namespace ChibiRuby.Compiler; +class MrbStateHandle(IntPtr invalidHandleValue) : SafeHandle(invalidHandleValue, true) +{ public static unsafe MrbStateHandle Create() { var ptr = NativeMethods.MrbOpen(); @@ -25,5 +21,4 @@ protected override unsafe bool ReleaseHandle() NativeMethods.MrbClose(DangerousGetPtr()); return true; } -} -} +} \ No newline at end of file diff --git a/src/ChibiRuby.Compiler/MrcCContextHandle.cs b/src/ChibiRuby.Compiler/MrcCContextHandle.cs index a52d307..37789d2 100644 --- a/src/ChibiRuby.Compiler/MrcCContextHandle.cs +++ b/src/ChibiRuby.Compiler/MrcCContextHandle.cs @@ -2,53 +2,49 @@ using System.Collections.Generic; using System.Runtime.InteropServices; -namespace ChibiRuby.Compiler +namespace ChibiRuby.Compiler; + +class MrcCContextHandle(IntPtr invalidHandleValue) : SafeHandle(invalidHandleValue, true) { - class MrcCContextHandle : SafeHandle + public static unsafe MrcCContextHandle Create(MrbStateHandle mrbStateHandle) { - public static unsafe MrcCContextHandle Create(MrbStateHandle mrbStateHandle) - { - var ptr = NativeMethods.MrcCContextNew(mrbStateHandle.DangerousGetPtr()); - return new MrcCContextHandle((IntPtr)ptr); - } - - public override bool IsInvalid => handle == IntPtr.Zero; + var ptr = NativeMethods.MrcCContextNew(mrbStateHandle.DangerousGetPtr()); + return new MrcCContextHandle((IntPtr)ptr); + } - public unsafe bool HasError => (DangerousGetPtr()->Flags & 0x01) != 0; + public override bool IsInvalid => handle == IntPtr.Zero; - public MrcCContextHandle(IntPtr invalidHandleValue) : base(invalidHandleValue, true) - { - } + public unsafe bool HasError => (DangerousGetPtr()->Flags & 0x01) != 0; - public unsafe MrcCContext* DangerousGetPtr() => (MrcCContext*)DangerousGetHandle(); + public unsafe MrcCContext* DangerousGetPtr() => (MrcCContext*)DangerousGetHandle(); - protected override unsafe bool ReleaseHandle() - { - if (IsClosed) return false; - NativeMethods.MrcCContextFree(DangerousGetPtr()); - return true; - } + protected override unsafe bool ReleaseHandle() + { + if (IsClosed) return false; + NativeMethods.MrcCContextFree(DangerousGetPtr()); + return true; + } - public unsafe IReadOnlyList GetDiagnostics() + public unsafe IReadOnlyList GetDiagnostics() + { + var list = new List(); + var nodePtr = DangerousGetPtr()->DiagnosticList; + while (nodePtr != null) { - var list = new List(); - var nodePtr = DangerousGetPtr()->DiagnosticList; - while (nodePtr != null) + var severity = nodePtr->DiagnosticCode switch { - var severity = nodePtr->DiagnosticCode switch - { - MrcDiagnosticCode.Warning => DiagnosticSeverity.Warning, - MrcDiagnosticCode.Error => DiagnosticSeverity.Error, - MrcDiagnosticCode.GeneratorWarning => DiagnosticSeverity.GeneratorWarning, - MrcDiagnosticCode.GeneratorError => DiagnosticSeverity.GeneratorError, - _ => throw new ArgumentOutOfRangeException() - }; - var message = Marshal.PtrToStringUTF8((IntPtr)nodePtr->Message); - var descriptor = new DiagnosticsDescriptor(severity, nodePtr->Line, nodePtr->Column, message); - list.Add(descriptor); - nodePtr = nodePtr->Next; - } - return list; + MrcDiagnosticCode.Warning => DiagnosticSeverity.Warning, + MrcDiagnosticCode.Error => DiagnosticSeverity.Error, + MrcDiagnosticCode.GeneratorWarning => DiagnosticSeverity.GeneratorWarning, + MrcDiagnosticCode.GeneratorError => DiagnosticSeverity.GeneratorError, + _ => throw new ArgumentOutOfRangeException() + }; + var message = Marshal.PtrToStringUTF8((IntPtr)nodePtr->Message); + var descriptor = new DiagnosticsDescriptor(severity, nodePtr->Line, nodePtr->Column, message); + list.Add(descriptor); + nodePtr = nodePtr->Next; } + + return list; } -} \ No newline at end of file +} diff --git a/tests/ChibiRuby.Tests/IrepDebugInfoTest.cs b/tests/ChibiRuby.Tests/IrepDebugInfoTest.cs index cbdac06..5590882 100644 --- a/tests/ChibiRuby.Tests/IrepDebugInfoTest.cs +++ b/tests/ChibiRuby.Tests/IrepDebugInfoTest.cs @@ -41,7 +41,10 @@ public void CompileWithDebugInfo_PopulatesIrepDebugInfo() [Test] public void CompileWithoutDebugInfo_DoesNotProduceIrepDebugInfo() { - using var c = compiler.Compile("x = 1\nx"u8, filename: "test.rb", debugInfo: false); + using var c = compiler.Compile("x = 1\nx"u8, filename: "test.rb", new MRubyCompileOptions + { + EnableDebugInfo = false + }); var irep = c.ToIrep(); Assert.That(irep.DebugInfo, Is.Null, "DebugInfo should be omitted when debugInfo=false"); }