Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementation of LDFN & CALL_SP, and some fixes delegate calling #251

Merged
merged 13 commits into from
Jun 30, 2024
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 @@
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 @@

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;

Check warning on line 202 in runtime/ishtar.generator/GeneratorContext.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

runtime/ishtar.generator/GeneratorContext.cs#L202

Remove this commented out code.
ctorGen.Emit(OpCodes.LDARG_2); // load scope
ctorGen.Emit(OpCodes.LDARG_0); // load this
ctorGen.Emit(OpCodes.STF, scope); // this.scope = scope;

Check warning on line 205 in runtime/ishtar.generator/GeneratorContext.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

runtime/ishtar.generator/GeneratorContext.cs#L205

Remove this commented out code.
Comment on lines +202 to +205
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove commented-out code.

The commented-out code should be removed to maintain code cleanliness.

- // this.ptrRef = ref;
- // this.scope = scope;
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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.STF, ptrRef);
ctorGen.Emit(OpCodes.LDARG_2); // load scope
ctorGen.Emit(OpCodes.LDARG_0); // load this
ctorGen.Emit(OpCodes.STF, scope);
Tools
GitHub Check: Codacy Static Code Analysis

[warning] 202-202: runtime/ishtar.generator/GeneratorContext.cs#L202
Remove this commented out code.


[warning] 205-205: runtime/ishtar.generator/GeneratorContext.cs#L205
Remove this commented out code.

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 @@

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 @@
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 @@
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 @@
$"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 @@
--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 @@
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 @@
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 @@
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 @@
++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;

Check notice on line 743 in runtime/ishtar.vm/VirtualMachine.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

runtime/ishtar.vm/VirtualMachine.cs#L743

Remove the unused local variable 'sw'.
} break;
Comment on lines +713 to +744
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the unused local variable sw.

The local variable sw in the CALL_SP opcode is unused and should be removed to clean up the code.

- var sw = raw->type;
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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 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;
} break;
Tools
GitHub Check: Codacy Static Code Analysis

[notice] 743-743: runtime/ishtar.vm/VirtualMachine.cs#L743
Remove the unused local variable 'sw'.

case CALL:
{
++ip;
Expand Down Expand Up @@ -736,6 +796,12 @@
// }
// }
//#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 @@
{
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 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
Loading