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:
@@ -16,3 +16,5 @@ lib*.a
|
|||||||
embedder
|
embedder
|
||||||
|
|
||||||
*_n.c
|
*_n.c
|
||||||
|
|
||||||
|
profiling-results.txt
|
||||||
@@ -67,10 +67,10 @@ The `noja` executable will be generated, which is a CLI that runs Noja code.
|
|||||||
## Usage
|
## Usage
|
||||||
You can run files by doing:
|
You can run files by doing:
|
||||||
```sh
|
```sh
|
||||||
location/of/noja run <filename>
|
location/of/noja <filename>
|
||||||
```
|
```
|
||||||
|
|
||||||
or you can run strings by doing:
|
or you can run strings by doing:
|
||||||
```sh
|
```sh
|
||||||
location/of/noja run inline <string>
|
location/of/noja -i <string>
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ Router = {table: Map, plug: Callable, solve: Callable};
|
|||||||
|
|
||||||
fun loadFile(path: String) {
|
fun loadFile(path: String) {
|
||||||
|
|
||||||
|
print("path=", path, "\n");
|
||||||
|
|
||||||
stream, error = files.openFile(path, files.READ);
|
stream, error = files.openFile(path, files.READ);
|
||||||
if error != none:
|
if error != none:
|
||||||
return none, error;
|
return none, error;
|
||||||
@@ -83,8 +85,8 @@ fun new() {
|
|||||||
if isCallable(method_item):
|
if isCallable(method_item):
|
||||||
res = method_item(req);
|
res = method_item(req);
|
||||||
else {
|
else {
|
||||||
cwd = getCurrentWorkingDirectory();
|
csd = getCurrentScriptDirectory();
|
||||||
file = path.join(cwd, method_item);
|
file = path.join(csd, method_item);
|
||||||
data, error = loadFile(file);
|
data, error = loadFile(file);
|
||||||
if error == none:
|
if error == none:
|
||||||
res = respond(200, data);
|
res = respond(200, data);
|
||||||
|
|||||||
+38
-1
@@ -251,6 +251,43 @@ fun parse(src: String) {
|
|||||||
return val, err;
|
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() {
|
fun test() {
|
||||||
|
|
||||||
compareAny = import("compare.noja").compareAny;
|
compareAny = import("compare.noja").compareAny;
|
||||||
@@ -330,4 +367,4 @@ fun test() {
|
|||||||
print(" total: ", total, "\n");
|
print(" total: ", total, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
return {parse: parse, test: test};
|
return {parse: parse, parseFile: parseFile, test: test};
|
||||||
+2
-2
@@ -8,13 +8,13 @@ Scanner = {
|
|||||||
consumeSpaces: Callable
|
consumeSpaces: Callable
|
||||||
};
|
};
|
||||||
|
|
||||||
fun newScanner(src: String) {
|
|
||||||
|
|
||||||
fun isSpace(c: String)
|
fun isSpace(c: String)
|
||||||
return c == ' '
|
return c == ' '
|
||||||
or c == '\t'
|
or c == '\t'
|
||||||
or c == '\n';
|
or c == '\n';
|
||||||
|
|
||||||
|
fun newScanner(src: String) {
|
||||||
|
|
||||||
scan = {
|
scan = {
|
||||||
src: src,
|
src: src,
|
||||||
i: 0,
|
i: 0,
|
||||||
|
|||||||
+142
-86
@@ -27,114 +27,170 @@
|
|||||||
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||||
** +--------------------------------------------------------------------------+
|
** +--------------------------------------------------------------------------+
|
||||||
*/
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdbool.h>
|
||||||
#include "../lib/noja.h"
|
#include "../lib/noja.h"
|
||||||
|
|
||||||
static const char usage[] =
|
static void usage(FILE *stream, const char *name)
|
||||||
"Usage patterns:\n"
|
{
|
||||||
" $ noja run file.noja\n"
|
fprintf(stream,
|
||||||
" $ noja run inline \"print('some noja code');\"\n"
|
"USAGE\n"
|
||||||
" $ noja dis file.noja\n"
|
" $ %s [-h | -o <file> | -p | -H <heap size> | {-d | -a}] [--] <file>\n", name);
|
||||||
" $ 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 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)
|
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)
|
for (int i = 1; i < argc; i++) {
|
||||||
{
|
|
||||||
// $ noja
|
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
|
||||||
fprintf(stderr, "Error: Incorrect usage.\n\n");
|
|
||||||
fprintf(stderr, usage);
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!strcmp(argv[1], "run"))
|
} else {
|
||||||
{
|
input = argv[i];
|
||||||
if(argc == 2)
|
break;
|
||||||
{
|
}
|
||||||
fprintf(stderr, "Error: Missing source file.\n");
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_Bool r;
|
int code;
|
||||||
|
switch (mode) {
|
||||||
|
|
||||||
if(!strcmp(argv[2], "inline"))
|
case Mode_HELP:
|
||||||
{
|
help(stdout, argv[0]);
|
||||||
if(argc == 3)
|
code = 0;
|
||||||
{
|
break;
|
||||||
fprintf(stderr, "Error: Missing source string.\n");
|
|
||||||
return -1;
|
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
|
break;
|
||||||
r = NOJA_runFile(argv[2]);
|
|
||||||
return r ? 0 : -1;
|
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"))
|
return code;
|
||||||
{
|
|
||||||
if(argc == 2)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Error: Missing source file.\n");
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_Bool r;
|
/*
|
||||||
|
noja [-o <file> | -p | {-d | -a}] [--] <file>
|
||||||
|
|
||||||
if(!strcmp(argv[2], "inline"))
|
-o can only be used with -d or -a
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!strcmp(argv[1], "dis"))
|
-h --help
|
||||||
{
|
-d --disassembly disassembly
|
||||||
if(argc == 2)
|
-i --inline inline
|
||||||
{
|
-a --assembly
|
||||||
fprintf(stderr, "Error: Missing source file.\n");
|
-p --profile
|
||||||
return -1;
|
-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;
|
|
||||||
}
|
|
||||||
@@ -61,6 +61,38 @@ static int bin_getCurrentWorkingDirectory(Runtime *runtime, Object **argv, unsig
|
|||||||
return returnValues2(error, runtime, rets, "s", path);
|
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)
|
static int bin_typename(Runtime *runtime, Object **argv, unsigned int argc, Object *rets[static MAX_RETS], Error *error)
|
||||||
{
|
{
|
||||||
UNUSED(argc);
|
UNUSED(argc);
|
||||||
@@ -364,5 +396,7 @@ StaticMapSlot bins_basic[] = {
|
|||||||
{ "typename", SM_FUNCT, .as_funct = bin_typename, .argc = 1, },
|
{ "typename", SM_FUNCT, .as_funct = bin_typename, .argc = 1, },
|
||||||
{ "keysof", SM_FUNCT, .as_funct = bin_keysof, .argc = 1, },
|
{ "keysof", SM_FUNCT, .as_funct = bin_keysof, .argc = 1, },
|
||||||
{ "getCurrentWorkingDirectory", SM_FUNCT, .as_funct = bin_getCurrentWorkingDirectory, .argc = 0 },
|
{ "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, {}, {} },
|
{ NULL, SM_END, {}, {} },
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ static int bin_accept(Runtime *runtime, Object **argv, unsigned int argc, Object
|
|||||||
int fd = pargs[0].as_int;
|
int fd = pargs[0].as_int;
|
||||||
|
|
||||||
struct sockaddr_in new_addr;
|
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);
|
int new_fd = accept(fd, (struct sockaddr*) &new_addr, &new_addr_size);
|
||||||
if (new_fd < 0)
|
if (new_fd < 0)
|
||||||
return returnValues2(error, runtime, rets, "ns", strerror(errno));
|
return returnValues2(error, runtime, rets, "ns", strerror(errno));
|
||||||
|
|||||||
@@ -124,13 +124,6 @@ fun stringFromMap(map: Map, can_use_method=true) {
|
|||||||
return s;
|
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 {
|
fun GenericIterator(T) return {
|
||||||
set : T,
|
set : T,
|
||||||
keys : List,
|
keys : List,
|
||||||
@@ -191,6 +184,13 @@ return {
|
|||||||
error(cat("Don't know how to convert ", typename(value), " to a string"));
|
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)
|
fun isCallable(x)
|
||||||
return istypeof(Callable, x);
|
return istypeof(Callable, x);
|
||||||
|
|
||||||
|
|||||||
@@ -92,10 +92,13 @@ static int bin_cat(Runtime *runtime, Object **argv, unsigned int argc, Object *r
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
total_count += Object_Count(argv[i], error);
|
size_t length;
|
||||||
|
Object_GetString(argv[i], &length);
|
||||||
|
total_count += length;
|
||||||
|
|
||||||
if(error->occurred)
|
// The Object_Count of a string doesn't match the
|
||||||
return -1;
|
// byte count, but the second argument of Object_GetString
|
||||||
|
// does.
|
||||||
}
|
}
|
||||||
|
|
||||||
char starting[128];
|
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)
|
for(unsigned int i = 0, written = 0; i < argc; i += 1)
|
||||||
{
|
{
|
||||||
size_t length;
|
size_t length;
|
||||||
@@ -125,7 +126,7 @@ static int bin_cat(Runtime *runtime, Object **argv, unsigned int argc, Object *r
|
|||||||
|
|
||||||
buffer[total_count] = '\0';
|
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)
|
if(starting != buffer)
|
||||||
free(buffer);
|
free(buffer);
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ static const InstrInfo instr_table[] = {
|
|||||||
INSTR(PUSHTRU)
|
INSTR(PUSHTRU)
|
||||||
INSTR(PUSHFLS)
|
INSTR(PUSHFLS)
|
||||||
INSTR(PUSHNNE)
|
INSTR(PUSHNNE)
|
||||||
INSTR(PUSHFUN, OPTP_IDX, OPTP_INT)
|
INSTR(PUSHFUN, OPTP_IDX, OPTP_INT, OPTP_STRING)
|
||||||
INSTR(PUSHLST, OPTP_INT)
|
INSTR(PUSHLST, OPTP_INT)
|
||||||
INSTR(PUSHMAP, OPTP_INT)
|
INSTR(PUSHMAP, OPTP_INT)
|
||||||
INSTR(PUSHTYP)
|
INSTR(PUSHTYP)
|
||||||
|
|||||||
@@ -255,18 +255,19 @@ static void emitInstrForArgumentNode(CodegenContext *ctx, ArgumentNode *arg, int
|
|||||||
emitInstr_POP1(ctx, arg->base.offset, arg->base.length);
|
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_func = Label_New(ctx);
|
||||||
Label *label_jump = Label_New(ctx);
|
Label *label_jump = Label_New(ctx);
|
||||||
|
|
||||||
// Push function.
|
// Push function.
|
||||||
{
|
{
|
||||||
Operand ops[2] = {
|
Operand ops[3] = {
|
||||||
{ .type = OPTP_PROMISE, .as_promise = Label_ToPromise(label_func) },
|
{ .type = OPTP_PROMISE, .as_promise = Label_ToPromise(label_func) },
|
||||||
{ .type = OPTP_INT, .as_int = func->argc },
|
{ .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
|
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.
|
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)
|
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_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
|
emitInstr_POP1(ctx, func->base.offset, func->base.length); // Pop function object
|
||||||
}
|
}
|
||||||
@@ -574,7 +575,7 @@ static void emitInstrForExprNode(CodegenContext *ctx, ExprNode *expr,
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
case EXPR_FUNC:
|
case EXPR_FUNC:
|
||||||
emitInstrForFuncExprNode(ctx, (FuncExprNode*) expr);
|
emitInstrForFuncExprNode(ctx, (FuncExprNode*) expr, "???");
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case EXPR_SELECT:
|
case EXPR_SELECT:
|
||||||
|
|||||||
@@ -489,12 +489,12 @@ static inline TokenKind next(Context *ctx, const char *file, int line)
|
|||||||
Token *prev = ctx->token;
|
Token *prev = ctx->token;
|
||||||
|
|
||||||
ctx->token = ctx->token->next;
|
ctx->token = ctx->token->next;
|
||||||
|
/*
|
||||||
fprintf(stderr, "NEXT [%.*s] -> [%.*s] from %s:%d\n",
|
fprintf(stderr, "NEXT [%.*s] -> [%.*s] from %s:%d\n",
|
||||||
prev->length, ctx->src + prev->offset,
|
prev->length, ctx->src + prev->offset,
|
||||||
ctx->token->length, ctx->src + ctx->token->offset,
|
ctx->token->length, ctx->src + ctx->token->offset,
|
||||||
file, line);
|
file, line);
|
||||||
|
*/
|
||||||
return current(ctx);
|
return current(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+86
-19
@@ -5,6 +5,7 @@
|
|||||||
#include "compiler/compile.h"
|
#include "compiler/compile.h"
|
||||||
#include "assembler/assemble.h"
|
#include "assembler/assemble.h"
|
||||||
#include "builtins/basic.h"
|
#include "builtins/basic.h"
|
||||||
|
#include "runtime/timing.h"
|
||||||
#include "noja.h"
|
#include "noja.h"
|
||||||
|
|
||||||
static void print_error(const char *type, Error *error)
|
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");
|
fprintf(stderr, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static _Bool interpret(Executable *exe)
|
#include <signal.h>
|
||||||
{
|
|
||||||
Runtime *runt = Runtime_New(-1, 1024*1024, NULL, NULL);
|
|
||||||
|
|
||||||
|
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)
|
if(runt == NULL)
|
||||||
{
|
{
|
||||||
Error error;
|
Error error;
|
||||||
@@ -46,6 +60,8 @@ static _Bool interpret(Executable *exe)
|
|||||||
Error_Free(&error);
|
Error_Free(&error);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
signal(SIGINT, signalHandler);
|
||||||
|
signal(SIGTERM, signalHandler);
|
||||||
|
|
||||||
// We use a [RuntimeError] instead of a simple [Error]
|
// We use a [RuntimeError] instead of a simple [Error]
|
||||||
// because the [RuntimeError] makes a snapshot of the
|
// because the [RuntimeError] makes a snapshot of the
|
||||||
@@ -158,6 +174,37 @@ static _Bool interpret(Executable *exe)
|
|||||||
RuntimeError_Free(&error);
|
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);
|
Runtime_Free(runt);
|
||||||
return retc > -1;
|
return retc > -1;
|
||||||
}
|
}
|
||||||
@@ -193,7 +240,7 @@ static _Bool disassemble(Source *src)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static _Bool interpret_file(const char *file)
|
static _Bool interpret_file(const char *file, bool time, size_t heap)
|
||||||
{
|
{
|
||||||
Error error;
|
Error error;
|
||||||
Error_Init(&error);
|
Error_Init(&error);
|
||||||
@@ -213,14 +260,14 @@ static _Bool interpret_file(const char *file)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
_Bool r = interpret(exe);
|
_Bool r = interpret(exe, time, heap);
|
||||||
|
|
||||||
Executable_Free(exe);
|
Executable_Free(exe);
|
||||||
Source_Free(src);
|
Source_Free(src);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
static _Bool interpret_code(const char *code)
|
static _Bool interpret_code(const char *code, bool time, size_t heap)
|
||||||
{
|
{
|
||||||
Error error;
|
Error error;
|
||||||
Error_Init(&error);
|
Error_Init(&error);
|
||||||
@@ -241,14 +288,14 @@ static _Bool interpret_code(const char *code)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
_Bool r = interpret(exe);
|
_Bool r = interpret(exe, time, heap);
|
||||||
|
|
||||||
Executable_Free(exe);
|
Executable_Free(exe);
|
||||||
Source_Free(src);
|
Source_Free(src);
|
||||||
return r;
|
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 error;
|
||||||
Error_Init(&error);
|
Error_Init(&error);
|
||||||
@@ -271,7 +318,7 @@ static _Bool interpret_asm_file(const char *file)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
_Bool r = interpret(exe);
|
_Bool r = interpret(exe, time, heap);
|
||||||
|
|
||||||
Executable_Free(exe);
|
Executable_Free(exe);
|
||||||
Source_Free(src);
|
Source_Free(src);
|
||||||
@@ -279,7 +326,7 @@ static _Bool interpret_asm_file(const char *file)
|
|||||||
return r;
|
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 error;
|
||||||
Error_Init(&error);
|
Error_Init(&error);
|
||||||
@@ -302,7 +349,7 @@ static _Bool interpret_asm_code(const char *code)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
_Bool r = interpret(exe);
|
_Bool r = interpret(exe, time, heap);
|
||||||
|
|
||||||
Executable_Free(exe);
|
Executable_Free(exe);
|
||||||
Source_Free(src);
|
Source_Free(src);
|
||||||
@@ -352,14 +399,14 @@ static _Bool disassemble_code(const char *code)
|
|||||||
return r;
|
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)
|
_Bool NOJA_dumpFileBytecode(const char *file)
|
||||||
@@ -372,12 +419,32 @@ _Bool NOJA_dumpStringBytecode(const char *str)
|
|||||||
return disassemble_code(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
@@ -1,9 +1,13 @@
|
|||||||
#ifndef NOJA_H
|
#ifndef NOJA_H
|
||||||
#define NOJA_H
|
#define NOJA_H
|
||||||
_Bool NOJA_runFile (const char *file);
|
_Bool NOJA_runFile (const char *file, size_t heap);
|
||||||
_Bool NOJA_runString(const char *str);
|
_Bool NOJA_runString(const char *str, size_t heap);
|
||||||
_Bool NOJA_runAssemblyFile(const char *file);
|
_Bool NOJA_runAssemblyFile(const char *file, size_t heap);
|
||||||
_Bool NOJA_runAssemblyString(const char *str);
|
_Bool NOJA_runAssemblyString(const char *str, size_t heap);
|
||||||
_Bool NOJA_dumpFileBytecode(const char *file);
|
_Bool NOJA_dumpFileBytecode(const char *file);
|
||||||
_Bool NOJA_dumpStringBytecode(const char *str);
|
_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 */
|
#endif /* NOJA_H */
|
||||||
+18
-13
@@ -355,17 +355,7 @@ _Bool Heap_StartCollection(Heap *heap, Error *error)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
_Bool Heap_StopCollection(Heap *heap)
|
static bool callDestructors(Heap *heap)
|
||||||
{
|
|
||||||
assert(heap->collecting == 1);
|
|
||||||
|
|
||||||
if(heap->collection_failed)
|
|
||||||
{
|
|
||||||
free(heap->old_body);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Call destructors here */
|
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
@@ -385,7 +375,7 @@ _Bool Heap_StopCollection(Heap *heap)
|
|||||||
heap->pend[i].destructor(obj, heap->error);
|
heap->pend[i].destructor(obj, heap->error);
|
||||||
|
|
||||||
if(heap->error->occurred)
|
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[i] = heap->pend[heap->pend_used-1];
|
||||||
heap->pend_used -= 1;
|
heap->pend_used -= 1;
|
||||||
@@ -403,20 +393,35 @@ _Bool Heap_StopCollection(Heap *heap)
|
|||||||
heap->pend_size /= 2;
|
heap->pend_size /= 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void freeOverflowAllocations(Heap *heap)
|
||||||
|
{
|
||||||
while(heap->old_oflow)
|
while(heap->old_oflow)
|
||||||
{
|
{
|
||||||
OflowAlloc *prev = heap->old_oflow->prev;
|
OflowAlloc *prev = heap->old_oflow->prev;
|
||||||
free(heap->old_oflow);
|
free(heap->old_oflow);
|
||||||
heap->old_oflow = prev;
|
heap->old_oflow = prev;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_Bool Heap_StopCollection(Heap *heap)
|
||||||
|
{
|
||||||
|
assert(heap->collecting == 1);
|
||||||
|
|
||||||
|
if(!callDestructors(heap))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
freeOverflowAllocations(heap);
|
||||||
|
|
||||||
free(heap->old_body);
|
free(heap->old_body);
|
||||||
|
|
||||||
heap->collecting = 0;
|
heap->collecting = 0;
|
||||||
heap->objcount = heap->movedcount;
|
heap->objcount = heap->movedcount;
|
||||||
return 1;
|
|
||||||
|
return !heap->collection_failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Heap_CollectExtension(void **referer, unsigned int size, void *userp)
|
void Heap_CollectExtension(void **referer, unsigned int size, void *userp)
|
||||||
|
|||||||
@@ -28,18 +28,22 @@
|
|||||||
** +--------------------------------------------------------------------------+
|
** +--------------------------------------------------------------------------+
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "../utils/defs.h"
|
#include "../utils/defs.h"
|
||||||
#include "../objects/objects.h"
|
#include "../objects/objects.h"
|
||||||
|
#include "timing.h"
|
||||||
#include "runtime.h"
|
#include "runtime.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Object base;
|
Object base;
|
||||||
|
const char *name;
|
||||||
Runtime *runtime;
|
Runtime *runtime;
|
||||||
Executable *exe;
|
Executable *exe;
|
||||||
int index, argc;
|
int index, argc;
|
||||||
Object *closure;
|
Object *closure;
|
||||||
|
TimingID timing_id;
|
||||||
} FunctionObject;
|
} FunctionObject;
|
||||||
|
|
||||||
static _Bool free_(Object *self, Error *error)
|
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.
|
// The right amount of arguments was provided.
|
||||||
argv2 = argv;
|
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);
|
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.
|
// NOTE: Every object reference is invalidated from here.
|
||||||
|
|
||||||
if(argv2 != argv)
|
if(argv2 != argv)
|
||||||
@@ -157,7 +179,7 @@ static TypeObject t_func = {
|
|||||||
* The newly created object. If an error occurred, NULL is returned
|
* The newly created object. If an error occurred, NULL is returned
|
||||||
* and information about the error is stored in the [error] argument.
|
* 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(runtime != NULL);
|
||||||
assert(exe != NULL);
|
assert(exe != NULL);
|
||||||
@@ -180,10 +202,19 @@ Object *Object_FromNojaFunction(Runtime *runtime, Executable *exe, int index, in
|
|||||||
}
|
}
|
||||||
|
|
||||||
func->runtime = runtime;
|
func->runtime = runtime;
|
||||||
|
func->name = name; // Should this be copied?
|
||||||
func->exe = exe_copy;
|
func->exe = exe_copy;
|
||||||
func->index = index;
|
func->index = index;
|
||||||
func->argc = argc;
|
func->argc = argc;
|
||||||
func->closure = closure;
|
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;
|
return (Object*) func;
|
||||||
}
|
}
|
||||||
+63
-43
@@ -31,6 +31,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include "runtime.h"
|
#include "runtime.h"
|
||||||
#include "../utils/path.h"
|
#include "../utils/path.h"
|
||||||
#include "../utils/defs.h"
|
#include "../utils/defs.h"
|
||||||
@@ -49,16 +50,25 @@ struct xFrame {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct xRuntime {
|
struct xRuntime {
|
||||||
void *callback_userp;
|
bool interrupt;
|
||||||
_Bool (*callback_addr)(Runtime*, void*);
|
RuntimeCallback callback;
|
||||||
_Bool free_heap;
|
_Bool free_heap;
|
||||||
Object *builtins;
|
Object *builtins;
|
||||||
int depth;
|
int depth;
|
||||||
Frame *frame;
|
Frame *frame;
|
||||||
Stack *stack;
|
Stack *stack;
|
||||||
Heap *heap;
|
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)
|
// Returns the length written in buff (not considering the zero byte)
|
||||||
size_t Runtime_GetCurrentScriptFolder(Runtime *runtime, char *buff, size_t buffsize)
|
size_t Runtime_GetCurrentScriptFolder(Runtime *runtime, char *buff, size_t buffsize)
|
||||||
@@ -153,50 +163,69 @@ Executable *Runtime_GetCurrentExecutable(Runtime *runtime)
|
|||||||
return runtime->frame->exe;
|
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)
|
return runtime->timing;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime->free_heap = free_heap;
|
void Runtime_Interrupt(Runtime *runtime)
|
||||||
runtime->callback_userp = callback_userp;
|
{
|
||||||
runtime->callback_addr = callback_addr;
|
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->builtins = NULL;
|
||||||
runtime->frame = NULL;
|
runtime->frame = NULL;
|
||||||
runtime->depth = 0;
|
runtime->depth = 0;
|
||||||
}
|
|
||||||
|
|
||||||
return runtime;
|
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)
|
if(heap_size < 0)
|
||||||
heap_size = 65536;
|
heap_size = 65536;
|
||||||
|
|
||||||
Heap *heap = Heap_New(heap_size);
|
Heap *heap = Heap_New(heap_size);
|
||||||
|
if(heap == NULL) return NULL;
|
||||||
|
|
||||||
if(heap == NULL)
|
return Runtime_New2(heap, 1, config);
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return Runtime_New2(stack_size, heap, 1, callback_userp, callback_addr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Runtime_Free(Runtime *runtime)
|
void Runtime_Free(Runtime *runtime)
|
||||||
{
|
{
|
||||||
|
if (runtime->timing != NULL)
|
||||||
|
TimingTable_free(runtime->timing);
|
||||||
if(runtime->free_heap)
|
if(runtime->free_heap)
|
||||||
Heap_Free(runtime->heap);
|
Heap_Free(runtime->heap);
|
||||||
Stack_Free(runtime->stack);
|
Stack_Free(runtime->stack);
|
||||||
@@ -839,6 +868,7 @@ static _Bool step(Runtime *runtime, Error *error)
|
|||||||
{
|
{
|
||||||
ASSERT(opc == 1);
|
ASSERT(opc == 1);
|
||||||
ASSERT(ops[0].type == OPTP_STRING);
|
ASSERT(ops[0].type == OPTP_STRING);
|
||||||
|
const char *name = ops[0].as_string;
|
||||||
|
|
||||||
if(runtime->frame->used == 0)
|
if(runtime->frame->used == 0)
|
||||||
{
|
{
|
||||||
@@ -849,7 +879,7 @@ static _Bool step(Runtime *runtime, Error *error)
|
|||||||
Object *val = Stack_Top(runtime->stack, 0);
|
Object *val = Stack_Top(runtime->stack, 0);
|
||||||
ASSERT(val != NULL);
|
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)
|
if(key == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1204,16 +1234,17 @@ static _Bool step(Runtime *runtime, Error *error)
|
|||||||
|
|
||||||
case OPCODE_PUSHFUN:
|
case OPCODE_PUSHFUN:
|
||||||
{
|
{
|
||||||
ASSERT(opc == 2);
|
ASSERT(opc == 3);
|
||||||
ASSERT(ops[0].type == OPTP_IDX);
|
ASSERT(ops[0].type == OPTP_IDX);
|
||||||
ASSERT(ops[1].type == OPTP_INT);
|
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);
|
Object *closure = Object_NewClosure(runtime->frame->closure, runtime->frame->locals, Runtime_GetHeap(runtime), error);
|
||||||
|
|
||||||
if(closure == NULL)
|
if(closure == NULL)
|
||||||
return 0;
|
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)
|
if(obj == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1470,14 +1501,13 @@ int run(Runtime *runtime, Error *error,
|
|||||||
|
|
||||||
// Run the code.
|
// Run the code.
|
||||||
|
|
||||||
if(runtime->callback_addr != NULL)
|
|
||||||
{
|
if(runtime->interrupt || (runtime->callback.func != NULL && !runtime->callback.func(runtime, runtime->callback.data)))
|
||||||
if(!runtime->callback_addr(runtime, runtime->callback_userp))
|
|
||||||
Error_Report(error, 0, "Forced abortion");
|
Error_Report(error, 0, "Forced abortion");
|
||||||
else
|
else
|
||||||
while(step(runtime, error))
|
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");
|
Error_Report(error, 0, "Forced abortion");
|
||||||
break;
|
break;
|
||||||
@@ -1489,16 +1519,6 @@ int run(Runtime *runtime, Error *error,
|
|||||||
if(!collect(runtime, error))
|
if(!collect(runtime, error))
|
||||||
break;
|
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 an error occurred, we want to return NULL.
|
||||||
if(error->occurred == 0)
|
if(error->occurred == 0)
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
#define RUNTIME_H
|
#define RUNTIME_H
|
||||||
|
|
||||||
#include <stdio.h> // meh.. just for the definition of FILE.
|
#include <stdio.h> // meh.. just for the definition of FILE.
|
||||||
|
#include "timing.h"
|
||||||
#include "../utils/error.h"
|
#include "../utils/error.h"
|
||||||
#include "../utils/stack.h"
|
#include "../utils/stack.h"
|
||||||
#include "../objects/objects.h"
|
#include "../objects/objects.h"
|
||||||
@@ -40,11 +41,23 @@
|
|||||||
typedef struct xRuntime Runtime;
|
typedef struct xRuntime Runtime;
|
||||||
typedef struct xSnapshot Snapshot;
|
typedef struct xSnapshot Snapshot;
|
||||||
|
|
||||||
Runtime* Runtime_New(int stack_size, int heap_size, void *callback_userp, _Bool (*callback_addr)(Runtime*, void*));
|
typedef struct {
|
||||||
Runtime* Runtime_New2(int stack_size, Heap *heap, _Bool free_heap, void *callback_userp, _Bool (*callback_addr)(Runtime*, void*));
|
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);
|
void Runtime_Free(Runtime *runtime);
|
||||||
_Bool Runtime_Pop(Runtime *runtime, Error *error, unsigned int n);
|
_Bool Runtime_Pop(Runtime *runtime, Error *error, unsigned int n);
|
||||||
_Bool Runtime_Push(Runtime *runtime, Error *error, Object *obj);
|
_Bool Runtime_Push(Runtime *runtime, Error *error, Object *obj);
|
||||||
|
void Runtime_Interrupt(Runtime *runtime);
|
||||||
Heap* Runtime_GetHeap(Runtime *runtime);
|
Heap* Runtime_GetHeap(Runtime *runtime);
|
||||||
Stack* Runtime_GetStack(Runtime *runtime);
|
Stack* Runtime_GetStack(Runtime *runtime);
|
||||||
Object* Runtime_GetBuiltins(Runtime *runtime);
|
Object* Runtime_GetBuiltins(Runtime *runtime);
|
||||||
@@ -53,6 +66,9 @@ int Runtime_GetCurrentIndex(Runtime *runtime);
|
|||||||
Executable *Runtime_GetCurrentExecutable(Runtime *runtime);
|
Executable *Runtime_GetCurrentExecutable(Runtime *runtime);
|
||||||
size_t Runtime_GetCurrentScriptFolder(Runtime *runtime, char *buff, size_t buffsize);
|
size_t Runtime_GetCurrentScriptFolder(Runtime *runtime, char *buff, size_t buffsize);
|
||||||
const char *Runtime_GetCurrentScriptAbsolutePath(Runtime *runtime);
|
const char *Runtime_GetCurrentScriptAbsolutePath(Runtime *runtime);
|
||||||
|
TimingTable *Runtime_GetTimingTable(Runtime *runtime);
|
||||||
|
RuntimeConfig Runtime_GetDefaultConfigs();
|
||||||
|
|
||||||
Snapshot *Snapshot_New(Runtime *runtime);
|
Snapshot *Snapshot_New(Runtime *runtime);
|
||||||
void Snapshot_Free(Snapshot *snapshot);
|
void Snapshot_Free(Snapshot *snapshot);
|
||||||
void Snapshot_Print(Snapshot *snapshot, FILE *fp);
|
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_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);
|
Object *Object_FromNativeFunction(Runtime *runtime, int (*callback)(Runtime*, Object**, unsigned int, Object*[static MAX_RETS], Error*), int argc, Heap *heap, Error *error);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Error base;
|
Error base;
|
||||||
Runtime *runtime;
|
Runtime *runtime;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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
|
||||||
Reference in New Issue
Block a user