Delegates (#50)

This commit is contained in:
Patrick Stevens
2025-06-15 19:15:58 +01:00
committed by GitHub
parent a0ee1f9713
commit 4be7317c22
6 changed files with 92 additions and 27 deletions

View File

@@ -1,6 +1,5 @@
using System;
using System.IO;
using System.Threading;
using System.Collections.Generic;
using System.Linq;
namespace HelloWorldApp
{
@@ -8,18 +7,12 @@ namespace HelloWorldApp
{
static int Main(string[] args)
{
object locker = new object();
bool lockTaken = false;
try
{
Monitor.Enter(locker, ref lockTaken);
return 1;
}
finally
{
if (lockTaken)
Monitor.Exit(locker);
}
var l = new List<int>();
l.Add(3);
l.Add(100);
var m = l.Select(x => x.ToString()).ToList();
// 2 + 103 + (1 + 3) = 109
return m.Count + l.Sum() + m.Select(x => x.Length).Sum();
}
}
}

View File

@@ -37,8 +37,17 @@ module AbstractMachine =
targetAssy.Name
targetType.BaseType
match baseType with
| ResolvedBaseType.Delegate -> failwith "TODO: need to generate code for delegates"
match baseType, instruction.ReturnState with
| ResolvedBaseType.Delegate,
Some {
WasConstructingObj = Some _
} ->
IlMachineState.executeDelegateConstructor instruction state
// can't advance the program counter here - there's no IL instructions executing!
|> IlMachineState.returnStackFrame loggerFactory baseClassTypes thread
|> Option.get
|> Tuple.withRight WhatWeDid.Executed
|> ExecutionResult.Stepped
| _ ->
let outcome =

View File

@@ -63,7 +63,7 @@ type EvalStackValue =
| EvalStackValue.Int64 i -> $"Int64(%i{i})"
| EvalStackValue.NativeInt src -> $"NativeInt(%O{src})"
| EvalStackValue.Float f -> $"Float(%f{f})"
| EvalStackValue.ManagedPointer managedPointerSource -> failwith "todo"
| EvalStackValue.ManagedPointer managedPointerSource -> $"Pointer(%O{managedPointerSource})"
| EvalStackValue.ObjectRef managedHeapAddress -> $"ObjectRef(%O{managedHeapAddress})"
| EvalStackValue.UserDefinedValueType evalStackValues -> failwith "todo"

View File

@@ -1149,3 +1149,46 @@ module IlMachineState =
let getSyncBlock (addr : ManagedHeapAddress) (state : IlMachineState) : SyncBlock =
state.ManagedHeap |> ManagedHeap.GetSyncBlock addr
let executeDelegateConstructor (instruction : MethodState) (state : IlMachineState) : IlMachineState =
// We've been called with arguments already popped from the stack into local arguments.
let constructing = instruction.Arguments.[2]
let methodPtr = instruction.Arguments.[1]
let targetObj = instruction.Arguments.[0]
let targetObj =
match targetObj with
| CliType.ObjectRef target -> target
| _ -> failwith $"Unexpected target type for delegate: {targetObj}"
let constructing =
match constructing with
| CliType.ObjectRef None -> failwith "unexpectedly constructing the null delegate"
| CliType.ObjectRef (Some target) -> target
| _ -> failwith $"Unexpectedly not constructing a managed object: {constructing}"
let heapObj =
match state.ManagedHeap.NonArrayObjects.TryGetValue constructing with
| true, obj -> obj
| false, _ -> failwith $"Delegate object {constructing} not found on heap"
// Standard delegate fields in .NET are _target and _methodPtr
// Update the fields with the target object and method pointer
let updatedFields =
heapObj.Fields
|> Map.add "_target" (CliType.ObjectRef targetObj)
|> Map.add "_methodPtr" methodPtr
let updatedObj =
{ heapObj with
Fields = updatedFields
}
let updatedHeap =
{ state.ManagedHeap with
NonArrayObjects = state.ManagedHeap.NonArrayObjects |> Map.add constructing updatedObj
}
{ state with
ManagedHeap = updatedHeap
}

View File

@@ -52,9 +52,10 @@ and MethodState =
MethodState.setProgramCounter (state._IlOpIndex + bytes) state
static member advanceProgramCounter (state : MethodState) =
MethodState.jumpProgramCounter
(IlOp.NumberOfBytes state.ExecutingMethod.Instructions.Value.Locations.[state.IlOpIndex])
state
let instruction =
state.ExecutingMethod.Instructions.Value.Locations.[state.IlOpIndex]
MethodState.jumpProgramCounter (IlOp.NumberOfBytes instruction) state
static member peekEvalStack (state : MethodState) : EvalStackValue option = EvalStack.Peek state.EvaluationStack

View File

@@ -303,19 +303,38 @@ module internal UnaryMetadataIlOp =
| Box -> failwith "TODO: Box unimplemented"
| Ldelema -> failwith "TODO: Ldelema unimplemented"
| Isinst ->
let targetType =
match metadataToken with
| MetadataToken.TypeDefinition td -> state.ActiveAssembly(thread).TypeDefs.[td]
| m -> failwith $"unexpected metadata token {m} in IsInst"
let actualObj, state = IlMachineState.popEvalStack thread state
let targetType : TypeDefn =
match metadataToken with
| MetadataToken.TypeDefinition td ->
TypeDefn.FromDefinition (
ComparableTypeDefinitionHandle.Make td,
state.ActiveAssembly(thread).Name.FullName,
failwith "TODO"
)
| MetadataToken.TypeSpecification handle -> state.ActiveAssembly(thread).TypeSpecs.[handle].Signature
| m -> failwith $"unexpected metadata token {m} in IsInst"
let returnObj =
match actualObj with
| EvalStackValue.ManagedPointer ManagedPointerSource.Null ->
// null IsInstance check always succeeds and results in a null reference
EvalStackValue.ManagedPointer ManagedPointerSource.Null
| v -> failwith $"TODO: %O{v}"
| EvalStackValue.ManagedPointer (ManagedPointerSource.LocalVariable _) -> failwith "TODO"
| EvalStackValue.ManagedPointer (ManagedPointerSource.Heap addr) ->
match state.ManagedHeap.NonArrayObjects.TryGetValue addr with
| true, v ->
{ new TypeInfoEval<_> with
member _.Eval typeInfo = failwith "TODO"
}
|> v.Type.Apply
| false, _ ->
match state.ManagedHeap.Arrays.TryGetValue addr with
| true, v -> failwith "TODO"
| false, _ -> failwith $"could not find managed object with address {addr}"
| esv -> failwith $"TODO: {esv}"
let state =
state