Start working on CALLMETHOD

This commit is contained in:
2026-07-16 14:39:39 +10:00
parent 8e4267d500
commit 7a53105eca
4 changed files with 312 additions and 3 deletions

View File

@@ -888,6 +888,137 @@ struct GroundExecutionResult _GroundBytecodeInstructionExecute(GroundBytecodeIns
return CONTINUE;
}
CALLMETHOD: {
// bytecode: object_offset, field0_offset, ..., fieldN_offset, method_offset, arg0_offset, ..., argM_offset, output_offset
GroundSize len = instruction->args.len;
// Navigate the object through field path to find the containing object and method
GroundBytecodeValue* current = HEAP_GET(heap, instruction->args.at[0]);
// Walk the field path to find the containing object
GroundSize fieldPairsCount = 0;
for (GroundSize fi = 1; fi < len - 1; fi++) {
if (current->type.type != GroundType_Object) break;
GroundBytecodeValue* next = &current->as.Object.values[instruction->args.at[fi]];
if (next->type.type == GroundType_Function) {
// This is the method
break;
}
current = next;
fieldPairsCount++;
}
// The method is at index 1 + fieldPairsCount
GroundSize methodFieldIdx = 1 + fieldPairsCount;
GroundSize methodArgCount = len - methodFieldIdx - 2; // subtract method and output
if (current->type.type != GroundType_Object) {
Ground.Log.Error("CALLMETHOD on non-object value in Ground.Bytecode.Instruction.execute");
Ground.Flags.error = true;
return CONTINUE;
}
GroundBytecodeFunction* method = &current->as.Object.values[instruction->args.at[methodFieldIdx]].as.Function;
if (method->isNativeFunction) {
// Native method handling (same as CALL native path but with self appended)
ffi_type* argTypes[methodArgCount + 1];
void* args[methodArgCount + 1];
// Self is the first arg for native methods
argTypes[0] = ffiTypeFromGroundType(&(GroundType){.type = GroundType_Object});
args[0] = current;
for (GroundSize i = 0; i < methodArgCount; i++) {
GroundBytecodeValue* val = HEAP_GET(heap, instruction->args.at[methodFieldIdx + 1 + i]);
argTypes[i + 1] = ffiTypeFromGroundType(&val->type);
switch (val->type.type) {
case GroundType_Int: args[i + 1] = &val->as.Int; break;
case GroundType_Double: args[i + 1] = &val->as.Double; break;
case GroundType_Char: args[i + 1] = &val->as.Char; break;
case GroundType_Bool: args[i + 1] = &val->as.Bool; break;
case GroundType_String: args[i + 1] = &val->as.String.cstr; break;
default: args[i + 1] = val; break;
}
}
ffi_cif cif;
ffi_status status = ffi_prep_cif(
&cif, FFI_DEFAULT_ABI, methodArgCount + 1,
ffiTypeFromGroundType(method->returnType), argTypes
);
if (status != FFI_OK) {
Ground.Log.Error("ffi CIF preparation failed in Ground.Bytecode.Instruction.execute");
Ground.Flags.error = true;
return CONTINUE;
}
union {
GroundInt Int;
GroundDouble Double;
GroundChar Char;
GroundBool Bool;
char* String;
} result;
ffi_call(&cif, method->program.native, &result, args);
switch (method->returnType->type) {
case GroundType_Int: HEAP_SET(heap, instruction->args.at[len - 1], Ground.New.BytecodeValue(Ground.New.Value.Int(result.Int))); break;
case GroundType_Char: HEAP_SET(heap, instruction->args.at[len - 1], Ground.New.BytecodeValue(Ground.New.Value.Char(result.Char))); break;
case GroundType_Double: HEAP_SET(heap, instruction->args.at[len - 1], Ground.New.BytecodeValue(Ground.New.Value.Double(result.Double))); break;
case GroundType_Bool: HEAP_SET(heap, instruction->args.at[len - 1], Ground.New.BytecodeValue(Ground.New.Value.Bool(result.Bool))); break;
case GroundType_String: HEAP_SET(heap, instruction->args.at[len - 1], Ground.New.BytecodeValue(Ground.New.Value.String(Ground.New.String(result.String)))); break;
default: break;
}
return CONTINUE;
}
if (method->closure == NULL) {
Ground.Log.Error("unexpected NULL closure in Ground.Bytecode.Instruction.execute");
Ground.Flags.error = true;
return CONTINUE;
}
// Total args for method: methodArgCount explicit + 1 self
GroundSize totalArgs = methodArgCount + 1;
GroundSize heapSize = totalArgs + method->closure->len;
GroundBytecodeHeap newHeap = { malloc(sizeof(GroundBytecodeValue) * heapSize), heapSize, heapSize };
if (newHeap.heap == NULL) {
Ground.Log.Error("malloc failed in Ground.Bytecode.Instruction.execute");
Ground.Flags.error = true;
return CONTINUE;
}
// Load explicit method arguments into the heap
for (GroundSize i = 0; i < methodArgCount; i++) {
newHeap.heap[i] = Ground.Copy.BytecodeValue(HEAP_GET(heap, instruction->args.at[methodFieldIdx + 1 + i]));
}
// Self (the containing object) is the last argument
newHeap.heap[methodArgCount] = Ground.Copy.BytecodeValue(current);
// Copy closure variables after arguments
for (GroundSize i = totalArgs; i < newHeap.len; i++) {
newHeap.heap[i] = Ground.Copy.BytecodeValue(&method->closure->heap[i - totalArgs]);
}
// Execute the method
GroundBytecodeValue result = Ground.Bytecode.Program.execute(method->program.ground, &newHeap);
GroundBytecodeValue resultCopy = Ground.Copy.BytecodeValue(&result);
// Clean up
for (GroundSize i = 0; i < newHeap.len; i++) {
Ground.Free.BytecodeValue(&newHeap.heap[i]);
}
free(newHeap.heap);
// Store the result
HEAP_SET(heap, instruction->args.at[len - 1], resultCopy);
return CONTINUE;
}
STRUCT: {