Finish initialising class of the main method before execution (#59)

This commit is contained in:
Patrick Stevens
2025-06-20 14:39:06 +01:00
committed by GitHub
parent 5a7cf11a5f
commit c620152fb1
3 changed files with 60 additions and 24 deletions

View File

@@ -527,7 +527,7 @@ module DumpedAssembly =
(baseTypeInfo : BaseTypeInfo option)
: ResolvedBaseType
=
let rec go (baseType : BaseTypeInfo option) =
let rec go (source : AssemblyName) (baseType : BaseTypeInfo option) =
match baseType with
| Some (BaseTypeInfo.TypeRef r) ->
let assy = loadedAssemblies.[source.FullName]
@@ -539,7 +539,7 @@ module DumpedAssembly =
| TypeResolutionResult.Resolved (assy, typeInfo) ->
match TypeInfo.isBaseType bct _.Name assy.Name typeInfo.TypeDefHandle with
| Some v -> v
| None -> go typeInfo.BaseType
| None -> go assy.Name typeInfo.BaseType
| Some (BaseTypeInfo.ForeignAssemblyType (assy, ty)) ->
let assy = loadedAssemblies.[assy.FullName]
@@ -547,7 +547,7 @@ module DumpedAssembly =
| Some v -> v
| None ->
let ty = assy.TypeDefs.[ty]
go ty.BaseType
go assy.Name ty.BaseType
| Some (BaseTypeInfo.TypeSpec _) -> failwith "TODO"
| Some (BaseTypeInfo.TypeDef h) ->
let assy = loadedAssemblies.[source.FullName]
@@ -556,7 +556,7 @@ module DumpedAssembly =
| Some v -> v
| None ->
let ty = assy.TypeDefs.[h]
go ty.BaseType
go assy.Name ty.BaseType
| None -> ResolvedBaseType.Object
go baseTypeInfo
go source baseTypeInfo

View File

@@ -142,6 +142,17 @@ type MethodInstructions =
ExceptionRegions : ImmutableArray<ExceptionRegion>
}
static member OnlyRet : MethodInstructions =
let op = IlOp.Nullary NullaryIlOp.Ret
{
Instructions = [ op, 0 ]
Locations = Map.empty |> Map.add 0 op
LocalsInit = false
LocalVars = None
ExceptionRegions = ImmutableArray.Empty
}
/// <summary>
/// Represents detailed information about a method in a .NET assembly.
/// This is a strongly-typed representation of MethodDefinition from System.Reflection.Metadata.

View File

@@ -40,6 +40,29 @@ module Program =
arrayAllocation, state
let rec pumpToReturn
(loggerFactory : ILoggerFactory)
(logger : ILogger)
(baseClassTypes : BaseClassTypes<DumpedAssembly>)
impls
(mainThread : ThreadId)
(state : IlMachineState)
: IlMachineState * ThreadId
=
match AbstractMachine.executeOneStep loggerFactory impls baseClassTypes state mainThread with
| ExecutionResult.Terminated (state, terminatingThread) -> state, terminatingThread
| ExecutionResult.Stepped (state', whatWeDid) ->
match whatWeDid with
| WhatWeDid.Executed ->
logger.LogInformation $"Executed one step; active assembly: {state'.ActiveAssembly(mainThread).Name.Name}"
| WhatWeDid.SuspendedForClassInit ->
logger.LogInformation "Suspended execution of current method for class initialisation."
| WhatWeDid.BlockedOnClassInit threadBlockingUs ->
logger.LogInformation "Unable to execute because class has not yet initialised."
pumpToReturn loggerFactory logger baseClassTypes impls mainThread state'
/// Returns the abstract machine's state at the end of execution, together with the thread which
/// caused execution to end.
let run
@@ -83,7 +106,7 @@ module Program =
dumped
// pretend there are no instructions, so we avoid preparing anything
{ mainMethod with
Instructions = None
Instructions = Some MethodInstructions.OnlyRet
}
None
(ImmutableArray.CreateRange [ CliType.ObjectRef None ])
@@ -153,7 +176,14 @@ module Program =
| TypeDefn.PrimitiveType PrimitiveType.Int32 -> ()
| _ -> failwith "Main method must return int32; other types not currently supported"
// Now that BCL initialisation has taken place, overwrite the main thread completely.
// We might be in the middle of class construction. Pump the static constructors to completion.
// We haven't yet entered the main method!
let state, _ =
pumpToReturn loggerFactory logger baseClassTypes impls mainThread state
// Now that BCL initialisation has taken place and the user-code classes are constructed,
// overwrite the main thread completely.
let methodState =
match
MethodState.Empty
@@ -171,27 +201,22 @@ module Program =
let threadState =
{ state.ThreadState.[mainThread] with
MethodStates = ImmutableArray.Create methodState
ActiveMethodState = 0
}
let state =
let state, init =
{ state with
ThreadState = state.ThreadState |> Map.add mainThread threadState
}
|> IlMachineState.ensureTypeInitialised
loggerFactory
baseClassTypes
mainThread
methodState.ExecutingMethod.DeclaringType
let rec go (state : IlMachineState) =
match AbstractMachine.executeOneStep loggerFactory impls baseClassTypes state mainThread with
| ExecutionResult.Terminated (state, terminatingThread) -> state, terminatingThread
| ExecutionResult.Stepped (state', whatWeDid) ->
match init with
| WhatWeDid.SuspendedForClassInit -> failwith "TODO: suspended for class init"
| WhatWeDid.BlockedOnClassInit _ -> failwith "logic error: surely this thread can't be blocked on class init"
| WhatWeDid.Executed -> ()
match whatWeDid with
| WhatWeDid.Executed ->
logger.LogInformation
$"Executed one step; active assembly: {state'.ActiveAssembly(mainThread).Name.Name}"
| WhatWeDid.SuspendedForClassInit ->
logger.LogInformation "Suspended execution of current method for class initialisation."
| WhatWeDid.BlockedOnClassInit threadBlockingUs ->
logger.LogInformation "Unable to execute because class has not yet initialised."
go state'
go state
pumpToReturn loggerFactory logger baseClassTypes impls mainThread state