Skip to content

Commit

Permalink
Merge pull request #251 from vein-lang/fixes/function-method-group-fixes
Browse files Browse the repository at this point in the history
Implementation of LDFN & CALL_SP, and some fixes delegate calling
  • Loading branch information
0xF6 authored Jun 30, 2024
2 parents cbf7fe5 + e2ddc48 commit 0997b7e
Show file tree
Hide file tree
Showing 12 changed files with 209 additions and 59 deletions.
2 changes: 1 addition & 1 deletion runtime/common/reflection/VeinClass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ private bool CheckCompatibilityFunctionClass(VeinClass userArg, VeinClass method
if (userInvoke is null || methodInvoke is null)
return false;

return userInvoke.Signature.HasCompatibility(methodInvoke.Signature);
return userInvoke.Signature.HasCompatibility(methodInvoke.Signature, true);
}

private bool CheckCompatibility(List<VeinComplexType> userArgs, List<VeinComplexType> methodArgs)
Expand Down
15 changes: 9 additions & 6 deletions runtime/common/reflection/VeinMethod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,20 @@ public record VeinMethodSignature(

public bool IsGeneric => Arguments.Any(x => x.IsGeneric);

public bool HasCompatibility(VeinMethodSignature otherSig)
public bool HasCompatibility(VeinMethodSignature otherSig, bool ignoreThis)
{
if (ArgLength != otherSig.ArgLength)
var a1Args = ignoreThis ? Arguments.Where(NotThis).ToArray() : Arguments.ToArray();
var a2Args = ignoreThis ? otherSig.Arguments.Where(NotThis).ToArray() : otherSig.Arguments.ToArray();

if (a1Args.Length != a2Args.Length)
return false;

var argumentsCompatibility = true;
for (int i = 0; i < Arguments.Count; i++)
for (int i = 0; i < a1Args.Length; i++)
{
var a1 = Arguments[i];
var a2 = otherSig.Arguments[i];

var a1 = a1Args[i];
var a2 = a2Args[i];
if (a1.IsGeneric != a2.IsGeneric)
{
argumentsCompatibility = false;
Expand Down
3 changes: 2 additions & 1 deletion runtime/common/reflection/VeinTypeCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ public enum VeinTypeCode : int
TYPE_ARRAY, /* Array<?> */
TYPE_TOKEN, /* type token */
TYPE_RAW, /* raw pointer */
TYPE_FUNCTION /* function class */
TYPE_FUNCTION, /* function class */
TYPE_NULL, /* null */
}

public static class VeinTypeCodeEx
Expand Down
26 changes: 17 additions & 9 deletions runtime/ishtar.generator/GeneratorContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,8 @@ public VeinClass CreateFunctionMulticastGroup(VeinMethodSignature sig)
var objType = TYPE_OBJECT.AsClass(types);
var rawType = TYPE_RAW.AsClass(types);

var ctorMethod = hiddenClass.DefineMethod(VeinMethod.METHOD_NAME_CONSTRUCTOR, TYPE_VOID.AsClass(types), [
var ctorMethod = hiddenClass.DefineMethod(VeinMethod.METHOD_NAME_CONSTRUCTOR, hiddenClass, [
new (VeinArgumentRef.THIS_ARGUMENT, hiddenClass),
new("fn", rawType),
new("scope",objType)
]);
Expand All @@ -196,25 +197,32 @@ public VeinClass CreateFunctionMulticastGroup(VeinMethodSignature sig)

var ctorGen = ctorMethod.GetGenerator();

ctorGen.Emit(OpCodes.LDARG_0);
ctorGen.Emit(OpCodes.STF, ptrRef);
ctorGen.Emit(OpCodes.LDARG_1);
ctorGen.Emit(OpCodes.STF, scope);
ctorGen.Emit(OpCodes.RET);
ctorGen.Emit(OpCodes.LDARG_1); // load ref
ctorGen.Emit(OpCodes.LDARG_0); // load this
ctorGen.Emit(OpCodes.STF, ptrRef); // this.ptrRef = ref;
ctorGen.Emit(OpCodes.LDARG_2); // load scope
ctorGen.Emit(OpCodes.LDARG_0); // load this
ctorGen.Emit(OpCodes.STF, scope); // this.scope = scope;
ctorGen.Emit(OpCodes.LDARG_0); // load this
ctorGen.Emit(OpCodes.RET); // return this


var method = hiddenClass.DefineMethod("invoke", Internal | Special,
sig.ReturnType,sig.Arguments.Where(VeinMethodSignature.NotThis).ToArray());

var hasThis = sig.Arguments.All(VeinMethodSignature.NotThis);
var hasNotThis = sig.Arguments.All(VeinMethodSignature.NotThis);

var generator = method.GetGenerator();

if (hasThis)
if (!hasNotThis)
{
generator.EmitThis();
generator.Emit(OpCodes.LDF, scope);
}
foreach (int i in ..method.Signature.ArgLength)
generator.Emit(OpCodes.LDARG_S, i); // TODO optimization for LDARG_X
generator.Emit(OpCodes.LDARG_S, i + 1); // TODO optimization for LDARG_X

generator.EmitThis();
generator.Emit(OpCodes.LDF, ptrRef);
generator.Emit(OpCodes.CALL_SP);
generator.Emit(OpCodes.RET);
Expand Down
14 changes: 9 additions & 5 deletions runtime/ishtar.generator/generators/call.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@ public static ILGenerator EmitCall(this ILGenerator gen, VeinClass @class, Invoc
{
var ctx = gen.ConsumeFromMetadata<GeneratorContext>("context");

if (ctx.IsCallingDelegate(invocation, out var argument))
if (ctx.IsCallingDelegate(invocation, out var argument, out var index))
{
gen.EmitLoadArgument(index!.Value);


foreach (var arg in invocation.Arguments)
gen.EmitExpression(arg);
var internalMethod = argument!.Type.FindMethod("invoke") ;
Expand All @@ -40,11 +43,12 @@ public static ILGenerator EmitCall(this ILGenerator gen, VeinClass @class, Invoc
return gen;
}

public static bool IsCallingDelegate(this GeneratorContext gen, InvocationExpression invocation, out VeinArgumentRef? arg)
public static bool IsCallingDelegate(this GeneratorContext gen, InvocationExpression invocation, out VeinArgumentRef? arg, out int? index)
{
arg = gen.CurrentMethod.Signature.Arguments
.Where(x => !x.IsGeneric)
.FirstOrDefault(x => x.Name.Equals(invocation.Name.ExpressionString));
(arg, index) = gen.CurrentMethod.Signature.Arguments
.Select((x, y) => (x, y))
.Where((x) => !x.x.IsGeneric)
.SingleOrDefault(x => x.x.Name.Equals(invocation.Name.ExpressionString));

if (arg is null)
return false;
Expand Down
7 changes: 3 additions & 4 deletions runtime/ishtar.generator/generators/operators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -273,9 +273,7 @@ public static ILGenerator EmitUnary(this ILGenerator gen, UnaryExpressionSyntax
var invokeMethod = fnType.FindMethod("invoke");



gen.Emit(OpCodes.NEWOBJ, fnType);
gen.Emit(OpCodes.LDNULL);

if (node.Operand is IdentifierExpression id)
{
Expand All @@ -285,13 +283,14 @@ public static ILGenerator EmitUnary(this ILGenerator gen, UnaryExpressionSyntax
}
else
throw new NotSupportedException("EmitLoadFunction");

gen.Emit(OpCodes.LDNULL);

var ctor = fnType.FindMethod(VeinMethod.METHOD_NAME_CONSTRUCTOR,
[
fnType,
VeinTypeCode.TYPE_RAW.AsClass(ctx.Module.Types),
VeinTypeCode.TYPE_OBJECT.AsClass(ctx.Module.Types)
]);
], true);



Expand Down
112 changes: 89 additions & 23 deletions runtime/ishtar.vm/VirtualMachine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,17 @@ private bool assert_violation_zone_writes(CallFrame* frame, SmartPointer<stackva

public void exec_method(CallFrame* invocation)
{
if (!Config.DisableValidationInvocationArgs)
{
var argsLen = invocation->method->ArgLength;

for (int i = 0; i != argsLen; i++)
{
if (invocation->args[i].type > TYPE_NULL)
FastFail(STATE_CORRUPT, $"[arg validation] argument [{i}/{argsLen}] for [{invocation->method->Name}] has corrupted", invocation);
}
}

println($"@.frame> {invocation->method->Owner->Name}::{invocation->method->Name}");

var _module = invocation->method->Owner->Owner;
Expand All @@ -276,6 +287,9 @@ public void exec_method(CallFrame* invocation)
var sp = stack.Ref + STACK_VIOLATION_LEVEL_SIZE;
var sp_start = sp;
var start = ip;

long getStackLen() => sp - sp_start;

var end = mh->code + mh->code_size;
var end_stack = sp + mh->max_stack;
uint* endfinally_ip = null;
Expand Down Expand Up @@ -304,22 +318,18 @@ void jump_to(int index)
return null;
}

// maybe mutable il code is bad, need research
void ForceFail(RuntimeIshtarClass* clazz)
void ForceThrow(RuntimeIshtarClass* clazz)
{
*ip = (uint)THROW;
CallFrame.FillStackTrace(invocation);
sp->data.p = (nint)GC.AllocObject(clazz, invocation);
sp->type = TYPE_CLASS;
sp++;
}

GC.Collect();


while (true)
{
vm_cycle_start:
invocation->last_ip = (OpCodeValue)(ushort)*ip;
println($"@@.{invocation->last_ip} 0x{(nint)ip:X} [sp: {(((nint)sp) - ((nint)sp))}]");
println($"@@.{invocation->last_ip} 0x{(nint)ip:X} [sp: {getStackLen()}]");

if (!invocation->exception.IsDefault() && invocation->level == 0)
return;
Expand Down Expand Up @@ -375,13 +385,17 @@ void ForceFail(RuntimeIshtarClass* clazz)
$"Arguments in current function is empty, but trying access it.", invocation);
*sp = args[(*ip) - (short)LDARG_0];
println($"load from args ({sp->type})");
++sp;
Assert(sp->type != TYPE_NONE, STATE_CORRUPT, "", invocation);
Assert(sp->type <= TYPE_NULL, STATE_CORRUPT, "", invocation);
++sp;
++ip;
break;
case LDARG_S:
++ip;
*sp = args[(*ip)];
println($"load from args ({sp->type})");
Assert(sp->type != TYPE_NONE, STATE_CORRUPT, "", invocation);
Assert(sp->type <= TYPE_NULL, STATE_CORRUPT, "", invocation);
++sp;
++ip;
break;
Expand Down Expand Up @@ -571,14 +585,17 @@ void ForceFail(RuntimeIshtarClass* clazz)
--sp;
if (@this->type == TYPE_NONE)
{
ForceFail(KnowTypes.NullPointerException(invocation));
break;
ForceThrow(KnowTypes.NullPointerException(invocation));
goto exception_handle;
}
//FFI.StaticValidate(invocation, @this, field.Owner);
var value = sp;
var this_obj = (IshtarObject*)@this->data.p;
var target_class = this_obj->clazz;
this_obj->vtable[field->vtable_offset] = IshtarMarshal.Boxing(invocation, value);
if (value->type == TYPE_NULL)
this_obj->vtable[field->vtable_offset] = null;
else
this_obj->vtable[field->vtable_offset] = IshtarMarshal.Boxing(invocation, value);
++ip;
}
break;
Expand All @@ -589,7 +606,14 @@ void ForceFail(RuntimeIshtarClass* clazz)
var @class = GetClass(*++ip, _module, invocation);
var field = GetField(fieldIdx, @class, _module, invocation);
var @this = sp;

if (@this->type == TYPE_NONE)
{
CallFrame.FillStackTrace(invocation);
FastFail(STATE_CORRUPT, $"[LDF] invalid @this object loaded, TYPE_NONE, maybe corrupted IL code", invocation);
}

if (@this->type == TYPE_NULL)
{
// TODO
CallFrame.FillStackTrace(invocation);
Expand All @@ -603,12 +627,13 @@ void ForceFail(RuntimeIshtarClass* clazz)
var value = IshtarMarshal.UnBoxing(invocation, obj);
*sp = value;
++ip;
println($"@@@ LDF -> {sp->type} (from {field->Name})");
++sp;
}
break;
case LDNULL:
sp->type = TYPE_OBJECT;
sp->data.p = (nint)0;
sp->type = TYPE_NULL;
sp->data.p = 0;
++sp;
++ip;
break;
Expand All @@ -621,12 +646,15 @@ void ForceFail(RuntimeIshtarClass* clazz)
var t1 = (IshtarObject*)sp->data.p;
if (t1 == null)
{
ForceFail(KnowTypes.NullPointerException(invocation));
continue;
ForceThrow(KnowTypes.NullPointerException(invocation));
goto exception_handle;
}
var r = IshtarObject.IsInstanceOf(invocation, t1, t2);
if (r == null)
ForceFail(KnowTypes.IncorrectCastFault(invocation));
{
ForceThrow(KnowTypes.IncorrectCastFault(invocation));
goto exception_handle;
}
else ++sp;
}
break;
Expand Down Expand Up @@ -682,6 +710,38 @@ void ForceFail(RuntimeIshtarClass* clazz)
++sp;
}
break;
case LDFN:
{
++ip;
var tokenIdx = *ip;
var owner = readTypeName(*++ip, _module, invocation);
var method = GetMethod(tokenIdx, owner, _module, invocation);
++ip;

var raw = GC.AllocRawValue(invocation); // TODO destroy

raw->type = VeinRawCode.ISHTAR_METHOD;
raw->data.m = method;

sp->type = TYPE_RAW;
sp->data.p = (nint)raw;
++sp;
} break;
case CALL_SP:
{
++ip;
sp--;

if (sp->type == TYPE_NULL)
{
ForceThrow(KnowTypes.NullPointerException(invocation));
goto exception_handle;
}

var method = *sp;
var raw = (rawval*)method.data.p;
var sw = raw->type;
} break;
case CALL:
{
++ip;
Expand Down Expand Up @@ -736,6 +796,12 @@ void ForceFail(RuntimeIshtarClass* clazz)
// }
// }
//#endif

println($"@@@ {owner->NameWithNS}::{method->Name} (argument {y} is {sp->type} type)");

if (sp->type > TYPE_NULL)
FastFail(STATE_CORRUPT, $"[call arg validation] trying fill corrupted argument [{y}/{method->ArgLength}] for [{method->Name}]", invocation);

method_args[y] = *sp;
}

Expand All @@ -761,14 +827,16 @@ void ForceFail(RuntimeIshtarClass* clazz)
{
sp->type = TYPE_CLASS;
sp->data.p = (nint)child_frame->exception.value;

invocation->exception = child_frame->exception;

GC.FreeStack(child_frame, method_args, method->ArgLength);
child_frame->Dispose();
goto exception_handle;
}

GC.FreeStack(child_frame, method_args, method->ArgLength);
child_frame->Dispose();
GC.Collect();
} break;
case LOC_INIT:
{
Expand Down Expand Up @@ -1235,11 +1303,9 @@ void ForceFail(RuntimeIshtarClass* clazz)

void fill_frame_exception()
{
invocation->exception = new CallFrameException
{
last_ip = ip,
value = (IshtarObject*)sp->data.p
};
if (invocation->exception.last_ip is null)
invocation->exception.last_ip = ip;
invocation->exception.value = (IshtarObject*)sp->data.p;
CallFrame.FillStackTrace(invocation);
ip++;
}
Expand Down
Loading

0 comments on commit 0997b7e

Please sign in to comment.