mirror of
				https://github.com/janet-lang/janet
				synced 2025-10-31 15:43:01 +00:00 
			
		
		
		
	Remove longjump/setjump from vm loop. Add out of memory
behavior option.
This commit is contained in:
		
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							| @@ -6,7 +6,7 @@ TARGET=interp | ||||
| PREFIX=/usr/local | ||||
|  | ||||
| # C sources | ||||
| HEADERS=vm.h ds.h compile.h parse.h value.h datatypes.h gc.h util.h | ||||
| HEADERS=vm.h ds.h compile.h parse.h value.h datatypes.h gc.h util.h gst.h | ||||
| SOURCES=main.c parse.c value.c vm.c ds.c compile.c gc.c | ||||
| OBJECTS=$(patsubst %.c,%.o,$(SOURCES)) | ||||
|  | ||||
|   | ||||
							
								
								
									
										20
									
								
								compile.h
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								compile.h
									
									
									
									
									
								
							| @@ -2,6 +2,20 @@ | ||||
| #define COMPILE_H_9VXF71HY | ||||
|  | ||||
| #include "datatypes.h" | ||||
| #include <setjmp.h> | ||||
|  | ||||
| typedef struct GstCompiler GstCompiler; | ||||
| typedef struct GstScope GstScope; | ||||
|  | ||||
| /* Compilation state */ | ||||
| struct GstCompiler { | ||||
|     Gst *vm; | ||||
|     const char *error; | ||||
|     jmp_buf onError; | ||||
|     GstScope *tail; | ||||
|     GstArray *env; | ||||
|     GstBuffer *buffer; | ||||
| }; | ||||
|  | ||||
| /* Initialize the Compiler */ | ||||
| void gst_compiler(GstCompiler *c, Gst *vm); | ||||
| @@ -15,10 +29,4 @@ void gst_compiler_add_global_cfunction(GstCompiler *c, const char *name, GstCFun | ||||
| /* Compile a function that evaluates the given form. */ | ||||
| GstFunction *gst_compiler_compile(GstCompiler *c, GstValue form); | ||||
|  | ||||
| /* Macro expansion. Macro expansion happens prior to the compilation process | ||||
|  * and is completely separate. This allows the compilation to not have to worry | ||||
|  * about garbage collection and other issues that would complicate both the | ||||
|  * runtime and the compilation. */ | ||||
| int gst_macro_expand(Gst *vm, GstValue x, GstObject *macros, GstValue *out); | ||||
|  | ||||
| #endif /* end of include guard: COMPILE_H_9VXF71HY */ | ||||
|   | ||||
							
								
								
									
										52
									
								
								datatypes.h
									
									
									
									
									
								
							
							
						
						
									
										52
									
								
								datatypes.h
									
									
									
									
									
								
							| @@ -2,7 +2,6 @@ | ||||
| #define DATATYPES_H_PJJ035NT | ||||
|  | ||||
| #include <stdint.h> | ||||
| #include <setjmp.h> | ||||
|  | ||||
| /* Flag for immutability in an otherwise mutable datastructure */ | ||||
| #define GST_IMMUTABLE 1 | ||||
| @@ -36,19 +35,15 @@ typedef struct GstArray GstArray; | ||||
| typedef struct GstBuffer GstBuffer; | ||||
| typedef struct GstObject GstObject; | ||||
| typedef struct GstThread GstThread; | ||||
| typedef GstValue (*GstCFunction)(Gst * vm); | ||||
| typedef int (*GstCFunction)(Gst * vm); | ||||
|  | ||||
| /* Implementation details */ | ||||
| typedef struct GstParser GstParser; | ||||
| typedef struct GstCompiler GstCompiler; | ||||
| typedef struct GstFuncDef GstFuncDef; | ||||
| typedef struct GstFuncEnv GstFuncEnv; | ||||
|  | ||||
| /* Definitely implementation details */ | ||||
| typedef struct GstStackFrame GstStackFrame; | ||||
| typedef struct GstParseState GstParseState; | ||||
| typedef struct GstBucket GstBucket; | ||||
| typedef struct GstScope GstScope; | ||||
|  | ||||
| /* The general gst value type. Contains a large union and | ||||
|  * the type information of the value */ | ||||
| @@ -155,6 +150,11 @@ struct GstStackFrame { | ||||
|     uint16_t *pc; | ||||
| }; | ||||
|  | ||||
| /* VM return status from c function */ | ||||
| #define GST_RETURN_OK 0 | ||||
| #define GST_RETURN_ERROR 1 | ||||
| #define GST_RETURN_CRASH 2 | ||||
|  | ||||
| /* The VM state */ | ||||
| struct Gst { | ||||
|     /* Garbage collection */ | ||||
| @@ -166,47 +166,9 @@ struct Gst { | ||||
|     GstThread *thread; | ||||
|     /* Return state */ | ||||
|     const char *crash; | ||||
|     jmp_buf jump; | ||||
|     GstValue error; | ||||
|     GstValue ret; /* Returned value from VMStart. Also holds errors. */ | ||||
|     GstValue ret; /* Returned value from gst_start. Also holds errors. */ | ||||
| }; | ||||
|  | ||||
| struct GstParser { | ||||
|     Gst *vm; | ||||
|     const char *error; | ||||
|     GstParseState *data; | ||||
|     GstValue value; | ||||
|     uint32_t count; | ||||
|     uint32_t cap; | ||||
|     uint32_t index; | ||||
|     uint32_t flags; | ||||
|     enum { | ||||
| 		GST_PARSER_PENDING = 0, | ||||
| 		GST_PARSER_FULL, | ||||
| 		GST_PARSER_ERROR | ||||
|     } status; | ||||
| }; | ||||
|  | ||||
| /* Compilation state */ | ||||
| struct GstCompiler { | ||||
|     Gst *vm; | ||||
|     const char *error; | ||||
|     jmp_buf onError; | ||||
|     GstScope *tail; | ||||
|     GstArray *env; | ||||
|     GstBuffer *buffer; | ||||
| }; | ||||
|  | ||||
| /* String utils */ | ||||
| #define gst_string_raw(s) ((uint32_t *)(s) - 2) | ||||
| #define gst_string_length(v) (gst_string_raw(v)[0]) | ||||
| #define gst_string_hash(v) (gst_string_raw(v)[1]) | ||||
|  | ||||
| /* Tuple utils */ | ||||
| #define gst_tuple_raw(s) ((uint32_t *)(s) - 2) | ||||
| #define gst_tuple_length(v) (gst_tuple_raw(v)[0]) | ||||
| #define gst_tuple_hash(v) (gst_tuple_raw(v)[1]) | ||||
|  | ||||
| /* Bytecode */ | ||||
| enum GstOpCode { | ||||
|     GST_OP_ADD = 0, /* Addition */ | ||||
|   | ||||
							
								
								
									
										3
									
								
								gc.c
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								gc.c
									
									
									
									
									
								
							| @@ -184,7 +184,7 @@ void gst_sweep(Gst *vm) { | ||||
| static void *gst_alloc_prepare(Gst *vm, char *rawBlock, uint32_t size) { | ||||
|     GCMemoryHeader *mdata; | ||||
|     if (rawBlock == NULL) { | ||||
|         gst_crash(vm, "out of memory"); | ||||
|         GST_OUT_OF_MEMORY; | ||||
|     } | ||||
|     vm->nextCollection += size; | ||||
|     mdata = (GCMemoryHeader *)rawBlock; | ||||
| @@ -216,7 +216,6 @@ void gst_collect(Gst *vm) { | ||||
|         gst_mark(vm, &thread); | ||||
|     } | ||||
|     gst_mark(vm, &vm->ret); | ||||
|     gst_mark(vm, &vm->error); | ||||
|     gst_sweep(vm); | ||||
|     vm->nextCollection = 0; | ||||
| } | ||||
|   | ||||
							
								
								
									
										12
									
								
								gst.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								gst.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| #ifndef gst_h_INCLUDED | ||||
| #define gst_h_INCLUDED | ||||
|  | ||||
| #include "util.h" | ||||
| #include "datatypes.h" | ||||
| #include "vm.h" | ||||
| #include "parse.h" | ||||
| #include "compile.h" | ||||
| #include "value.h" | ||||
|  | ||||
| #endif // gst_h_INCLUDED | ||||
|  | ||||
							
								
								
									
										14
									
								
								main.c
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								main.c
									
									
									
									
									
								
							| @@ -1,10 +1,6 @@ | ||||
| #include <stdlib.h> | ||||
| #include <stdio.h> | ||||
| #include "datatypes.h" | ||||
| #include "vm.h" | ||||
| #include "parse.h" | ||||
| #include "compile.h" | ||||
| #include "value.h" | ||||
| #include "gst.h" | ||||
|  | ||||
| /* Simple printer for gst strings */ | ||||
| void string_put(FILE *out, uint8_t * string) { | ||||
| @@ -15,16 +11,14 @@ void string_put(FILE *out, uint8_t * string) { | ||||
| } | ||||
|  | ||||
| /* Test c function */ | ||||
| GstValue print(Gst *vm) { | ||||
| int print(Gst *vm) { | ||||
|     uint32_t j, count; | ||||
|     GstValue nil; | ||||
|     count = gst_count_args(vm); | ||||
|     for (j = 0; j < count; ++j) { | ||||
|         string_put(stdout, gst_to_string(vm, gst_arg(vm, j))); | ||||
|         fputc('\n', stdout); | ||||
|     } | ||||
|     nil.type = GST_NIL; | ||||
|     return nil; | ||||
|     return GST_RETURN_OK; | ||||
| } | ||||
|  | ||||
| /* A simple repl for debugging */ | ||||
| @@ -98,7 +92,7 @@ void debug_repl(FILE *in, FILE *out) { | ||||
|                     fprintf(out, "VM crash: %s\n", vm.crash); | ||||
|                 } else { | ||||
|                     fprintf(out, "VM error: "); | ||||
|                     string_put(out, gst_to_string(&vm, vm.error)); | ||||
|                     string_put(out, gst_to_string(&vm, vm.ret)); | ||||
|                     printf("\n"); | ||||
|                 } | ||||
|             } | ||||
|   | ||||
							
								
								
									
										20
									
								
								parse.h
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								parse.h
									
									
									
									
									
								
							| @@ -3,6 +3,26 @@ | ||||
|  | ||||
| #include "datatypes.h" | ||||
|  | ||||
| typedef struct GstParser GstParser; | ||||
| typedef struct GstParseState GstParseState; | ||||
|  | ||||
| /* Holds the parsing state */ | ||||
| struct GstParser { | ||||
|     Gst *vm; | ||||
|     const char *error; | ||||
|     GstParseState *data; | ||||
|     GstValue value; | ||||
|     uint32_t count; | ||||
|     uint32_t cap; | ||||
|     uint32_t index; | ||||
|     uint32_t flags; | ||||
|     enum { | ||||
| 		GST_PARSER_PENDING = 0, | ||||
| 		GST_PARSER_FULL, | ||||
| 		GST_PARSER_ERROR | ||||
|     } status; | ||||
| }; | ||||
|  | ||||
| /* Some parser flags */ | ||||
| #define GST_PARSER_FLAG_INCOMMENT 1 | ||||
| #define GST_PARSER_FLAG_EXPECTING_COMMENT 2 | ||||
|   | ||||
							
								
								
									
										25
									
								
								util.h
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								util.h
									
									
									
									
									
								
							| @@ -1,6 +1,16 @@ | ||||
| #ifndef util_h_INCLUDED | ||||
| #define util_h_INCLUDED | ||||
|  | ||||
| /* String utils */ | ||||
| #define gst_string_raw(s) ((uint32_t *)(s) - 2) | ||||
| #define gst_string_length(v) (gst_string_raw(v)[0]) | ||||
| #define gst_string_hash(v) (gst_string_raw(v)[1]) | ||||
|  | ||||
| /* Tuple utils */ | ||||
| #define gst_tuple_raw(s) ((uint32_t *)(s) - 2) | ||||
| #define gst_tuple_length(v) (gst_tuple_raw(v)[0]) | ||||
| #define gst_tuple_hash(v) (gst_tuple_raw(v)[1]) | ||||
|  | ||||
| /* Memcpy for moving memory */ | ||||
| #ifndef gst_memcpy | ||||
| #include <string.h> | ||||
| @@ -36,5 +46,20 @@ | ||||
| #define NULL ((void *)0) | ||||
| #endif | ||||
|  | ||||
| /* C function helpers */ | ||||
|  | ||||
| /* Return in a c function */ | ||||
| #define gst_c_return(vm, x) (do { (vm)->ret = (x); return GST_RETURN_OK; } while (0)) | ||||
|  | ||||
| /* Throw error from a c function */ | ||||
| #define gst_c_throw(vm, e) (do { (vm)->ret = (e); return GST_RETURN_ERROR; } while (0)) | ||||
|  | ||||
| /* What to do when out of memory */ | ||||
| #ifndef GST_OUT_OF_MEMORY | ||||
| #include <stdlib.h> | ||||
| #include <stdio.h> | ||||
| #define GST_OUT_OF_MEMORY do { printf("out of memory.\n"); exit(1); } while (0) | ||||
| #endif | ||||
|  | ||||
| #endif // util_h_INCLUDED | ||||
|  | ||||
|   | ||||
							
								
								
									
										57
									
								
								value.c
									
									
									
									
									
								
							
							
						
						
									
										57
									
								
								value.c
									
									
									
									
									
								
							| @@ -388,75 +388,78 @@ static uint8_t to_byte(GstNumber raw) { | ||||
| 	return (uint8_t) raw; | ||||
| } | ||||
|  | ||||
| /* Get a value out af an associated data structure. Can throw VM error. */ | ||||
| GstValue gst_get(Gst *vm, GstValue ds, GstValue key) { | ||||
| /* Get a value out af an associated data structure.  | ||||
|  * Returns possible c error message, and NULL for no error. The | ||||
|  * useful return value is written to out on success */ | ||||
| const char *gst_get(GstValue ds, GstValue key, GstValue *out) { | ||||
|     int32_t index; | ||||
|     GstValue ret; | ||||
| 	switch (ds.type) { | ||||
| 	case GST_ARRAY: | ||||
|        	gst_assert_type(vm, key, GST_NUMBER); | ||||
|         if (key.type != GST_NUMBER) return "expected numeric key"; | ||||
| 		index = to_index(key.data.number, ds.data.array->count); | ||||
| 		if (index == -1) gst_error(vm, "invalid array access"); | ||||
| 		if (index == -1) return "invalid array access"; | ||||
| 		ret = ds.data.array->data[index]; | ||||
| 		break; | ||||
| 	case GST_TUPLE: | ||||
|        	gst_assert_type(vm, key, GST_NUMBER); | ||||
|         if (key.type != GST_NUMBER) return "expected numeric key"; | ||||
| 		index = to_index(key.data.number, gst_tuple_length(ds.data.tuple)); | ||||
| 		if (index < 0) gst_error(vm, "invalid tuple access"); | ||||
| 		if (index < 0) return "invalid tuple access"; | ||||
| 		ret = ds.data.tuple[index]; | ||||
| 		break; | ||||
|     case GST_BYTEBUFFER: | ||||
|         gst_assert_type(vm, key, GST_NUMBER); | ||||
|         if (key.type != GST_NUMBER) return "expected numeric key"; | ||||
|         index = to_index(key.data.number, ds.data.buffer->count); | ||||
| 		if (index == -1) gst_error(vm, "invalid buffer access"); | ||||
| 		if (index == -1) return "invalid buffer access"; | ||||
| 		ret.type = GST_NUMBER; | ||||
| 		ret.data.number = ds.data.buffer->data[index]; | ||||
| 		break; | ||||
|     case GST_STRING: | ||||
|         gst_assert_type(vm, key, GST_NUMBER); | ||||
|         if (key.type != GST_NUMBER) return "expected numeric key"; | ||||
|         index = to_index(key.data.number, gst_string_length(ds.data.string)); | ||||
| 		if (index == -1) gst_error(vm, "invalid string access"); | ||||
| 		if (index == -1) return "invalid string access"; | ||||
| 		ret.type = GST_NUMBER; | ||||
| 		ret.data.number = ds.data.string[index]; | ||||
| 		break; | ||||
|     case GST_OBJECT: | ||||
|         return gst_object_get(ds.data.object, key); | ||||
|        	ret = gst_object_get(ds.data.object, key); | ||||
|        	break; | ||||
|     default: | ||||
|         gst_error(vm, "cannot get"); | ||||
|        return "cannot get"; | ||||
| 	} | ||||
| 	return ret; | ||||
| 	*out = ret; | ||||
| 	return NULL; | ||||
| } | ||||
|  | ||||
| /* Set a value in an associative data structure. Can throw VM error. */ | ||||
| void gst_set(Gst *vm, GstValue ds, GstValue key, GstValue value) { | ||||
| /* Set a value in an associative data structure. Returns possible | ||||
|  * error message, and NULL if no error. */ | ||||
| const char *gst_set(Gst *vm, GstValue ds, GstValue key, GstValue value) { | ||||
|     int32_t index; | ||||
| 	switch (ds.type) { | ||||
| 	case GST_ARRAY: | ||||
|         if (ds.data.array->flags & GST_IMMUTABLE) | ||||
|             goto immutable; | ||||
|        	gst_assert_type(vm, key, GST_NUMBER); | ||||
|             return "cannot set immutable value"; | ||||
|         if (key.type != GST_NUMBER) return "expected numeric key"; | ||||
| 		index = to_index(key.data.number, ds.data.array->count); | ||||
| 		if (index == -1) gst_error(vm, "invalid array access"); | ||||
| 		if (index == -1) return "invalid array access"; | ||||
| 		ds.data.array->data[index] = value; | ||||
| 		break; | ||||
|     case GST_BYTEBUFFER: | ||||
|         if (ds.data.buffer->flags & GST_IMMUTABLE) | ||||
|             goto immutable; | ||||
|         gst_assert_type(vm, key, GST_NUMBER); | ||||
|         gst_assert_type(vm, value, GST_NUMBER); | ||||
|             return "cannot set immutable value"; | ||||
|         if (key.type != GST_NUMBER) return "expected numeric key"; | ||||
|         if (value.type != GST_NUMBER) return "expected numeric value"; | ||||
|         index = to_index(key.data.number, ds.data.buffer->count); | ||||
| 		if (index == -1) gst_error(vm, "invalid buffer access"); | ||||
| 		if (index == -1) return "invalid buffer access"; | ||||
| 		ds.data.buffer->data[index] = to_byte(value.data.number); | ||||
| 		break; | ||||
|     case GST_OBJECT: | ||||
|         if (ds.data.object->flags & GST_IMMUTABLE) | ||||
|             goto immutable; | ||||
|             return "cannot set immutable value"; | ||||
|         gst_object_put(vm, ds.data.object, key, value); | ||||
|         break; | ||||
|     default: | ||||
|         gst_error(vm, "cannot set"); | ||||
|     	return "cannot set"; | ||||
| 	} | ||||
| 	return; | ||||
|     immutable: | ||||
|         gst_error(vm, "cannot set immutable value"); | ||||
| 	return NULL; | ||||
| } | ||||
|   | ||||
							
								
								
									
										4
									
								
								value.h
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								value.h
									
									
									
									
									
								
							| @@ -15,10 +15,10 @@ int gst_compare(GstValue x, GstValue y); | ||||
| int gst_equals(GstValue x, GstValue y); | ||||
|  | ||||
| /* Get a value from an associative gst object. Can throw errors. */ | ||||
| GstValue gst_get(Gst *vm, GstValue ds, GstValue key); | ||||
| const char *gst_get(GstValue ds, GstValue key, GstValue *out); | ||||
|  | ||||
| /* Set a value in an associative gst object. Can throw errors. */ | ||||
| void gst_set(Gst *vm, GstValue ds, GstValue key, GstValue value); | ||||
| const char *gst_set(Gst *vm, GstValue ds, GstValue key, GstValue value); | ||||
|  | ||||
| /* Load a c style string into a gst value (copies data) */ | ||||
| GstValue gst_load_cstring(Gst *vm, const char *string); | ||||
|   | ||||
							
								
								
									
										170
									
								
								vm.c
									
									
									
									
									
								
							
							
						
						
									
										170
									
								
								vm.c
									
									
									
									
									
								
							| @@ -1,23 +1,28 @@ | ||||
|  | ||||
| #include "vm.h" | ||||
| #include "util.h" | ||||
| #include "value.h" | ||||
| #include "ds.h" | ||||
| #include "gc.h" | ||||
|  | ||||
| /* Macros for errors in the vm */ | ||||
|  | ||||
| /* Exit from the VM normally */ | ||||
| #define gst_exit(vm, r) return ((vm)->ret = (r), GST_RETURN_OK) | ||||
|  | ||||
| /* Bail from the VM with an error string. */ | ||||
| #define gst_error(vm, e) return ((vm)->ret = gst_load_cstring((vm), (e)), GST_RETURN_ERROR) | ||||
|  | ||||
| /* Crash. Not catchable, unlike error. */ | ||||
| #define gst_crash(vm, e) return ((vm)->crash = (e), GST_RETURN_CRASH) | ||||
|  | ||||
| /* Error if the condition is false */ | ||||
| #define gst_assert(vm, cond, e) do {if (!(cond)){gst_error((vm), (e));}} while (0) | ||||
|  | ||||
| static const char GST_NO_UPVALUE[] = "no upvalue"; | ||||
| static const char GST_EXPECTED_FUNCTION[] = "expected function"; | ||||
| static const char GST_EXPECTED_NUMBER_ROP[] = "expected right operand to be number"; | ||||
| static const char GST_EXPECTED_NUMBER_LOP[] = "expected left operand to be number"; | ||||
|  | ||||
| /* Get a literal */ | ||||
| static GstValue gst_vm_literal(Gst *vm, GstFunction *fn, uint16_t index) { | ||||
|     if (index > fn->def->literalsLen) { | ||||
|         gst_error(vm, GST_NO_UPVALUE); | ||||
|     } | ||||
|     return fn->def->literals[index]; | ||||
| } | ||||
|  | ||||
| /* Load a function into the VM. The function will be called with | ||||
|  * no arguments when run */ | ||||
| static void gst_load(Gst *vm, GstValue callee) { | ||||
| @@ -61,53 +66,18 @@ int gst_start(Gst *vm, GstValue func) { | ||||
|     GstStackFrame frame; | ||||
|     GstValue temp, v1, v2; | ||||
|     uint16_t *pc; | ||||
|  | ||||
| 	/* Load the callee */ | ||||
| 	gst_load(vm, func); | ||||
|  | ||||
| 	/* Intialize local state */ | ||||
| 	thread = *vm->thread; | ||||
|     stack = thread.data + thread.count; | ||||
|     frame = *((GstStackFrame *)(stack - GST_FRAME_SIZE)); | ||||
|     pc = frame.pc; | ||||
|  | ||||
|     /* Set jmp_buf to jump back to for return. */ | ||||
|     { | ||||
|         int n; | ||||
|         if ((n = setjmp(vm->jump))) { | ||||
|             /* Good return */ | ||||
|             if (n == 1) { | ||||
|                 return 0; | ||||
|             } else if (n == 2) { | ||||
|                 /* Error. */ | ||||
|                 while (!frame.errorJump) { | ||||
|                     /* Check for closure */ | ||||
|                     if (frame.env) { | ||||
|                         frame.env->thread = NULL; | ||||
|                         frame.env->stackOffset = frame.size; | ||||
|                         frame.env->values = gst_alloc(vm, sizeof(GstValue) * frame.size); | ||||
|                         gst_memcpy(frame.env->values,  | ||||
|                             thread.data + thread.count,  | ||||
|                             frame.size * sizeof(GstValue)); | ||||
|                     } | ||||
|                     stack -= frame.prevSize + GST_FRAME_SIZE; | ||||
|                     if (stack <= thread.data) { | ||||
|                         thread.count = 0; | ||||
|                         break; | ||||
|                     } | ||||
|                     frame = *((GstStackFrame *)(stack - GST_FRAME_SIZE)); | ||||
|                 } | ||||
|                 if (thread.count < GST_FRAME_SIZE) | ||||
|                     return n; | ||||
|                 /* Jump to the error location */ | ||||
|                 pc = frame.errorJump; | ||||
|                 /* Set error */ | ||||
|                 stack[frame.errorSlot] = vm->error; | ||||
|             } else { | ||||
|                 /* Crash. just return */ | ||||
|                 return n; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* Main interpreter loop */ | ||||
|     mainloop: | ||||
|     for (;;) { | ||||
|          | ||||
|         switch (*pc) { | ||||
| @@ -290,10 +260,16 @@ int gst_start(Gst *vm, GstValue func) { | ||||
|                 /* Call the function */ | ||||
|                 if (temp.type == GST_CFUNCTION) { | ||||
|                     /* Save current state to vm thread */ | ||||
|                     int status; | ||||
|                     *((GstStackFrame *)(stack - GST_FRAME_SIZE)) = frame; | ||||
|                     *vm->thread = thread; | ||||
|                     v2 = temp.data.cfunction(vm); | ||||
|                     goto ret; | ||||
|                     vm->ret.type = GST_NIL; | ||||
|                     status = temp.data.cfunction(vm); | ||||
| 					v1 = vm->ret; | ||||
| 					if (status == GST_RETURN_OK) | ||||
|                         goto ret; | ||||
|                     else | ||||
|                         goto vm_error; | ||||
|                 } else { | ||||
|                     for (; i < locals; ++i) | ||||
|                         stack[i].type = GST_NIL; | ||||
| @@ -308,7 +284,9 @@ int gst_start(Gst *vm, GstValue func) { | ||||
|  | ||||
|         case GST_OP_CST: /* Load constant value */ | ||||
|             gst_assert(vm, frame.callee.type == GST_FUNCTION, GST_EXPECTED_FUNCTION); | ||||
|             stack[pc[1]] = gst_vm_literal(vm, frame.callee.data.function, pc[2]); | ||||
|             if (pc[2] > frame.callee.data.function->def->literalsLen) | ||||
|                 gst_error(vm, GST_NO_UPVALUE); | ||||
|             stack[pc[1]] = frame.callee.data.function->def->literals[pc[2]]; | ||||
|             pc += 3; | ||||
|             break; | ||||
|  | ||||
| @@ -343,7 +321,9 @@ int gst_start(Gst *vm, GstValue func) { | ||||
|                     frame.env->stackOffset = thread.count; | ||||
|                     frame.env->values = NULL; | ||||
|                 } | ||||
|                 temp = gst_vm_literal(vm, frame.callee.data.function, pc[2]); | ||||
|                 if (pc[2] > frame.callee.data.function->def->literalsLen) | ||||
|                     gst_error(vm, GST_NO_UPVALUE); | ||||
|                 temp = frame.callee.data.function->def->literals[pc[2]]; | ||||
|                 if (temp.type != GST_NIL) | ||||
|                     gst_error(vm, "cannot create closure"); | ||||
|                 fn = gst_alloc(vm, sizeof(GstFunction)); | ||||
| @@ -488,10 +468,16 @@ int gst_start(Gst *vm, GstValue func) { | ||||
|                 /* Call the function */ | ||||
|                 if (temp.type == GST_CFUNCTION) { | ||||
|                     /* Save current state to vm thread */ | ||||
|                     int status; | ||||
|                     *((GstStackFrame *)(stack - GST_FRAME_SIZE)) = frame; | ||||
|                     *vm->thread = thread; | ||||
|                     v2 = temp.data.cfunction(vm); | ||||
|                     goto ret; | ||||
|                     vm->ret.type = GST_NIL; | ||||
|                     status = temp.data.cfunction(vm); | ||||
|                     v1 = vm->ret; | ||||
|                     if (status == GST_RETURN_OK) | ||||
|                         goto ret; | ||||
|                     else | ||||
|                         goto vm_error; | ||||
|                 } else { | ||||
|                     pc = temp.data.function->def->byteCode; | ||||
|                 } | ||||
| @@ -502,29 +488,38 @@ int gst_start(Gst *vm, GstValue func) { | ||||
|             v2.type = GST_NIL; | ||||
|             goto ret; | ||||
|  | ||||
|         case GST_OP_GET: | ||||
| 			temp = gst_get(vm, stack[pc[2]], stack[pc[3]]); | ||||
|             stack[pc[1]] = temp; | ||||
| 			pc += 4; | ||||
|             break; | ||||
|         case GST_OP_GET: /* Associative get */ | ||||
|         	{ | ||||
|             	const char *err; | ||||
|     			err = gst_get(stack[pc[2]], stack[pc[3]], stack + pc[1]); | ||||
|     			if (err != NULL) | ||||
| 					gst_error(vm, err); | ||||
|     			pc += 4; | ||||
|                 break; | ||||
|         	} | ||||
|  | ||||
|         case GST_OP_SET: | ||||
| 			gst_set(vm, stack[pc[1]], stack[pc[2]], stack[pc[3]]); | ||||
| 			pc += 4; | ||||
|             break; | ||||
|         case GST_OP_SET: /* Associative set */ | ||||
|         	{ | ||||
|             	const char *err; | ||||
|     			err = gst_set(vm, stack[pc[1]], stack[pc[2]], stack[pc[3]]); | ||||
|     			if (err != NULL) | ||||
|         			gst_error(vm, err); | ||||
|     			pc += 4; | ||||
|                 break; | ||||
|         	} | ||||
|  | ||||
|     	case GST_OP_ERR: | ||||
| 			vm->error = stack[pc[1]]; | ||||
| 			longjmp(vm->jump, 2); | ||||
|     	case GST_OP_ERR: /* Throw error */ | ||||
| 			vm->ret = stack[pc[1]]; | ||||
| 			goto vm_error; | ||||
|         	break; | ||||
|  | ||||
| 		case GST_OP_TRY: | ||||
| 		case GST_OP_TRY: /* Begin try block */ | ||||
|     		frame.errorSlot = pc[1]; | ||||
|     		frame.errorJump = pc + *(uint32_t *)(pc + 2); | ||||
|     		pc += 4; | ||||
|     		break; | ||||
|  | ||||
|     	case GST_OP_UTY: | ||||
|     	case GST_OP_UTY: /* End try block */ | ||||
|         	frame.errorJump = NULL; | ||||
| 			pc++; | ||||
|         	break; | ||||
| @@ -560,6 +555,36 @@ int gst_start(Gst *vm, GstValue func) { | ||||
|         *vm->thread = thread; | ||||
|         gst_maybe_collect(vm); | ||||
|     } | ||||
|  | ||||
|     /* Handle errors from c functions and vm opcodes */ | ||||
|     vm_error: | ||||
|         while (!frame.errorJump) { | ||||
|             /* Check for closure */ | ||||
|             if (frame.env) { | ||||
|                 frame.env->thread = NULL; | ||||
|                 frame.env->stackOffset = frame.size; | ||||
|                 frame.env->values = gst_alloc(vm, sizeof(GstValue) * frame.size); | ||||
|                 gst_memcpy(frame.env->values,  | ||||
|                     thread.data + thread.count,  | ||||
|                     frame.size * sizeof(GstValue)); | ||||
|             } | ||||
|             stack -= frame.prevSize + GST_FRAME_SIZE; | ||||
|             if (stack <= thread.data) { | ||||
|                 thread.count = 0; | ||||
|                 break; | ||||
|             } | ||||
|             frame = *((GstStackFrame *)(stack - GST_FRAME_SIZE)); | ||||
|         } | ||||
|         /* If we completely unwind the stack we just return */ | ||||
|         if (thread.count < GST_FRAME_SIZE) | ||||
|             return GST_RETURN_ERROR; | ||||
|         /* Jump to the error location */ | ||||
|         pc = frame.errorJump; | ||||
|         /* Set error */ | ||||
|         stack[frame.errorSlot] = vm->ret; | ||||
|         /* Resume vm */ | ||||
|         goto mainloop; | ||||
|  | ||||
| } | ||||
|  | ||||
| /* Get an argument from the stack */ | ||||
| @@ -567,7 +592,11 @@ GstValue gst_arg(Gst *vm, uint16_t index) { | ||||
|     GstValue *stack = vm->thread->data + vm->thread->count; | ||||
|     GstStackFrame *frame = (GstStackFrame *)(stack - GST_FRAME_SIZE); | ||||
|     uint16_t frameSize = frame->size; | ||||
|     gst_assert(vm, frameSize > index, "cannot get arg out of stack bounds"); | ||||
|     if (frameSize <= index) { | ||||
| 		GstValue ret; | ||||
| 		ret.type = GST_NIL; | ||||
| 		return ret; | ||||
|     } | ||||
|     return stack[index]; | ||||
| } | ||||
|  | ||||
| @@ -576,7 +605,7 @@ void gst_set_arg(Gst* vm, uint16_t index, GstValue x) { | ||||
|     GstValue *stack = vm->thread->data + vm->thread->count; | ||||
|     GstStackFrame *frame = (GstStackFrame *)(stack - GST_FRAME_SIZE); | ||||
|     uint16_t frameSize = frame->size; | ||||
|     gst_assert(vm, frameSize > index, "cannot set arg out of stack bounds"); | ||||
|    	if (frameSize <= index) return; | ||||
|     stack[index] = x; | ||||
| } | ||||
|  | ||||
| @@ -590,7 +619,6 @@ uint16_t gst_count_args(Gst *vm) { | ||||
| /* Initialize the VM */ | ||||
| void gst_init(Gst *vm) { | ||||
|     vm->ret.type = GST_NIL; | ||||
|     vm->error.type = GST_NIL; | ||||
|     vm->crash = NULL; | ||||
|     /* Garbage collection */ | ||||
|     vm->blocks = NULL; | ||||
|   | ||||
							
								
								
									
										17
									
								
								vm.h
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								vm.h
									
									
									
									
									
								
							| @@ -4,23 +4,6 @@ | ||||
| #include "datatypes.h" | ||||
| #include "value.h" | ||||
|  | ||||
| /* Exit from the VM normally */ | ||||
| #define gst_exit(vm, r) ((vm)->ret = (r), longjmp((vm)->jump, 1)) | ||||
|  | ||||
| /* Bail from the VM with an error string. */ | ||||
| #define gst_error(vm, e) ((vm)->error = gst_load_cstring((vm), (e)), longjmp((vm)->jump, 2)) | ||||
|  | ||||
| /* Crash. Not catchable, unlike error. */ | ||||
| #define gst_crash(vm, e) ((vm)->crash = (e), longjmp((vm)->jump, 3)) | ||||
|  | ||||
| /* Error if the condition is false */ | ||||
| #define gst_assert(vm, cond, e) do \ | ||||
|     { if (!(cond)) { gst_error((vm), (e)); } } while (0) | ||||
|  | ||||
| /* Type assertion */ | ||||
| #define gst_assert_type(vm, f, t) \ | ||||
|     gst_assert((vm), (f).type == (t), "Expected a different type.") | ||||
|  | ||||
| /* Initialize the VM */ | ||||
| void gst_init(Gst * vm); | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Calvin Rose
					Calvin Rose