Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions Compilation/ILCompiler.ArrowFunctions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ private void CollectAndDefineArrowFunctions(List<Stmt> statements)
if (arrow.IsObjectMethod)
{
paramTypes = new Type[arrow.Parameters.Count + 1];
paramTypes[0] = typeof(object); // __this
paramTypes[0] = _types.Object; // __this
for (int i = 0; i < arrow.Parameters.Count; i++)
paramTypes[i + 1] = typeof(object);
paramTypes[i + 1] = _types.Object;
}
else
{
paramTypes = arrow.Parameters.Select(_ => typeof(object)).ToArray();
paramTypes = arrow.Parameters.Select(_ => _types.Object).ToArray();
}

if (captures.Count == 0)
Expand All @@ -42,7 +42,7 @@ private void CollectAndDefineArrowFunctions(List<Stmt> statements)
var methodBuilder = _programType.DefineMethod(
$"<>Arrow_{_arrowMethodCounter++}",
MethodAttributes.Private | MethodAttributes.Static,
typeof(object),
_types.Object,
paramTypes
);

Expand All @@ -67,14 +67,14 @@ private void CollectAndDefineArrowFunctions(List<Stmt> statements)
var displayClass = _moduleBuilder.DefineType(
$"<>c__DisplayClass{_displayClassCounter++}",
TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit,
typeof(object)
_types.Object
);

// Add fields for captured variables
Dictionary<string, FieldBuilder> fieldMap = [];
foreach (var capturedVar in captures)
{
var field = displayClass.DefineField(capturedVar, typeof(object), FieldAttributes.Public);
var field = displayClass.DefineField(capturedVar, _types.Object, FieldAttributes.Public);
fieldMap[capturedVar] = field;
}
_displayClassFields[arrow] = fieldMap;
Expand All @@ -87,15 +87,15 @@ private void CollectAndDefineArrowFunctions(List<Stmt> statements)
);
var ctorIL = ctorBuilder.GetILGenerator();
ctorIL.Emit(OpCodes.Ldarg_0);
ctorIL.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes)!);
ctorIL.Emit(OpCodes.Call, _types.GetDefaultConstructor(_types.Object));
ctorIL.Emit(OpCodes.Ret);
_displayClassConstructors[arrow] = ctorBuilder;

// Add Invoke method
var invokeMethod = displayClass.DefineMethod(
"Invoke",
MethodAttributes.Public,
typeof(object),
_types.Object,
paramTypes
);

Expand Down
6 changes: 3 additions & 3 deletions Compilation/ILCompiler.Functions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ private void EmitDefaultEntryPoint(List<Stmt> statements)
var mainMethod = _programType.DefineMethod(
"Main",
MethodAttributes.Public | MethodAttributes.Static,
typeof(void),
_types.Void,
Type.EmptyTypes
);

