using System.Collections.Concurrent; using System.Runtime.CompilerServices; using Lua.Internal; using Lua.Runtime; namespace Lua.CodeAnalysis.Compilation; public class ScopeCompilationContext : IDisposable { static class Pool { static ConcurrentStack stack = new(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ScopeCompilationContext Rent() { if (!stack.TryPop(out var context)) { context = new(); } return context; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Return(ScopeCompilationContext context) { context.Reset(); stack.Push(context); } } readonly Dictionary, LocalVariableDescription> localVariables = new(256, Utf16StringMemoryComparer.Default); readonly Dictionary, LabelDescription> labels = new(32, Utf16StringMemoryComparer.Default); byte lastLocalVariableIndex; public byte StackStartPosition { get; private set; } public byte StackPosition { get; set; } public byte StackTopPosition { get => (byte)(StackPosition - 1); } public bool HasCapturedLocalVariables { get; internal set; } /// /// Function context /// public FunctionCompilationContext Function { get; internal set; } = default!; /// /// Parent scope context /// public ScopeCompilationContext? Parent { get; private set; } public ScopeCompilationContext CreateChildScope() { var childScope = Pool.Rent(); childScope.Parent = this; childScope.Function = Function; childScope.StackStartPosition = StackPosition; childScope.StackPosition = StackPosition; return childScope; } public FunctionCompilationContext CreateChildFunction() { var context = FunctionCompilationContext.Create(this); return context; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PushInstruction(in Instruction instruction, SourcePosition position, bool incrementStackPosition = false) { Function.PushOrMergeInstruction(lastLocalVariableIndex, instruction, position, ref incrementStackPosition); if (incrementStackPosition) { StackPosition++; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void TryPushCloseUpValue(byte top, SourcePosition position) { if (HasCapturedLocalVariables && top != 0) { Function.PushInstruction(Instruction.Jmp(top, 0), position); } } /// /// Add new local variable. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AddLocalVariable(ReadOnlyMemory name, LocalVariableDescription description, bool markAsLastLocalVariable = true) { localVariables[name] = description; lastLocalVariableIndex = description.RegisterIndex; } /// /// Gets the local variable in scope. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryGetLocalVariable(ReadOnlyMemory name, out LocalVariableDescription description) { if (localVariables.TryGetValue(name, out description)) return true; // Find local variables defined in the same function if (Parent != null) { return Parent.TryGetLocalVariable(name, out description); } return false; } /// /// Gets the local variable in this scope. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryGetLocalVariableInThisScope(ReadOnlyMemory name, out LocalVariableDescription description) { return localVariables.TryGetValue(name, out description); } /// /// Add new label. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AddLabel(LabelDescription description) { labels.Add(description.Name, description); } /// /// Gets the label in scope. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryGetLabel(ReadOnlyMemory name, out LabelDescription description) { if (labels.TryGetValue(name, out description)) return true; // Find labels defined in the same function if (Parent != null) { return Parent.TryGetLabel(name, out description); } return false; } /// /// Resets the values ​​held in the context. /// public void Reset() { Parent = null; StackStartPosition = 0; StackPosition = 0; HasCapturedLocalVariables = false; localVariables.Clear(); labels.Clear(); lastLocalVariableIndex = 0; } /// /// Returns the context object to the pool. /// public void Dispose() { Function = null!; Pool.Return(this); } }