new built-in getCurrentScriptDirectory; new cli option syntax; added -p option to profile programs and -H to set heap size; fixed a bug in string.cat

This commit is contained in:
Francesco Cozzuto
2023-01-19 20:14:23 +01:00
parent 37420cdea2
commit 9cc7121cd9
21 changed files with 631 additions and 255 deletions
+2
View File
@@ -16,3 +16,5 @@ lib*.a
embedder
*_n.c
profiling-results.txt
+2 -2
View File
@@ -67,10 +67,10 @@ The `noja` executable will be generated, which is a CLI that runs Noja code.
## Usage
You can run files by doing:
```sh
location/of/noja run <filename>
location/of/noja <filename>
```
or you can run strings by doing:
```sh
location/of/noja run inline <string>
location/of/noja -i <string>
```
+4 -2
View File
@@ -5,6 +5,8 @@ Router = {table: Map, plug: Callable, solve: Callable};
fun loadFile(path: String) {
print("path=", path, "\n");
stream, error = files.openFile(path, files.READ);
if error != none:
return none, error;
@@ -83,8 +85,8 @@ fun new() {
if isCallable(method_item):
res = method_item(req);
else {
cwd = getCurrentWorkingDirectory();
file = path.join(cwd, method_item);
csd = getCurrentScriptDirectory();
file = path.join(csd, method_item);
data, error = loadFile(file);
if error == none:
res = respond(200, data);
+38 -1
View File
@@ -251,6 +251,43 @@ fun parse(src: String) {
return val, err;
}
fun readFile(name: String) {
stream, error = files.openFile(name, files.READ);
if error != none:
return error;
text = "";
size = 256;
do {
size = size * 2;
buff = buffer.new(size);
read_bytes, error = files.read(stream, buff);
if error != none:
return error;
text2, error = buffer.toString(buff);
if error != none:
return none, error;
assert(text2 != none);
text = string.cat(text, text2);
} while count(buff) == read_bytes;
return text;
}
fun parseFile(name: String) {
text, error = readFile(name);
if error != none:
return none, error;
assert(text != none);
result, error = parse(text);
return result, error;
}
fun test() {
compareAny = import("compare.noja").compareAny;
@@ -330,4 +367,4 @@ fun test() {
print(" total: ", total, "\n");
}
return {parse: parse, test: test};
return {parse: parse, parseFile: parseFile, test: test};
+2 -2
View File
@@ -8,13 +8,13 @@ Scanner = {
consumeSpaces: Callable
};
fun newScanner(src: String) {
fun isSpace(c: String)
return c == ' '
or c == '\t'
or c == '\n';
fun newScanner(src: String) {
scan = {
src: src,
i: 0,
+142 -86
View File
@@ -27,114 +27,170 @@
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
** +--------------------------------------------------------------------------+
*/
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include "../lib/noja.h"
static const char usage[] =
"Usage patterns:\n"
" $ noja run file.noja\n"
" $ noja run inline \"print('some noja code');\"\n"
" $ noja dis file.noja\n"
" $ noja dis inline \"print('some noja code');\"\n"
" $ noja asm file.noja\n"
" $ noja asm inline \"PUSHINT 5; PUSHFLT 1.0; ADD; POP 1;\"\n";
static void usage(FILE *stream, const char *name)
{
fprintf(stream,
"USAGE\n"
" $ %s [-h | -o <file> | -p | -H <heap size> | {-d | -a}] [--] <file>\n", name);
}
static void help(FILE *stream, const char *name)
{
usage(stream, name);
fprintf(stream,
"OPTIONS\n"
" -h, --help Show this message\n"
" -d, --disassembly Output the bytecode associated to noja code\n"
" -i, --inline Execute a string of code instead of a file\n"
" -a, --assembly Specify that the source is bytecode and not noja code\n"
" -p, --profile Profile the execution of the source (can't be used with -d)\n"
" -o, --output Specify the output file of -p or -d\n"
" -H, --heap <size> Heap size\n"
"\n");
}
typedef enum {
Mode_DISASSEMBLy,
Mode_ASSEMBLY,
Mode_DEFAULT,
Mode_HELP,
} Mode;
int main(int argc, char **argv)
{
assert(argc > 0);
Mode mode = Mode_DEFAULT;
bool profile = false;
bool no_file = false;
const char *output = NULL;
const char *input = NULL;
size_t heap = 1024 * 1024;
if(argc == 1)
{
// $ noja
fprintf(stderr, "Error: Incorrect usage.\n\n");
fprintf(stderr, usage);
for (int i = 1; i < argc; i++) {
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
mode = Mode_HELP;
} else if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--disassembly")) {
mode = Mode_DISASSEMBLy;
} else if (!strcmp(argv[i], "-i") || !strcmp(argv[i], "--inline")) {
no_file = true;
} else if (!strcmp(argv[i], "-a") || !strcmp(argv[i], "--assembly")) {
mode = Mode_ASSEMBLY;
} else if (!strcmp(argv[i], "-p") || !strcmp(argv[i], "--profile")) {
profile = true;
} else if (!strcmp(argv[i], "-o") || !strcmp(argv[i], "--output")) {
if (i+1 == argc || argv[i+1][0] == '-') {
fprintf(stderr, "Missing file path after %s option\n", argv[i]);
usage(stderr, argv[0]);
return -1;
}
output = argv[++i];
} else if (!strcmp(argv[i], "-H") || !strcmp(argv[i], "--heap")) {
if (i+1 == argc || argv[i+1][0] == '-') {
fprintf(stderr, "Missing byte count after %s option\n", argv[i]);
usage(stderr, argv[0]);
return -1;
}
heap = atoi(argv[++i]);
if (heap == 0) {
fprintf(stderr, "Invalid heap size\n");
usage(stderr, argv[0]);
return -1;
}
if(!strcmp(argv[1], "run"))
{
if(argc == 2)
{
fprintf(stderr, "Error: Missing source file.\n");
return -1;
} else {
input = argv[i];
break;
}
}
_Bool r;
int code;
switch (mode) {
if(!strcmp(argv[2], "inline"))
{
if(argc == 3)
{
fprintf(stderr, "Error: Missing source string.\n");
return -1;
case Mode_HELP:
help(stdout, argv[0]);
code = 0;
break;
case Mode_DEFAULT:
if (input == NULL) {
fprintf(stderr, "No input file");
code = -1;
break;
}
r = NOJA_runString(argv[3]);
if (profile) {
if (no_file) code = NOJA_profileString(input, heap) ? 0 : -1;
else code = NOJA_profileFile(input, heap) ? 0 : -1;
} else {
if (output != NULL)
fprintf(stderr, "Ignoring option -o\n");
if (no_file) code = NOJA_runString(input, heap) ? 0 : -1;
else code = NOJA_runFile(input, heap) ? 0 : -1;
}
else
r = NOJA_runFile(argv[2]);
return r ? 0 : -1;
break;
case Mode_ASSEMBLY:
if (input == NULL) {
fprintf(stderr, "No assembly input file");
code = -1;
break;
}
if (profile) {
if (no_file) code = NOJA_profileAssemblyString(input, heap) ? 0 : -1;
else code = NOJA_profileAssemblyFile(input, heap) ? 0 : -1;
} else {
if (output != NULL)
fprintf(stderr, "Ignoring option -o\n");
if (no_file) code = NOJA_runAssemblyString(input, heap) ? 0 : -1;
else code = NOJA_runAssemblyFile(input, heap) ? 0 : -1;
}
break;
case Mode_DISASSEMBLy:
if (input == NULL) {
fprintf(stderr, "No disassembly input file");
code = -1;
break;
}
if (no_file) code = NOJA_dumpStringBytecode(input) ? 0 : -1;
else code = NOJA_dumpFileBytecode(input) ? 0 : -1;
break;
}
if(!strcmp(argv[1], "asm"))
{
if(argc == 2)
{
fprintf(stderr, "Error: Missing source file.\n");
return -1;
return code;
}
_Bool r;
/*
noja [-o <file> | -p | {-d | -a}] [--] <file>
if(!strcmp(argv[2], "inline"))
{
if(argc == 3)
{
fprintf(stderr, "Error: Missing source string.\n");
return -1;
}
r = NOJA_runAssemblyString(argv[3]);
}
else
r = NOJA_runAssemblyFile(argv[2]);
return r ? 0 : -1;
}
-o can only be used with -d or -a
if(!strcmp(argv[1], "dis"))
{
if(argc == 2)
{
fprintf(stderr, "Error: Missing source file.\n");
return -1;
}
-h --help
-d --disassembly disassembly
-i --inline inline
-a --assembly
-p --profile
-o --output
_Bool r;
noja <file>
noja -i <code>
if(!strcmp(argv[2], "inline"))
{
if(argc == 3)
{
fprintf(stderr, "Error: Missing source string.\n");
return -1;
}
r = NOJA_dumpStringBytecode(argv[3]);
}
else
r = NOJA_dumpFileBytecode(argv[2]);
return r ? 0 : -1;
}
if(!strcmp(argv[1], "help"))
{
fprintf(stdout, usage);
return 0;
}
fprintf(stderr, "Error: Incorrect usage.\n\n");
fprintf(stderr, usage);
return -1;
}
*/
+34
View File
@@ -61,6 +61,38 @@ static int bin_getCurrentWorkingDirectory(Runtime *runtime, Object **argv, unsig
return returnValues2(error, runtime, rets, "s", path);
}
static int bin_getCurrentScriptDirectory(Runtime *runtime, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Error *error)
{
UNUSED(argc);
UNUSED(argv);
ASSERT(argc == 0);
const char *path = Runtime_GetCurrentScriptAbsolutePath(runtime);
if (path == NULL)
return returnValues2(error, runtime, rets, "n");
size_t len = strlen(path);
while (path[len-1] != '/') // This underflows if path has len 0 or doesn't contain a "/".
len--;
Object *obj = Object_FromString(path, len, Runtime_GetHeap(runtime), error);
if (obj == NULL)
return -1;
rets[0] = obj;
return 1;
}
static int bin_getCurrentScriptLocation(Runtime *runtime, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Error *error)
{
UNUSED(argc);
UNUSED(argv);
ASSERT(argc == 0);
const char *path = Runtime_GetCurrentScriptAbsolutePath(runtime);
if (path == NULL)
return returnValues2(error, runtime, rets, "n");
return returnValues2(error, runtime, rets, "s", path);
}
static int bin_typename(Runtime *runtime, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Error *error)
{
UNUSED(argc);
@@ -364,5 +396,7 @@ StaticMapSlot bins_basic[] = {
{ "typename", SM_FUNCT, .as_funct = bin_typename, .argc = 1, },
{ "keysof", SM_FUNCT, .as_funct = bin_keysof, .argc = 1, },
{ "getCurrentWorkingDirectory", SM_FUNCT, .as_funct = bin_getCurrentWorkingDirectory, .argc = 0 },
{ "getCurrentScriptDirectory", SM_FUNCT, .as_funct = bin_getCurrentScriptDirectory, .argc = 0 },
{ "getCurrentScriptLocation", SM_FUNCT, .as_funct = bin_getCurrentScriptLocation, .argc = 0 },
{ NULL, SM_END, {}, {} },
};
+1 -1
View File
@@ -163,7 +163,7 @@ static int bin_accept(Runtime *runtime, Object **argv, unsigned int argc, Object
int fd = pargs[0].as_int;
struct sockaddr_in new_addr;
socklen_t new_addr_size;
socklen_t new_addr_size = sizeof(new_addr);
int new_fd = accept(fd, (struct sockaddr*) &new_addr, &new_addr_size);
if (new_fd < 0)
return returnValues2(error, runtime, rets, "ns", strerror(errno));
+7 -7
View File
@@ -124,13 +124,6 @@ fun stringFromMap(map: Map, can_use_method=true) {
return s;
}
fun integerFromDigit(char: String) {
res = ord(char) - ord('0');
if res < 0 or res > 9:
error("String isn't a digit");
return res;
}
fun GenericIterator(T) return {
set : T,
keys : List,
@@ -191,6 +184,13 @@ return {
error(cat("Don't know how to convert ", typename(value), " to a string"));
}
fun integerFromDigit(char: String) {
res = ord(char) - ord('0');
if res < 0 or res > 9:
error("String isn't a digit");
return res;
}
fun isCallable(x)
return istypeof(Callable, x);
+7 -6
View File
@@ -92,10 +92,13 @@ static int bin_cat(Runtime *runtime, Object **argv, unsigned int argc, Object *r
return -1;
}
total_count += Object_Count(argv[i], error);
size_t length;
Object_GetString(argv[i], &length);
total_count += length;
if(error->occurred)
return -1;
// The Object_Count of a string doesn't match the
// byte count, but the second argument of Object_GetString
// does.
}
char starting[128];
@@ -112,8 +115,6 @@ static int bin_cat(Runtime *runtime, Object **argv, unsigned int argc, Object *r
}
}
Object *result = NULL;
for(unsigned int i = 0, written = 0; i < argc; i += 1)
{
size_t length;
@@ -125,7 +126,7 @@ static int bin_cat(Runtime *runtime, Object **argv, unsigned int argc, Object *r
buffer[total_count] = '\0';
result = Object_FromString(buffer, total_count, Runtime_GetHeap(runtime), error);
Object *result = Object_FromString(buffer, total_count, Runtime_GetHeap(runtime), error);
if(starting != buffer)
free(buffer);
+1 -1
View File
@@ -110,7 +110,7 @@ static const InstrInfo instr_table[] = {
INSTR(PUSHTRU)
INSTR(PUSHFLS)
INSTR(PUSHNNE)
INSTR(PUSHFUN, OPTP_IDX, OPTP_INT)
INSTR(PUSHFUN, OPTP_IDX, OPTP_INT, OPTP_STRING)
INSTR(PUSHLST, OPTP_INT)
INSTR(PUSHMAP, OPTP_INT)
INSTR(PUSHTYP)
+6 -5
View File
@@ -255,18 +255,19 @@ static void emitInstrForArgumentNode(CodegenContext *ctx, ArgumentNode *arg, int
emitInstr_POP1(ctx, arg->base.offset, arg->base.length);
}
static void emitInstrForFuncExprNode(CodegenContext *ctx, FuncExprNode *func)
static void emitInstrForFuncExprNode(CodegenContext *ctx, FuncExprNode *func, const char *name)
{
Label *label_func = Label_New(ctx);
Label *label_jump = Label_New(ctx);
// Push function.
{
Operand ops[2] = {
Operand ops[3] = {
{ .type = OPTP_PROMISE, .as_promise = Label_ToPromise(label_func) },
{ .type = OPTP_INT, .as_int = func->argc },
{ .type = OPTP_STRING, .as_string = name },
};
CodegenContext_EmitInstr(ctx, OPCODE_PUSHFUN, ops, 2, func->base.base.offset, func->base.base.length);
CodegenContext_EmitInstr(ctx, OPCODE_PUSHFUN, ops, 3, func->base.base.offset, func->base.base.length);
}
emitInstr_JUMP(ctx, label_jump, func->base.base.offset, func->base.base.length); // Jump after the function code
Label_SetHere(label_func, ctx); // This is the function code index.
@@ -302,7 +303,7 @@ static void emitInstrForFuncExprNode(CodegenContext *ctx, FuncExprNode *func)
static void emitInstrForFuncDeclNode(CodegenContext *ctx, FuncDeclNode *func)
{
emitInstrForFuncExprNode(ctx, func->expr);
emitInstrForFuncExprNode(ctx, func->expr, func->name->val);
emitInstr_ASS(ctx, func->name->val, func->base.offset, func->base.length); // Assign variable
emitInstr_POP1(ctx, func->base.offset, func->base.length); // Pop function object
}
@@ -574,7 +575,7 @@ static void emitInstrForExprNode(CodegenContext *ctx, ExprNode *expr,
return;
case EXPR_FUNC:
emitInstrForFuncExprNode(ctx, (FuncExprNode*) expr);
emitInstrForFuncExprNode(ctx, (FuncExprNode*) expr, "???");
return;
case EXPR_SELECT:
+2 -2
View File
@@ -489,12 +489,12 @@ static inline TokenKind next(Context *ctx, const char *file, int line)
Token *prev = ctx->token;
ctx->token = ctx->token->next;
/*
fprintf(stderr, "NEXT [%.*s] -> [%.*s] from %s:%d\n",
prev->length, ctx->src + prev->offset,
ctx->token->length, ctx->src + ctx->token->offset,
file, line);
*/
return current(ctx);
}
+86 -19
View File
@@ -5,6 +5,7 @@
#include "compiler/compile.h"
#include "assembler/assemble.h"
#include "builtins/basic.h"
#include "runtime/timing.h"
#include "noja.h"
static void print_error(const char *type, Error *error)
@@ -33,10 +34,23 @@ static void print_error(const char *type, Error *error)
fprintf(stderr, "\n");
}
static _Bool interpret(Executable *exe)
{
Runtime *runt = Runtime_New(-1, 1024*1024, NULL, NULL);
#include <signal.h>
Runtime *runt = NULL;
static void signalHandler(int signo)
{
(void) signo;
if (runt != NULL)
Runtime_Interrupt(runt);
}
static _Bool interpret(Executable *exe, bool time, size_t heap)
{
RuntimeConfig config = Runtime_GetDefaultConfigs();
config.time = time;
runt = Runtime_New(heap, config);
if(runt == NULL)
{
Error error;
@@ -46,6 +60,8 @@ static _Bool interpret(Executable *exe)
Error_Free(&error);
return 0;
}
signal(SIGINT, signalHandler);
signal(SIGTERM, signalHandler);
// We use a [RuntimeError] instead of a simple [Error]
// because the [RuntimeError] makes a snapshot of the
@@ -158,6 +174,37 @@ static _Bool interpret(Executable *exe)
RuntimeError_Free(&error);
}
TimingTable *table = Runtime_GetTimingTable(runt);
if (table != NULL) {
const char *file = "profiling-results.txt";
FILE *stream = fopen(file, "wb");
if (stream == NULL) {
fprintf(stderr, "Failed to serialize profiling results\n");
} else {
const FunctionExecutionSummary *summary;
size_t count;
summary = TimingTable_getSummary(table, &count);
assert(summary != NULL);
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);
}
}
fclose(stream);
fprintf(stderr, "Wrote profiling result to %s\n", file);
}
}
Runtime_Free(runt);
return retc > -1;
}
@@ -193,7 +240,7 @@ static _Bool disassemble(Source *src)
return 1;
}
static _Bool interpret_file(const char *file)
static _Bool interpret_file(const char *file, bool time, size_t heap)
{
Error error;
Error_Init(&error);
@@ -213,14 +260,14 @@ static _Bool interpret_file(const char *file)
return 0;
}
_Bool r = interpret(exe);
_Bool r = interpret(exe, time, heap);
Executable_Free(exe);
Source_Free(src);
return r;
}
static _Bool interpret_code(const char *code)
static _Bool interpret_code(const char *code, bool time, size_t heap)
{
Error error;
Error_Init(&error);
@@ -241,14 +288,14 @@ static _Bool interpret_code(const char *code)
return 0;
}
_Bool r = interpret(exe);
_Bool r = interpret(exe, time, heap);
Executable_Free(exe);
Source_Free(src);
return r;
}
static _Bool interpret_asm_file(const char *file)
static _Bool interpret_asm_file(const char *file, bool time, size_t heap)
{
Error error;
Error_Init(&error);
@@ -271,7 +318,7 @@ static _Bool interpret_asm_file(const char *file)
return 0;
}
_Bool r = interpret(exe);
_Bool r = interpret(exe, time, heap);
Executable_Free(exe);
Source_Free(src);
@@ -279,7 +326,7 @@ static _Bool interpret_asm_file(const char *file)
return r;
}
static _Bool interpret_asm_code(const char *code)
static _Bool interpret_asm_code(const char *code, bool time, size_t heap)
{
Error error;
Error_Init(&error);
@@ -302,7 +349,7 @@ static _Bool interpret_asm_code(const char *code)
return 0;
}
_Bool r = interpret(exe);
_Bool r = interpret(exe, time, heap);
Executable_Free(exe);
Source_Free(src);
@@ -352,14 +399,14 @@ static _Bool disassemble_code(const char *code)
return r;
}
_Bool NOJA_runString(const char *str)
_Bool NOJA_runString(const char *str, size_t heap)
{
return interpret_code(str);
return interpret_code(str, false, heap);
}
_Bool NOJA_runFile(const char *file)
_Bool NOJA_runFile(const char *file, size_t heap)
{
return interpret_file(file);
return interpret_file(file, false, heap);
}
_Bool NOJA_dumpFileBytecode(const char *file)
@@ -372,12 +419,32 @@ _Bool NOJA_dumpStringBytecode(const char *str)
return disassemble_code(str);
}
_Bool NOJA_runAssemblyFile(const char *file)
_Bool NOJA_runAssemblyFile(const char *file, size_t heap)
{
return interpret_asm_file(file);
return interpret_asm_file(file, false, heap);
}
_Bool NOJA_runAssemblyString(const char *str)
_Bool NOJA_runAssemblyString(const char *str, size_t heap)
{
return interpret_asm_code(str);
return interpret_asm_code(str, false, heap);
}
_Bool NOJA_profileString(const char *str, size_t heap)
{
return interpret_code(str, true, heap);
}
_Bool NOJA_profileFile(const char *file, size_t heap)
{
return interpret_file(file, true, heap);
}
_Bool NOJA_profileAssemblyFile(const char *file, size_t heap)
{
return interpret_asm_file(file, true, heap);
}
_Bool NOJA_profileAssemblyString(const char *str, size_t heap)
{
return interpret_asm_code(str, true, heap);
}
+8 -4
View File
@@ -1,9 +1,13 @@
#ifndef NOJA_H
#define NOJA_H
_Bool NOJA_runFile (const char *file);
_Bool NOJA_runString(const char *str);
_Bool NOJA_runAssemblyFile(const char *file);
_Bool NOJA_runAssemblyString(const char *str);
_Bool NOJA_runFile (const char *file, size_t heap);
_Bool NOJA_runString(const char *str, size_t heap);
_Bool NOJA_runAssemblyFile(const char *file, size_t heap);
_Bool NOJA_runAssemblyString(const char *str, size_t heap);
_Bool NOJA_dumpFileBytecode(const char *file);
_Bool NOJA_dumpStringBytecode(const char *str);
_Bool NOJA_profileFile(const char *file, size_t heap);
_Bool NOJA_profileString(const char *str, size_t heap);
_Bool NOJA_profileAssemblyFile(const char *file, size_t heap);
_Bool NOJA_profileAssemblyString(const char *str, size_t heap);
#endif /* NOJA_H */
+18 -13
View File
@@ -355,17 +355,7 @@ _Bool Heap_StartCollection(Heap *heap, Error *error)
return 1;
}
_Bool Heap_StopCollection(Heap *heap)
{
assert(heap->collecting == 1);
if(heap->collection_failed)
{
free(heap->old_body);
return 0;
}
/* Call destructors here */
static bool callDestructors(Heap *heap)
{
int i = 0;
@@ -385,7 +375,7 @@ _Bool Heap_StopCollection(Heap *heap)
heap->pend[i].destructor(obj, heap->error);
if(heap->error->occurred)
return 0; // There will be leaks.
return false; // There will be leaks.
heap->pend[i] = heap->pend[heap->pend_used-1];
heap->pend_used -= 1;
@@ -403,20 +393,35 @@ _Bool Heap_StopCollection(Heap *heap)
heap->pend_size /= 2;
}
}
return true;
}
static void freeOverflowAllocations(Heap *heap)
{
while(heap->old_oflow)
{
OflowAlloc *prev = heap->old_oflow->prev;
free(heap->old_oflow);
heap->old_oflow = prev;
}
}
_Bool Heap_StopCollection(Heap *heap)
{
assert(heap->collecting == 1);
if(!callDestructors(heap))
return false;
freeOverflowAllocations(heap);
free(heap->old_body);
heap->collecting = 0;
heap->objcount = heap->movedcount;
return 1;
return !heap->collection_failed;
}
void Heap_CollectExtension(void **referer, unsigned int size, void *userp)
+32 -1
View File
@@ -28,18 +28,22 @@
** +--------------------------------------------------------------------------+
*/
#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)
@@ -108,8 +112,26 @@ static int call(Object *self, Object **argv, unsigned int argc, Object *rets[sta
// 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)
@@ -157,7 +179,7 @@ static TypeObject t_func = {
* 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, Executable *exe, int index, int argc, Object *closure, Heap *heap, Error *error)
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);
@@ -180,10 +202,19 @@ Object *Object_FromNojaFunction(Runtime *runtime, Executable *exe, int index, in
}
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;
}
+63 -43
View File
@@ -31,6 +31,7 @@
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#include "runtime.h"
#include "../utils/path.h"
#include "../utils/defs.h"
@@ -49,16 +50,25 @@ struct xFrame {
};
struct xRuntime {
void *callback_userp;
_Bool (*callback_addr)(Runtime*, void*);
bool interrupt;
RuntimeCallback callback;
_Bool free_heap;
Object *builtins;
int depth;
Frame *frame;
Stack *stack;
Heap *heap;
TimingTable *timing;
};
RuntimeConfig Runtime_GetDefaultConfigs()
{
return (RuntimeConfig) {
.stack = 1024,
.callback = { .func = NULL, .data = NULL },
.time = false,
};
}
// Returns the length written in buff (not considering the zero byte)
size_t Runtime_GetCurrentScriptFolder(Runtime *runtime, char *buff, size_t buffsize)
@@ -153,50 +163,69 @@ Executable *Runtime_GetCurrentExecutable(Runtime *runtime)
return runtime->frame->exe;
}
Runtime *Runtime_New2(int stack_size, Heap *heap, _Bool free_heap, void *callback_userp, _Bool (*callback_addr)(Runtime*, void*))
TimingTable *Runtime_GetTimingTable(Runtime *runtime)
{
if(stack_size < 0)
stack_size = 1024;
Runtime *runtime = malloc(sizeof(Runtime));
if(runtime != NULL)
{
runtime->heap = heap;
runtime->stack = Stack_New(stack_size);
if(runtime->stack == NULL)
{
Heap_Free(runtime->heap);
free(runtime);
return runtime->timing;
}
runtime->free_heap = free_heap;
runtime->callback_userp = callback_userp;
runtime->callback_addr = callback_addr;
void Runtime_Interrupt(Runtime *runtime)
{
runtime->interrupt = true;
}
Runtime *Runtime_New2(Heap *heap, _Bool free_it, RuntimeConfig config)
{
Runtime *runtime = malloc(sizeof(Runtime));
if (runtime == NULL)
return NULL;
runtime->stack = Stack_New(config.stack);
if(runtime->stack == NULL) {
if (free_it)
Heap_Free(heap);
free(runtime);
return NULL;
}
TimingTable *timing_table = NULL;
if (config.time) {
timing_table = TimingTable_new();
if (timing_table == NULL) {
if (free_it)
Heap_Free(heap);
free(runtime);
return NULL;
}
}
runtime->interrupt = false;
runtime->timing = timing_table;
runtime->heap = heap;
runtime->free_heap = free_it;
runtime->callback = config.callback;
runtime->builtins = NULL;
runtime->frame = NULL;
runtime->depth = 0;
}
return runtime;
}
Runtime *Runtime_New(int stack_size, int heap_size, void *callback_userp, _Bool (*callback_addr)(Runtime*, void*))
Runtime *Runtime_New(int heap_size, RuntimeConfig config)
{
if(heap_size < 0)
heap_size = 65536;
Heap *heap = Heap_New(heap_size);
if(heap == NULL) return NULL;
if(heap == NULL)
return NULL;
return Runtime_New2(stack_size, heap, 1, callback_userp, callback_addr);
return Runtime_New2(heap, 1, config);
}
void Runtime_Free(Runtime *runtime)
{
if (runtime->timing != NULL)
TimingTable_free(runtime->timing);
if(runtime->free_heap)
Heap_Free(runtime->heap);
Stack_Free(runtime->stack);
@@ -839,6 +868,7 @@ static _Bool step(Runtime *runtime, Error *error)
{
ASSERT(opc == 1);
ASSERT(ops[0].type == OPTP_STRING);
const char *name = ops[0].as_string;
if(runtime->frame->used == 0)
{
@@ -849,7 +879,7 @@ static _Bool step(Runtime *runtime, Error *error)
Object *val = Stack_Top(runtime->stack, 0);
ASSERT(val != NULL);
Object *key = Object_FromString(ops[0].as_string, -1, runtime->heap, error);
Object *key = Object_FromString(name, -1, runtime->heap, error);
if(key == NULL)
return 0;
@@ -1204,16 +1234,17 @@ static _Bool step(Runtime *runtime, Error *error)
case OPCODE_PUSHFUN:
{
ASSERT(opc == 2);
ASSERT(opc == 3);
ASSERT(ops[0].type == OPTP_IDX);
ASSERT(ops[1].type == OPTP_INT);
ASSERT(ops[2].type == OPTP_STRING);
Object *closure = Object_NewClosure(runtime->frame->closure, runtime->frame->locals, Runtime_GetHeap(runtime), error);
if(closure == NULL)
return 0;
Object *obj = Object_FromNojaFunction(runtime, runtime->frame->exe, ops[0].as_int, ops[1].as_int, closure, runtime->heap, error);
Object *obj = Object_FromNojaFunction(runtime, ops[2].as_string, runtime->frame->exe, ops[0].as_int, ops[1].as_int, closure, runtime->heap, error);
if(obj == NULL)
return 0;
@@ -1470,14 +1501,13 @@ int run(Runtime *runtime, Error *error,
// Run the code.
if(runtime->callback_addr != NULL)
{
if(!runtime->callback_addr(runtime, runtime->callback_userp))
if(runtime->interrupt || (runtime->callback.func != NULL && !runtime->callback.func(runtime, runtime->callback.data)))
Error_Report(error, 0, "Forced abortion");
else
while(step(runtime, error))
{
if(!runtime->callback_addr(runtime, runtime->callback_userp))
if(runtime->interrupt || (runtime->callback.func != NULL && !runtime->callback.func(runtime, runtime->callback.data)))
{
Error_Report(error, 0, "Forced abortion");
break;
@@ -1489,16 +1519,6 @@ int run(Runtime *runtime, Error *error,
if(!collect(runtime, error))
break;
}
}
else
while(step(runtime, error))
{
if(Heap_GetUsagePercentage(runtime->heap) > 100)
if(!collect(runtime, error))
break;
//printf("%2.2f%% percent.\n", Heap_GetUsagePercentage(runtime->heap));
}
// If an error occurred, we want to return NULL.
if(error->occurred == 0)
+20 -3
View File
@@ -32,6 +32,7 @@
#define RUNTIME_H
#include <stdio.h> // meh.. just for the definition of FILE.
#include "timing.h"
#include "../utils/error.h"
#include "../utils/stack.h"
#include "../objects/objects.h"
@@ -40,11 +41,23 @@
typedef struct xRuntime Runtime;
typedef struct xSnapshot Snapshot;
Runtime* Runtime_New(int stack_size, int heap_size, void *callback_userp, _Bool (*callback_addr)(Runtime*, void*));
Runtime* Runtime_New2(int stack_size, Heap *heap, _Bool free_heap, void *callback_userp, _Bool (*callback_addr)(Runtime*, void*));
typedef struct {
bool (*func)(Runtime*, void*);
void *data;
} RuntimeCallback;
typedef struct {
bool time;
size_t stack;
RuntimeCallback callback;
} RuntimeConfig;
Runtime* Runtime_New(int heap_size, RuntimeConfig config);
Runtime* Runtime_New2(Heap *heap, bool free_it, 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);
void Runtime_Interrupt(Runtime *runtime);
Heap* Runtime_GetHeap(Runtime *runtime);
Stack* Runtime_GetStack(Runtime *runtime);
Object* Runtime_GetBuiltins(Runtime *runtime);
@@ -53,6 +66,9 @@ int Runtime_GetCurrentIndex(Runtime *runtime);
Executable *Runtime_GetCurrentExecutable(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();
Snapshot *Snapshot_New(Runtime *runtime);
void Snapshot_Free(Snapshot *snapshot);
void Snapshot_Print(Snapshot *snapshot, FILE *fp);
@@ -90,8 +106,9 @@ struct StaticMapSlot {
};
Object *Object_NewStaticMap(StaticMapSlot slots[], void (*initfn)(StaticMapSlot[]), Runtime *runt, Error *error);
Object *Object_FromNojaFunction(Runtime *runtime, Executable *exe, int index, int argc, Object *closure, Heap *heap, Error *error);
Object *Object_FromNojaFunction(Runtime *runtime, const char *name, Executable *exe, int index, int argc, Object *closure, Heap *heap, Error *error);
Object *Object_FromNativeFunction(Runtime *runtime, int (*callback)(Runtime*, Object**, unsigned int, Object*[static MAX_RETS], Error*), int argc, Heap *heap, Error *error);
typedef struct {
Error base;
Runtime *runtime;
+76
View File
@@ -0,0 +1,76 @@
#include <stdlib.h>
#include <string.h>
#include "timing.h"
#include "../utils/defs.h"
struct TimingTable {
FunctionExecutionSummary *entries;
size_t size, used;
};
TimingTable *TimingTable_new()
{
TimingTable *table = malloc(sizeof(TimingTable));
if (table == NULL)
return NULL;
table->entries = NULL;
table->size = 0;
table->used = 0;
return table;
}
void TimingTable_free(TimingTable *table)
{
if (table != NULL) {
for(size_t i = 0; i < table->used; i++) {
free(table->entries[i].name);
Source_Free(table->entries[i].src);
}
free(table->entries);
free(table);
}
}
void TimingTable_sumCallTime(TimingTable *table, TimingID id, double time)
{
if (id < 0 || id >= (int) table->used)
abort();
table->entries[id].calls++;
table->entries[id].time += time;
}
TimingID TimingTable_newEntry(TimingTable *table, Source *src, size_t line, const char *name)
{
if (table->size == table->used) {
size_t new_size = 2 * table->size;
if (new_size == 0) new_size = 8;
void *temp = realloc(table->entries, new_size * sizeof(FunctionExecutionSummary));
if (temp == NULL) abort();
table->entries = temp;
table->size = new_size;
}
char *name2 = strdup(name);
if (name2 == NULL)
abort();
TimingID id = (int) table->used++;
table->entries[id].src = Source_Copy(src);
table->entries[id].line = line;
table->entries[id].name = name2;
table->entries[id].time = 0;
table->entries[id].calls = 0;
return id;
}
const FunctionExecutionSummary*
TimingTable_getSummary(TimingTable *table, size_t *count)
{
ASSERT(table != NULL && count != NULL);
*count = table->used;
return table->entries;
}
+23
View File
@@ -0,0 +1,23 @@
#ifndef TIMING_H
#define TIMING_H
#include <stddef.h>
#include "../utils/source.h"
typedef struct {
size_t calls;
double time;
size_t line;
char *name;
Source *src;
} FunctionExecutionSummary;
typedef int TimingID;
typedef struct TimingTable TimingTable;
TimingTable *TimingTable_new();
void TimingTable_free(TimingTable *table);
TimingID TimingTable_newEntry(TimingTable *table, Source *src, size_t line, const char *name);
void TimingTable_sumCallTime(TimingTable *table, TimingID id, double time);
TimingTable *TimingTable_getDefaultTable();
const FunctionExecutionSummary *TimingTable_getSummary(TimingTable *table, size_t *count);
#endif