major restructuring

This commit is contained in:
Francesco Cozzuto
2023-01-20 20:52:20 +01:00
parent 0980cc193d
commit 411388755f
30 changed files with 2175 additions and 2362 deletions
+54
View File
@@ -0,0 +1,54 @@
#include "run.h"
#include "builtins_api.h"
#include "../builtins/basic.h"
bool Runtime_plugBuiltinsFromStaticMap(Runtime *runtime, StaticMapSlot *bin_table, void (*bin_table_constructor)(StaticMapSlot*), Error *error)
{
Object *object = Object_NewStaticMap(bin_table, bin_table_constructor, runtime, error);
if (object == NULL)
return false;
return Runtime_plugBuiltins(runtime, object, error);
}
bool Runtime_plugBuiltinsFromSource(Runtime *runtime, Source *source, Error *error)
{
Object *rets[8];
int retc = runSource(runtime, source, rets, error);
if(retc < 0)
return false;
if (retc == 0)
return true;
return Runtime_plugBuiltins(runtime, rets[0], error);
}
bool Runtime_plugBuiltinsFromFile(Runtime *runtime, const char *file, Error *error)
{
Source *source = Source_FromFile(file, error);
if (source == NULL)
return false;
bool result = Runtime_plugBuiltinsFromSource(runtime, source, error);
Source_Free(source);
return result;
}
bool Runtime_plugBuiltinsFromString(Runtime *runtime, const char *string, Error *error)
{
Source *source = Source_FromString("<prelude>", string, -1, error);
if (source == NULL)
return false;
bool result = Runtime_plugBuiltinsFromSource(runtime, source, error);
Source_Free(source);
return result;
}
bool Runtime_plugDefaultBuiltins(Runtime *runtime, Error *error)
{
extern char start_noja[];
return Runtime_plugBuiltinsFromStaticMap(runtime, bins_basic, bins_basic_init, error)
&& Runtime_plugBuiltinsFromString(runtime, start_noja, error)
&& Runtime_plugBuiltinsFromString(runtime, "XXX=999;", error);
}
+6
View File
@@ -0,0 +1,6 @@
#include "runtime.h"
bool Runtime_plugDefaultBuiltins(Runtime *runtime, Error *error);
bool Runtime_plugBuiltinsFromString(Runtime *runtime, const char *string, Error *error);
bool Runtime_plugBuiltinsFromFile(Runtime *runtime, const char *file, Error *error);
bool Runtime_plugBuiltinsFromStaticMap(Runtime *runtime, StaticMapSlot *bin_table, void (*bin_table_constructor)(StaticMapSlot*), Error *error);
bool Runtime_plugBuiltinsFromSource(Runtime *runtime, Source *source, Error *error);
-220
View File
@@ -1,220 +0,0 @@
/* +--------------------------------------------------------------------------+
** | _ _ _ |
** | | \ | | (_) |
** | | \| | ___ _ __ _ |
** | | . ` |/ _ \| |/ _` | |
** | | |\ | (_) | | (_| | |
** | |_| \_|\___/| |\__,_| |
** | _/ | |
** | |__/ |
** +--------------------------------------------------------------------------+
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
** +--------------------------------------------------------------------------+
** | This file is part of The Noja Interpreter. |
** | |
** | The Noja Interpreter is free software: you can redistribute it and/or |
** | modify it under the terms of the GNU General Public License as published |
** | by the Free Software Foundation, either version 3 of the License, or (at |
** | your option) any later version. |
** | |
** | The Noja Interpreter is distributed in the hope that it will be useful, |
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
** | Public License for more details. |
** | |
** | You should have received a copy of the GNU General Public License along |
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
** +--------------------------------------------------------------------------+
*/
#include <time.h>
#include <assert.h>
#include <stdlib.h>
#include "../utils/defs.h"
#include "../objects/objects.h"
#include "timing.h"
#include "runtime.h"
typedef struct {
Object base;
const char *name;
Runtime *runtime;
Executable *exe;
int index, argc;
Object *closure;
TimingID timing_id;
} FunctionObject;
static _Bool free_(Object *self, Error *error)
{
(void) error;
FunctionObject *func = (FunctionObject*) self;
Executable_Free(func->exe);
return 1;
}
static void walk(Object *self, void (*callback)(Object **referer, void *userp), void *userp)
{
FunctionObject *func = (FunctionObject*) self;
callback(&func->closure, userp);
}
static int call(Object *self, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Heap *heap, Error *error)
{
assert(self != NULL && heap != NULL && error != NULL);
FunctionObject *func = (FunctionObject*) self;
assert(func->exe != NULL);
assert(func->argc >= 0);
assert(func->index >= 0);
// Make sure the right amount of arguments is provided.
Object **argv2;
int expected_argc = func->argc;
if(expected_argc < (int) argc)
{
// Nothing to be done. By using
// the right argc the additional
// arguments are ignored implicitly.
argv2 = argv;
}
else if(expected_argc > (int) argc)
{
// Some arguments are missing.
argv2 = malloc(sizeof(Object*) * expected_argc);
if(argv2 == NULL)
{
Error_Report(error, ErrorType_INTERNAL, "No memory");
return -1;
}
// Copy the provided arguments.
for(int i = 0; i < (int) argc; i += 1)
argv2[i] = argv[i];
// Set the unspecified arguments to none.
for(int i = argc; i < expected_argc; i += 1)
{
argv2[i] = Object_NewNone(heap, error);
if(argv2[i] == NULL)
return -1;
}
}
else
// The right amount of arguments was provided.
argv2 = argv;
clock_t begin;
TimingID timing_id;
TimingTable *timing_table = Runtime_GetTimingTable(func->runtime);
if (timing_table != NULL) {
begin = clock();
// Need to save the object's member
// before the run function since it
// may trigger a GC cycle invalidating
// the object pointer.
timing_id = func->timing_id;
}
int retc = run(func->runtime, error, func->exe, func->index, func->closure, argv2, expected_argc, rets);
if (timing_table != NULL) {
double time = (double) (clock() - begin) / CLOCKS_PER_SEC;
TimingTable_sumCallTime(timing_table, timing_id, time);
}
// NOTE: Every object reference is invalidated from here.
if(argv2 != argv)
free(argv2);
return retc;
}
static TypeObject t_func = {
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
.name = "function",
.size = sizeof (FunctionObject),
.call = call,
.walk = walk,
.free = free_,
};
/* Symbol: Object_FromNojaFunction
*
* Creates an object from a noja executable structure.
*
* Args:
* - runtime: The reference to an instanciated Runtime.
*
* - exe: A noja executable.
*
* - index: The index of the first bytecode instruction
* of the noja function within the executable.
*
* - argc: The number of arguments the function expects.
* It must be positive (unlike [Object_FromNativeFunction],
* where -1 means variadic).
*
* - closure: An object containing variables that will be
* accessible from the noja function other than
* the ones that will be defined inside it.
*
* - heap: The heap that will be used to allocate the object.
* It can't be NULL.
*
* - error: Output parameter where error information is stored.
* It can't be NULL.
*
* Returns:
* The newly created object. If an error occurred, NULL is returned
* and information about the error is stored in the [error] argument.
*/
Object *Object_FromNojaFunction(Runtime *runtime, const char *name, Executable *exe, int index, int argc, Object *closure, Heap *heap, Error *error)
{
assert(runtime != NULL);
assert(exe != NULL);
assert(index >= 0);
assert(argc >= 0);
assert(heap != NULL);
assert(error != NULL);
FunctionObject *func = (FunctionObject*) Heap_Malloc(heap, &t_func, error);
if(func == NULL)
return NULL;
Executable *exe_copy = Executable_Copy(exe);
if(exe_copy == NULL)
{
Error_Report(error, ErrorType_INTERNAL, "Failed to copy executable");
return NULL;
}
func->runtime = runtime;
func->name = name; // Should this be copied?
func->exe = exe_copy;
func->index = index;
func->argc = argc;
func->closure = closure;
TimingTable *table = Runtime_GetTimingTable(runtime);
if (table != NULL) {
#warning "TODO: Calculate line number"
size_t line = 0;
Source *src = Executable_GetSource(exe);
func->timing_id = TimingTable_newEntry(table, src, line, name);
}
return (Object*) func;
}
-173
View File
@@ -1,173 +0,0 @@
/* +--------------------------------------------------------------------------+
** | _ _ _ |
** | | \ | | (_) |
** | | \| | ___ _ __ _ |
** | | . ` |/ _ \| |/ _` | |
** | | |\ | (_) | | (_| | |
** | |_| \_|\___/| |\__,_| |
** | _/ | |
** | |__/ |
** +--------------------------------------------------------------------------+
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
** +--------------------------------------------------------------------------+
** | This file is part of The Noja Interpreter. |
** | |
** | The Noja Interpreter is free software: you can redistribute it and/or |
** | modify it under the terms of the GNU General Public License as published |
** | by the Free Software Foundation, either version 3 of the License, or (at |
** | your option) any later version. |
** | |
** | The Noja Interpreter is distributed in the hope that it will be useful, |
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
** | Public License for more details. |
** | |
** | You should have received a copy of the GNU General Public License along |
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
** +--------------------------------------------------------------------------+
*/
/*
* WHAT IS THIS FILE?
* This file implements an object that makes it possible
* to call native functions from within noja code.
*
*/
#include <assert.h>
#include <stdlib.h>
#include "../utils/defs.h"
#include "../objects/objects.h"
#include "runtime.h"
typedef struct {
Object base;
Runtime *runtime;
int (*callback)(Runtime *runtime, Object **argv, unsigned int argc, Object *rets[MAX_RETS], Error *error);
int argc;
} NativeFunctionObject;
static int call(Object *self, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Heap *heap, Error *error)
{
assert(self != NULL);
assert(heap != NULL);
assert(error != NULL);
NativeFunctionObject *func = (NativeFunctionObject*) self;
// If the function isn't variadic, make sure
// the right amount of arguments is provided.
Object **argv2;
int argc2;
int expected_argc = func->argc;
if(expected_argc < 0 || expected_argc == (int) argc)
{
// The function is variadic or the right
// amount of arguments was provided.
argv2 = argv;
argc2 = argc;
}
else if(expected_argc < (int) argc)
{
// Nothing to be done. By using
// the right argc the additional
// arguments are ignored implicitly.
argv2 = argv;
argc2 = expected_argc;
}
else if(expected_argc > (int) argc)
{
// Some arguments are missing.
argv2 = malloc(sizeof(Object*) * expected_argc);
argc2 = expected_argc;
if(argv2 == NULL)
{
Error_Report(error, 1, "No memory");
return -1;
}
// Copy the provided arguments.
for(int i = 0; i < (int) argc; i += 1)
argv2[i] = argv[i];
// Set the unspecified arguments to none.
for(int i = argc; i < expected_argc; i += 1)
{
argv2[i] = Object_NewNone(heap, error);
if(argv2[i] == NULL)
{
free(argv2);
return -1;
}
}
} else {
UNREACHABLE;
argv2 = NULL;
argc2 = -1;
}
assert(func->callback != NULL);
int retc = func->callback(func->runtime, argv2, argc2, rets, error);
// NOTE: Since the callback may have executed some bytecode, a GC
// cycle may have been triggered, therefore we must assume
// every object reference that was locally saved is invalidated
// from here (the returned object is good tho).
if(argv2 != argv)
free(argv2);
return retc;
}
static TypeObject t_nfunc = {
.base = (Object) { .type = &t_type, .flags = Object_STATIC },
.name = "native function",
.size = sizeof (NativeFunctionObject),
.call = call,
};
/* Symbol: Object_FromNativeFunction
*
* Creates an object from a function pointer.
*
* Args:
* - runtime: The reference to an instanciated Runtime. This must be
* provided so that the callback can also access it.
*
* - callback: The native function to be executed when this object
* is called.
*
* - argc: The number of arguments the function expects. If -1 is
* provided, then the function is considered to be variadic.
*
* - heap: The heap that will be used to allocate the object.
* It can't be NULL.
*
* - error: Output parameter where error information is stored.
* It can't be NULL.
*
* Returns:
* The newly created object. If an error occurred, NULL is returned
* and information about the error is stored in the [error] argument.
*/
Object *Object_FromNativeFunction(Runtime *runtime, int (*callback)(Runtime*, Object**, unsigned int, Object*[static MAX_RETS], Error*), int argc, Heap *heap, Error *error)
{
assert(callback != NULL);
NativeFunctionObject *func = (NativeFunctionObject*) Heap_Malloc(heap, &t_nfunc, error);
if(func == NULL)
return NULL;
func->runtime = runtime;
func->callback = callback;
func->argc = argc;
return (Object*) func;
}
+73
View File
@@ -0,0 +1,73 @@
#include <string.h>
#include <unistd.h>
#include "path.h"
#include "../utils/defs.h"
#include "../utils/path.h"
// Returns the length written in buff (not considering the zero byte)
size_t Runtime_GetCurrentScriptFolder(Runtime *runtime, char *buff, size_t buffsize)
{
const char *path = Runtime_GetCurrentScriptAbsolutePath(runtime);
if(path == NULL) {
if(getcwd(buff, buffsize) == NULL)
return 0;
size_t cwdlen = strlen(buff);
if (buff[cwdlen-1] == '/')
return cwdlen;
else {
if (cwdlen+1 >= buffsize)
return 0;
buff[cwdlen] = '/';
return cwdlen+1;
}
}
// This following block is a custom implementation
// of [dirname], which doesn't write into the input
// string and is way buggier. It will for sure give
// problems in the future!!
size_t dir_len;
{
// This is buggy code!!
size_t path_len = strlen(path);
ASSERT(path_len > 0); // Not empty
ASSERT(Path_IsAbsolute(path)); // Is absolute
ASSERT(path[path_len-1] != '/'); // Doesn't end with a slash.
size_t popped = 0;
while(path[path_len-1-popped] != '/')
popped += 1;
ASSERT(path_len > popped);
dir_len = path_len - popped;
ASSERT(dir_len < path_len);
ASSERT(path[dir_len-1] == '/');
}
if(dir_len >= buffsize)
return 0;
memcpy(buff, path, dir_len);
buff[dir_len] = '\0';
return dir_len;
}
const char *Runtime_GetCurrentScriptAbsolutePath(Runtime *runtime)
{
Executable *exe = Runtime_GetMostRecentExecutable(runtime);
if (exe == NULL)
return NULL;
Source *src = Executable_GetSource(exe);
if(src == NULL)
return NULL;
const char *path = Source_GetAbsolutePath(src);
if(path == NULL)
return NULL;
ASSERT(path[0] != '\0');
return path;
}
+4
View File
@@ -0,0 +1,4 @@
#include <stddef.h>
#include "runtime.h"
const char *Runtime_GetCurrentScriptAbsolutePath(Runtime *runtime);
size_t Runtime_GetCurrentScriptFolder(Runtime *runtime, char *buff, size_t buffsize);
File diff suppressed because it is too large Load Diff
+12
View File
@@ -0,0 +1,12 @@
#include "runtime.h"
int runSource(Runtime *runtime, Source *source, Object *rets[static MAX_RETS], Error *error);
int runBytecodeSource(Runtime *runtime, Source *source, Object *rets[static MAX_RETS], Error *error);
bool runFile(Runtime *runtime, const char *file, Error *error);
bool runString(Runtime *runtime, const char *string, Error *error);
bool runBytecodeFile(Runtime *runtime, const char *file, Error *error);
bool runBytecodeString(Runtime *runtime, const char *string, Error *error);
int runFileEx(Runtime *runtime, const char *file, Object *rets[static MAX_RETS], Error *error);
int runStringEx(Runtime *runtime, const char *name, const char *string, Object *rets[static MAX_RETS], Error *error);
int runBytecodeFileEx(Runtime *runtime, const char *file, Object *rets[static MAX_RETS], Error *error);
int runBytecodeStringEx(Runtime *runtime, const char *name, const char *string, Object *rets[static MAX_RETS], Error *error);
int runFileRelativeToScript(Runtime *runtime, const char *file, Object *rets[static MAX_RETS], Error *error);
+448 -1340
View File
File diff suppressed because it is too large Load Diff
+27 -9
View File
@@ -40,6 +40,7 @@
typedef struct xRuntime Runtime;
typedef struct xSnapshot Snapshot;
typedef struct StaticMapSlot StaticMapSlot;
typedef struct {
bool (*func)(Runtime*, void*);
@@ -48,31 +49,49 @@ typedef struct {
typedef struct {
bool time;
size_t heap;
size_t stack;
RuntimeCallback callback;
} RuntimeConfig;
Runtime* Runtime_New(int heap_size, RuntimeConfig config);
Runtime* Runtime_New2(Heap *heap, bool free_it, RuntimeConfig config);
Runtime* Runtime_New(RuntimeConfig config);
void Runtime_Free(Runtime *runtime);
_Bool Runtime_Pop(Runtime *runtime, Error *error, unsigned int n);
_Bool Runtime_Push(Runtime *runtime, Error *error, Object *obj);
Object *Runtime_Top(Runtime *runtime, int n);
bool Runtime_Pop (Runtime *runtime, Error *error, Object **p, unsigned int n);
bool Runtime_Push(Runtime *runtime, Error *error, Object *obj);
bool Runtime_PushFrame(Runtime *runtime, Error *error, Object *closure, Executable *exe, int index);
bool Runtime_PushNativeFrame(Runtime *runtime, Error *error);
bool Runtime_PushFailedFrame(Runtime *runtime, Error *error, Source *source, int offset);
bool Runtime_PopFrame(Runtime *runtime);
void Runtime_SetInstructionIndex(Runtime *runtime, int index);
bool Runtime_SetVariable(Runtime *runtime, Error *error, const char *name, Object *value);
bool Runtime_GetVariable(Runtime *runtime, Error *error, const char *name, Object **value);
Object *Runtime_GetLocals(Runtime *runtime);
Object *Runtime_GetClosure(Runtime *runtime);
size_t Runtime_GetFrameStackUsage(Runtime *runtime);
bool Runtime_WasInterrupted(Runtime *runtime);
RuntimeCallback Runtime_GetCallback(Runtime *runtime);
bool Runtime_CollectGarbage(Runtime *runtime, Error *error);
void Runtime_PrintStackTrace(Runtime *runtime, FILE *stream);
void Runtime_Interrupt(Runtime *runtime);
Heap* Runtime_GetHeap(Runtime *runtime);
Stack* Runtime_GetStack(Runtime *runtime);
Object* Runtime_GetBuiltins(Runtime *runtime);
void Runtime_SetBuiltins(Runtime *runtime, Object *builtins);
int Runtime_GetCurrentIndex(Runtime *runtime);
Executable *Runtime_GetCurrentExecutable(Runtime *runtime);
Executable *Runtime_GetMostRecentExecutable(Runtime *runtime);
size_t Runtime_GetCurrentScriptFolder(Runtime *runtime, char *buff, size_t buffsize);
const char *Runtime_GetCurrentScriptAbsolutePath(Runtime *runtime);
TimingTable *Runtime_GetTimingTable(Runtime *runtime);
RuntimeConfig Runtime_GetDefaultConfigs();
bool Runtime_plugBuiltins(Runtime *runtime, Object *object, Error *error);
int Runtime_runSource(Runtime *runtime, Source *source, Object *rets[static MAX_RETS], Error *error);
int Runtime_runBytecodeSource(Runtime *runtime, Source *source, Object *rets[static MAX_RETS], Error *error);
Snapshot *Snapshot_New(Runtime *runtime);
void Snapshot_Free(Snapshot *snapshot);
void Snapshot_Print(Snapshot *snapshot, FILE *fp);
int run(Runtime *runtime, Error *error, Executable *exe, int index, Object *closure, Object **argv, int argc, Object *rets[static MAX_RETS]);
typedef enum {
SM_END,
@@ -87,8 +106,6 @@ typedef enum {
SM_OBJECT,
} StaticMapSlotKind;
typedef struct StaticMapSlot StaticMapSlot;
struct StaticMapSlot {
const char *name;
StaticMapSlotKind kind;
@@ -117,5 +134,6 @@ typedef struct {
void RuntimeError_Init(RuntimeError *error, Runtime *runtime);
void RuntimeError_Free(RuntimeError *error);
void RuntimeError_Print(RuntimeError *error, ErrorType type_if_unspecified, FILE *stream);
#endif
-59
View File
@@ -1,59 +0,0 @@
/* +--------------------------------------------------------------------------+
** | _ _ _ |
** | | \ | | (_) |
** | | \| | ___ _ __ _ |
** | | . ` |/ _ \| |/ _` | |
** | | |\ | (_) | | (_| | |
** | |_| \_|\___/| |\__,_| |
** | _/ | |
** | |__/ |
** +--------------------------------------------------------------------------+
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
** +--------------------------------------------------------------------------+
** | This file is part of The Noja Interpreter. |
** | |
** | The Noja Interpreter is free software: you can redistribute it and/or |
** | modify it under the terms of the GNU General Public License as published |
** | by the Free Software Foundation, either version 3 of the License, or (at |
** | your option) any later version. |
** | |
** | The Noja Interpreter is distributed in the hope that it will be useful, |
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
** | Public License for more details. |
** | |
** | You should have received a copy of the GNU General Public License along |
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
** +--------------------------------------------------------------------------+
*/
#include <assert.h>
#include "runtime.h"
static void on_report(Error *error)
{
assert(error != NULL);
RuntimeError *error2 = (RuntimeError*) error;
if(error2->runtime != NULL)
error2->snapshot = Snapshot_New(error2->runtime);
}
void RuntimeError_Init(RuntimeError *error, Runtime *runtime)
{
assert(error != NULL);
Error_Init2(&error->base, on_report);
error->runtime = runtime;
error->snapshot = NULL;
}
void RuntimeError_Free(RuntimeError *error)
{
if(error->snapshot)
Snapshot_Free(error->snapshot);
Error_Free(&error->base);
}
+31
View File
@@ -0,0 +1,31 @@
#include "serialize_profiling.h"
void Runtime_SerializeProfilingResultsToStream(Runtime *runtime, FILE *stream)
{
TimingTable *table = Runtime_GetTimingTable(runtime);
if (table == NULL)
return; // Runtime wasn't profiling
size_t count;
const FunctionExecutionSummary *summary = TimingTable_getSummary(table, &count);
for (size_t i = 0; i < count; i++) {
if (summary[i].calls > 0) {
fprintf(stream, "%20s - %s - %ld calls - %.2lfus\n",
summary[i].name,
Source_GetName(summary[i].src),
summary[i].calls,
summary[i].time * 1000000);
}
}
}
bool Runtime_SerializeProfilingResultsToFile(Runtime *runtime, const char *file)
{
FILE *stream = fopen(file, "wb");
if (stream == NULL)
return false;
Runtime_SerializeProfilingResultsToStream(runtime, stream);
fclose(stream);
return true;
}
+4
View File
@@ -0,0 +1,4 @@
#include "runtime.h"
void Runtime_SerializeProfilingResultsToStream(Runtime *runtime, FILE *stream);
bool Runtime_SerializeProfilingResultsToFile(Runtime *runtime, const char *file);