Expand Down Expand Up @@ -393,8 +393,8 @@ private void EmitExeEntryPointWithUserMain(List<Stmt> statements, Stmt.Function
var mainMethod = _programType.DefineMethod(
"Main",
MethodAttributes.Public | MethodAttributes.Static,
typeof(void),
[typeof(string[])] // Accept string[] args from .NET runtime
_types.Void,
[_types.StringArray] // Accept string[] args from .NET runtime
);

_entryPoint = mainMethod;
Expand Down
10 changes: 7 additions & 3 deletions Compilation/ILEmitter.Calls.Closures.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,10 @@ private void EmitCapturingArrowFunction(Expr.ArrowFunction af, MethodBuilder met
if (!_ctx.DisplayClassFields.TryGetValue(af, out var fieldMap))
{
// No fields to populate, just create TSFunction
// Use two-argument GetMethodFromHandle for display class methods
IL.Emit(OpCodes.Ldtoken, method);
IL.Emit(OpCodes.Call, _ctx.Types.GetMethod(_ctx.Types.MethodBase, "GetMethodFromHandle", _ctx.Types.RuntimeMethodHandle));
IL.Emit(OpCodes.Ldtoken, displayClass);
IL.Emit(OpCodes.Call, _ctx.Types.GetMethod(_ctx.Types.MethodBase, "GetMethodFromHandle", _ctx.Types.RuntimeMethodHandle, _ctx.Types.RuntimeTypeHandle));
IL.Emit(OpCodes.Castclass, _ctx.Types.MethodInfo);
IL.Emit(OpCodes.Newobj, _ctx.Runtime!.TSFunctionCtor);
return;
Expand Down Expand Up @@ -131,9 +133,11 @@ private void EmitCapturingArrowFunction(Expr.ArrowFunction af, MethodBuilder met
// Create TSFunction: new TSFunction(displayInstance, method)
// Stack has: displayInstance

// Load method info
// Load method info - use two-argument GetMethodFromHandle for display class methods
// This is required because the method's parameter types need the declaring type context to resolve
IL.Emit(OpCodes.Ldtoken, method);
IL.Emit(OpCodes.Call, _ctx.Types.GetMethod(_ctx.Types.MethodBase, "GetMethodFromHandle", _ctx.Types.RuntimeMethodHandle));
IL.Emit(OpCodes.Ldtoken, displayClass);
IL.Emit(OpCodes.Call, _ctx.Types.GetMethod(_ctx.Types.MethodBase, "GetMethodFromHandle", _ctx.Types.RuntimeMethodHandle, _ctx.Types.RuntimeTypeHandle));
IL.Emit(OpCodes.Castclass, _ctx.Types.MethodInfo);

// Call $TSFunction constructor
Expand Down
3 changes: 3 additions & 0 deletions Compilation/ILEmitter.Calls.cs
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,9 @@ private void EmitFunctionValueCall(Expr.Call c)
// Emit the callee (should produce a TSFunction on the stack)
EmitExpression(c.Callee);

// Cast to TSFunction - needed for IL verification when the callee is stored in an object-typed local
IL.Emit(OpCodes.Castclass, _ctx.Runtime!.TSFunctionType);

// Check if any argument is a spread
bool hasSpreads = c.Arguments.Any(a => a is Expr.Spread);

Expand Down
21 changes: 21 additions & 0 deletions Compilation/ILEmitter.Expressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,27 @@ protected override void EmitAssign(Expr.Assign a)
IL.Emit(OpCodes.Starg, argIndex);
SetStackUnknown();
}
else if (_ctx.CapturedFields?.TryGetValue(a.Name.Lexeme, out var field) == true)
{
// Captured field in display class (closure)
EmitBoxIfNeeded(a.Value);
IL.Emit(OpCodes.Dup);
// Store to field: need temp since value is on top of stack
var temp = IL.DeclareLocal(_ctx.Types.Object);
IL.Emit(OpCodes.Stloc, temp);
IL.Emit(OpCodes.Ldarg_0); // Load display class instance
IL.Emit(OpCodes.Ldloc, temp);
IL.Emit(OpCodes.Stfld, field);
SetStackUnknown();
}
else if (_ctx.TopLevelStaticVars?.TryGetValue(a.Name.Lexeme, out var topLevelField) == true)
{
// Top-level static variable
EmitBoxIfNeeded(a.Value);
IL.Emit(OpCodes.Dup);
IL.Emit(OpCodes.Stsfld, topLevelField);
SetStackUnknown();
}
else
{
// Unknown target - box for safety
Expand Down
6 changes: 5 additions & 1 deletion Compilation/ILVerifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,11 @@ public List<string> Verify(Stream assemblyStream)
{
var typeName = GetTypeName(metadataReader, method.GetDeclaringType());
var methodName = metadataReader.GetString(method.Name);
errors.Add($"[IL Error] {typeName}.{methodName}: {result.Message}");
// Include error code and any additional args for debugging
var argsStr = result.Args != null && result.Args.Length > 0
? $" [{string.Join(", ", result.Args)}]"
: "";
errors.Add($"[IL Error] {typeName}.{methodName}: {result.Code}{argsStr} - {result.Message}");
}
}
catch (Exception ex)
Expand Down
5 changes: 4 additions & 1 deletion Compilation/RuntimeEmitter.Arrays.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,14 @@ private void EmitGetElement(TypeBuilder typeBuilder, EmittedRuntime runtime)
il.Emit(OpCodes.Ret);

il.MarkLabel(stringLabel);
var charLocal = il.DeclareLocal(_types.Char);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Castclass, _types.String);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Callvirt, _types.GetMethod(_types.String, "get_Chars", _types.Int32));
il.Emit(OpCodes.Call, _types.GetMethod(_types.Char, "ToString", _types.EmptyTypes));
il.Emit(OpCodes.Stloc, charLocal);
il.Emit(OpCodes.Ldloca, charLocal);
il.Emit(OpCodes.Call, _types.GetMethodNoParams(_types.Char, "ToString"));
il.Emit(OpCodes.Ret);
}

