From daf5cee20a615a8d47327c7cd6533ed6bb208e0a Mon Sep 17 00:00:00 2001
From: nakst <>
Date: Thu, 10 Mar 2022 14:36:18 +0000
Subject: [PATCH] scripting engine: callback supports in library functions

---
 util/script.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 65 insertions(+), 2 deletions(-)

diff --git a/util/script.c b/util/script.c
index 25535eb..ff36fc5 100644
--- a/util/script.c
+++ b/util/script.c
@@ -130,6 +130,7 @@
 #define T_INTERPOLATE_ILIST   (111)
 #define T_ROT3                (112)
 #define T_LIBCALL             (113)
+#define T_END_CALLBACK        (114)
 
 #define T_FLOAT_ADD           (120)
 #define T_FLOAT_MINUS         (121)
@@ -315,6 +316,7 @@ typedef struct HeapEntry {
 	uint8_t type;
 	bool gcMark;
 	bool internalValuesAreManaged;
+	uint32_t externalReferenceCount;
 
 	union {
 		struct { // T_STR
@@ -4707,7 +4709,7 @@ uintptr_t HeapAllocate(ExecutionContext *context) {
 		uintptr_t reclaimed = 0;
 
 		for (uintptr_t i = 1; i < context->heapEntriesAllocated; i++) {
-			if (!context->heap[i].gcMark) {
+			if (!context->heap[i].gcMark && !context->heap[i].externalReferenceCount) {
 				// PrintDebug("\033[0;32mFreeing index %d...\033[0m\n", i);
 				HeapFreeEntry(context, i);
 				*link = i;
@@ -6257,6 +6259,8 @@ int ScriptExecuteFunction(uintptr_t instructionPointer, ExecutionContext *contex
 			} else {
 				break;
 			}
+		} else if (command == T_END_CALLBACK) {
+			break;
 		} else {
 			PrintDebug("Unknown command %d.\n", command);
 			return -1;
@@ -6384,6 +6388,25 @@ bool ScriptParameterCString(void *engine, char **output) {
 	return true;
 }
 
+void ScriptHeapRefClose(void *engine, intptr_t index) {
+	ExecutionContext *context = (ExecutionContext *) engine;
+	Assert(index >= 0 || index < (intptr_t) context->heapEntriesAllocated);
+	Assert(context->heap[index].externalReferenceCount);
+	context->heap[index].externalReferenceCount--;
+}
+
+bool ScriptParameterHeapRef(void *engine, intptr_t *output) {
+	ExecutionContext *context = (ExecutionContext *) engine;
+	context->c->parameterCount++;
+	if (context->c->stackPointer < context->c->parameterCount) return false;
+	intptr_t index = context->c->stack[context->c->stackPointer - context->c->parameterCount].i;
+	if (index < 0 || index >= (intptr_t) context->heapEntriesAllocated) return false;
+	if (context->heap[index].externalReferenceCount == 0xFFFFFFFF) return false;
+	context->heap[index].externalReferenceCount++;
+	*output = index;
+	return true;
+}
+
 bool ScriptParameterInt64(void *engine, int64_t *output) {
 	ExecutionContext *context = (ExecutionContext *) engine;
 	context->c->parameterCount++;
@@ -6392,6 +6415,15 @@ bool ScriptParameterInt64(void *engine, int64_t *output) {
 	return true;
 }
 
+bool ScriptParameterDouble(void *engine, double *output) {
+	ExecutionContext *context = (ExecutionContext *) engine;
+	context->c->parameterCount++;
+	if (context->c->stackPointer < context->c->parameterCount) return false;
+	*output = context->c->stack[context->c->stackPointer - context->c->parameterCount].f;
+	return true;
+}
+
+bool ScriptParameterBool  (void *engine,     bool *output) { int64_t i; if (!ScriptParameterInt64(engine, &i)) return false; *output = i; return true; }
 bool ScriptParameterUint32(void *engine, uint32_t *output) { int64_t i; if (!ScriptParameterInt64(engine, &i)) return false; *output = i; return true; }
 bool ScriptParameterUint64(void *engine, uint64_t *output) { int64_t i; if (!ScriptParameterInt64(engine, &i)) return false; *output = i; return true; }
 bool ScriptParameterInt32 (void *engine,  int32_t *output) { int64_t i; if (!ScriptParameterInt64(engine, &i)) return false; *output = i; return true; }
@@ -6414,6 +6446,33 @@ void ScriptReturnPointer(void *engine, void *input) {
 	ScriptReturnInt(engine, (int64_t) (intptr_t) input);
 }
 
+bool ScriptRunCallback(void *engine, intptr_t functionPointer, int64_t *parameters, bool *managedParameters, size_t parameterCount) {
+	// TODO Do this in a separate coroutine?
+
+	ExecutionContext *context = (ExecutionContext *) engine;
+
+	for (intptr_t i = parameterCount - 1; i >= -1; i--) {
+		if (context->c->stackPointer == context->c->stackEntriesAllocated) {
+			PrintError4(context, 0, "Stack overflow.\n");
+			return false;
+		}
+
+		if (i == -1) {
+			context->c->stack[context->c->stackPointer].i = functionPointer;
+			context->c->stackIsManaged[context->c->stackPointer] = true;
+		} else {
+			context->c->stack[context->c->stackPointer].i = parameters[i];
+			context->c->stackIsManaged[context->c->stackPointer] = managedParameters[i];
+		}
+
+		context->c->stackPointer++;
+	}
+
+	int result = ScriptExecuteFunction(2, context);
+	if (result == -1) PrintError3("The script was malformed.\n");
+	return result > 0;
+}
+
 bool ScriptLoad(Tokenizer tokenizer, ExecutionContext *context, ImportData *importData, bool replMode) {
 	Node *previousRootNode = context->rootNode;
 	ImportData *previousImportData = context->functionData->importData;
@@ -6425,6 +6484,10 @@ bool ScriptLoad(Tokenizer tokenizer, ExecutionContext *context, ImportData *impo
 	FunctionBuilderAppend(context->functionData, &b, sizeof(b));
 	b = T_AWAIT; // Put a T_AWAIT command at address 1.
 	FunctionBuilderAppend(context->functionData, &b, sizeof(b));
+	b = T_CALL; // Put a T_CALL command at address 2 (for ScriptRunCallback).
+	FunctionBuilderAppend(context->functionData, &b, sizeof(b));
+	b = T_END_CALLBACK; // Put a T_END_CALLBACK command at address 3 (for ScriptRunCallback).
+	FunctionBuilderAppend(context->functionData, &b, sizeof(b));
 
 	bool success = context->rootNode 
 		&& ASTSetScopes(&tokenizer, context, context->rootNode, NULL)
@@ -7670,7 +7733,7 @@ void *LibraryLoad(const char *name) {
 	strcat(name2, name);
 	strcat(name2, ".so");
 
-	void *library = dlopen(name2, RTLD_NOW);
+	void *library = dlopen(name2, RTLD_LAZY);
 
 	if (!library) {
 		PrintError3("The library \"%s\" could not be found or loaded.\n", name2);