Expand Down
8 changes: 5 additions & 3 deletions Compilation/RuntimeEmitter.CoreUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -223,17 +223,19 @@ private void EmitToNumber(TypeBuilder typeBuilder, EmittedRuntime runtime)
runtime.ToNumber = method;

var il = method.GetILGenerator();
var resultLocal = il.DeclareLocal(_types.Double);

// Use Convert.ToDouble with try-catch fallback to NaN
il.BeginExceptionBlock();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, _types.GetMethod(_types.Convert, "ToDouble", _types.Object));
var endLabel = il.DefineLabel();
il.Emit(OpCodes.Br, endLabel);
il.Emit(OpCodes.Stloc, resultLocal);
il.BeginCatchBlock(_types.Exception);
il.Emit(OpCodes.Pop);
il.Emit(OpCodes.Ldc_R8, double.NaN);
il.Emit(OpCodes.Stloc, resultLocal);
il.EndExceptionBlock();
il.MarkLabel(endLabel);
il.Emit(OpCodes.Ldloc, resultLocal);
il.Emit(OpCodes.Ret);
}

Expand Down
5 changes: 3 additions & 2 deletions Compilation/RuntimeEmitter.DynamicImport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ private void EmitDynamicImportModule(TypeBuilder typeBuilder, EmittedRuntime run
// Locals
var factoryLocal = il.DeclareLocal(funcType);
var tcsLocal = il.DeclareLocal(tcsType);
var exceptionLocal = il.DeclareLocal(_types.Exception);

// Labels
var notFoundLabel = il.DefineLabel();
Expand Down Expand Up @@ -194,9 +195,9 @@ private void EmitDynamicImportModule(TypeBuilder typeBuilder, EmittedRuntime run
il.Emit(OpCodes.Newobj, _types.GetConstructor(_types.Exception, _types.String));

// tcs.SetException(ex);
il.Emit(OpCodes.Stloc_0); // Store exception temporarily
il.Emit(OpCodes.Stloc, exceptionLocal);
il.Emit(OpCodes.Ldloc, tcsLocal);
il.Emit(OpCodes.Ldloc_0); // Load exception
il.Emit(OpCodes.Ldloc, exceptionLocal);
il.Emit(OpCodes.Callvirt, _types.GetMethod(tcsType, "SetException", _types.Exception));

// return tcs.Task;
Expand Down
8 changes: 5 additions & 3 deletions Compilation/RuntimeEmitter.Iterator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -393,12 +393,14 @@ private void EmitIterateToList(TypeBuilder typeBuilder, EmittedRuntime runtime)
il.Emit(OpCodes.Bge, strLoopEnd);

// result.Add(str[idx].ToString())
var charLocal = il.DeclareLocal(_types.Char);
il.Emit(OpCodes.Ldloc, resultLocal);
il.Emit(OpCodes.Ldloc, strLocal);
il.Emit(OpCodes.Ldloc, idxLocal);
il.Emit(OpCodes.Callvirt, _types.String.GetMethod("get_Chars", [typeof(int)])!);
var charToString = typeof(char).GetMethod("ToString", Type.EmptyTypes)!;
il.Emit(OpCodes.Call, charToString);
il.Emit(OpCodes.Callvirt, _types.String.GetMethod("get_Chars", [_types.Int32])!);
il.Emit(OpCodes.Stloc, charLocal);
il.Emit(OpCodes.Ldloca, charLocal);
il.Emit(OpCodes.Call, _types.GetMethodNoParams(_types.Char, "ToString"));
il.Emit(OpCodes.Callvirt, _types.GetMethod(_types.ListOfObject, "Add", _types.Object));

il.Emit(OpCodes.Ldloc, idxLocal);
Expand Down
Loading