├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── README.md ├── faster-than-nodejs.png ├── src ├── cript.c ├── cript.h ├── cvm.c ├── cvm.h ├── error.c ├── error.h ├── hash.c ├── hash.h ├── hash_helper.h ├── hash_internal.h ├── inst.h ├── inst_list.c ├── inst_list.h ├── inst_output.c ├── inst_output.h ├── ir_builder.c ├── ir_builder.h ├── libs.c ├── libs.h ├── list.h ├── parse.c ├── parse.h ├── parse_internal.h ├── string_pool.c ├── string_pool.h ├── value.c ├── value.h ├── young_gen.c └── young_gen.h └── test ├── CMakeLists.txt ├── code_test.c ├── cstring_test.c ├── cutest-1.5 ├── AllTests.c ├── CuTest.c ├── CuTest.h ├── CuTestTest.c ├── README.txt ├── index.html ├── license.txt ├── make-tests.sh └── style.css ├── cvm_test.c ├── hash_test.c ├── ir_builder_test.c ├── parser_test.c ├── script_test ├── assert.cr ├── compare.js ├── quicksort.cr ├── test.cr ├── test_all.cr ├── test_call_lightfunc.cr ├── test_concat.cr ├── test_equal.cr ├── test_import.cr ├── test_imported.cr ├── test_imported2.cr ├── test_oop.cr ├── test_parse_number.cr ├── test_quicksort.cr ├── test_sizeof.cr └── test_typeof.cr ├── test_main.c └── young_gen_test.c /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | build 3 | profile 4 | release 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "test/gtest"] 2 | path = test/gtest 3 | url = https://github.com/google/googletest 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(cript) 3 | 4 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -m32 -g") 5 | if ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU") 6 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") 7 | endif() 8 | 9 | option(PROFILE "enable profiling" OFF) 10 | if (PROFILE) 11 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -pg") 12 | endif() 13 | 14 | set(CMAKE_EXPORT_COMPILE_COMMANDS on) 15 | 16 | set(LIB_SRC_FILES src/cript.h src/cvm.c src/cvm.h src/list.h src/error.c src/error.h src/value.c src/value.h src/hash.c src/hash.h src/string_pool.c src/string_pool.h src/hash_helper.h src/inst.h src/inst_list.c src/inst_list.h src/parse.c src/parse.h src/parse_internal.h src/inst_output.h src/inst_output.c src/young_gen.h src/young_gen.c src/hash_internal.h src/libs.c src/libs.h src/ir_builder.h src/ir_builder.c) 17 | set(SOURCE_FILES src/cript.c) 18 | 19 | add_library(libcript ${LIB_SRC_FILES}) 20 | add_executable(cript ${SOURCE_FILES}) 21 | target_link_libraries(cript libcript) 22 | 23 | add_subdirectory(test) 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cript 2 | 3 | A simple and lightweight script language. 4 | 5 | [中文介绍](https://www.clarkok.com/blog/2016/04/20/Cript%E4%B8%80%E9%97%A8%E8%84%9A%E6%9C%AC%E8%AF%AD%E8%A8%80/) 6 | 7 | ## Language feature 8 | 9 | * supported types 10 | * 30bits signed integer 11 | * 256byte-max-length internal string 12 | * hash-based Object and Array 13 | * value-captured Closure 14 | * lightweight C function 15 | * GC managed userdata 16 | * dynamic types 17 | * full support of recursion 18 | * on-the-fly import 19 | * call script functions from C functions and vise-versa 20 | 21 | ## Virtual machine feature 22 | 23 | * copy-and-swap GC 24 | * register-based VM 25 | * SSA-like IR 26 | * able to register C functions 27 | * zero dependency except libc 28 | 29 | ## Current limitation 30 | 31 | * only support `while` loop and `if` branch statement 32 | * heap size is hardcoded as 1MB 33 | * single thread 34 | * 32bits VM 35 | 36 | ## Compile 37 | 38 | ``` 39 | git clone https://github.com/clarkok/cript.git 40 | cd cript 41 | mkdir build 42 | cd build 43 | cmake ../ 44 | make 45 | ``` 46 | 47 | ## Run script 48 | 49 | ``` 50 | ./path/to/cript ./path/to/script.cr 51 | ``` 52 | 53 | ## Hello world! 54 | 55 | ``` 56 | // save it to hello_world.cr and run it! 57 | global.println('Hello World!'); 58 | ``` 59 | 60 | ## Example scripts 61 | 62 | See `test/script_test`, there is enough examples. 63 | 64 | ## Current status 65 | 66 | Trying to optimize compiler 67 | 68 | ## Faster than NodeJS 69 | 70 | Haha, just kidding. 71 | 72 | ![screenshot](/faster-than-nodejs.png?raw=true "Faster than NodeJS") 73 | -------------------------------------------------------------------------------- /faster-than-nodejs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clarkok/cript/e1e39db19e425f002924bb7c0475ac5420cdbc1e/faster-than-nodejs.png -------------------------------------------------------------------------------- /src/cript.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/4/16. 3 | // 4 | 5 | #include 6 | #include 7 | 8 | #include "cript.h" 9 | #include "cvm.h" 10 | #include "error.h" 11 | 12 | #include "libs.h" 13 | 14 | #include "inst_output.h" 15 | 16 | static void 17 | output_help() 18 | { 19 | printf( 20 | "Cript %s\n" 21 | "Usage:" 22 | " cript [options] source.cr\n" 23 | "\n" 24 | "Options:" 25 | " -c\tprint compiled ir before execute\n" 26 | " -h\tprint this help message and exit\n" 27 | " -v\tprint current version and exit\n", 28 | CRIPT_VERSION 29 | ); 30 | } 31 | 32 | int 33 | main(int argc, const char **argv) 34 | { 35 | const char *source = NULL; 36 | int output_insts = 0; 37 | 38 | --argc; ++argv; 39 | while (argc) { 40 | const char *arg = *argv; 41 | if (arg[0] == '-') { 42 | switch (arg[1]) { 43 | case 'v': 44 | { 45 | printf( 46 | "Cript version %s\n" 47 | "Clarkok Zhang(mail@clarkok.com)\n", 48 | CRIPT_VERSION 49 | ); 50 | return 0; 51 | } 52 | case 'c': 53 | { 54 | output_insts = 1; 55 | break; 56 | } 57 | case 'h': 58 | output_help(); 59 | return 0; 60 | default: 61 | { 62 | printf( 63 | "Unknown argument: %s\n", 64 | arg 65 | ); 66 | output_help(); 67 | return -1; 68 | } 69 | } 70 | } 71 | else { 72 | if (source) { 73 | error("Multiple source file\n Use import instead\n"); 74 | return -1; 75 | } 76 | else { 77 | source = arg; 78 | } 79 | } 80 | --argc; ++argv; 81 | } 82 | 83 | if (!source) { 84 | error("No source file\n"); 85 | return -1; 86 | } 87 | ParseState *parse_state = parse_state_new_from_file(source); 88 | parse(parse_state); 89 | 90 | VMState *vm = cvm_state_new_from_parse_state(parse_state); 91 | 92 | if (output_insts) { 93 | output_vm_state(stdout, vm); 94 | } 95 | 96 | lib_register(vm); 97 | cvm_state_run(vm); 98 | 99 | return 0; 100 | } 101 | -------------------------------------------------------------------------------- /src/cript.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/4/16. 3 | // 4 | 5 | #ifndef CRIPT_CRIPT_H 6 | #define CRIPT_CRIPT_H 7 | 8 | #define CRIPT_VERSION "0.3.0" 9 | 10 | #define USE_COMPUTED_GOTO 0 11 | 12 | #endif //CRIPT_CRIPT_H 13 | -------------------------------------------------------------------------------- /src/cvm.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/4/16. 3 | // 4 | 5 | #include 6 | 7 | #include "cript.h" 8 | #include "cvm.h" 9 | #include "error.h" 10 | #include "young_gen.h" 11 | #include "hash_internal.h" 12 | 13 | #include "inst_output.h" 14 | 15 | #define type_error(vm, expected) \ 16 | error_f("%s", "TypeError: expected " expected) 17 | 18 | #define hash_type_error(vm, expected, actual) \ 19 | error_f("TypeError: expected %s, but met %s\n", \ 20 | hash_type_to_str(expected), \ 21 | hash_type_to_str(actual)) 22 | 23 | #define vm_scene_top(vm) \ 24 | list_get(list_head(&vm->scenes), VMScene, _linked) 25 | 26 | #define vm_frame_top(vm) \ 27 | list_get(list_head(&(vm_scene_top(vm)->frames)), VMFrame, _linked) 28 | 29 | inline void 30 | cvm_set_register(VMState *vm, unsigned int reg, Value value) 31 | { 32 | if (reg) { 33 | VMFrame *frame = vm_frame_top(vm); 34 | frame->regs[reg] = value; 35 | } 36 | } 37 | 38 | inline Value 39 | cvm_get_register(VMState *vm, unsigned int reg_id) 40 | { return vm_frame_top(vm)->regs[reg_id]; } 41 | 42 | #define _cvm_try_alloc(vm, ret, expr) \ 43 | do { \ 44 | ret = expr; \ 45 | if (!ret) { \ 46 | cvm_young_gc(vm); \ 47 | ret = expr; \ 48 | if (!ret) { \ 49 | error_f("Out of memory when trying to exec %s", #expr); \ 50 | } \ 51 | } \ 52 | } while (0) 53 | 54 | static inline Hash * 55 | _cvm_allocate_new_hash(VMState *vm, size_t capacity, int type) 56 | { 57 | Hash *hash; 58 | _cvm_try_alloc(vm, hash, young_gen_new_hash(vm->young_gen, capacity, type)); 59 | return hash; 60 | } 61 | 62 | static inline Hash * 63 | _cvm_allocate_new_object(VMState *vm, size_t capacity) 64 | { return _cvm_allocate_new_hash(vm, capacity, HT_OBJECT); } 65 | 66 | static inline Hash * 67 | _cvm_allocate_new_array(VMState *vm, size_t capacity) 68 | { return _cvm_allocate_new_hash(vm, capacity, HT_ARRAY); } 69 | 70 | static inline Hash * 71 | _cvm_get_hash_in_register(VMState *vm, size_t reg) 72 | { 73 | Value val = cvm_get_register(vm, reg); 74 | if (!value_is_ptr(val) || value_is_null(val)) { 75 | type_error(vm, "Object"); 76 | } 77 | Hash *hash = value_to_ptr(val); 78 | if (hash->type == HT_REDIRECT) { 79 | while(hash->type == HT_REDIRECT) { 80 | hash = (Hash*)(hash->size); 81 | } 82 | cvm_set_register(vm, reg, value_from_ptr(hash)); 83 | } 84 | return hash; 85 | } 86 | 87 | static inline Hash * 88 | _cvm_set_hash(VMState *vm, Hash *hash, uintptr_t key, Value val) 89 | { 90 | hash_set(hash, key, val); 91 | if (hash_need_expand(hash)) { 92 | Hash *new_hash = _cvm_allocate_new_hash(vm, _hash_expand_size(hash), hash->type); 93 | if (hash->type == HT_GC_LEFT) { 94 | hash = (Hash*)hash->size; 95 | } 96 | hash_rehash(new_hash, hash); 97 | hash->type = HT_REDIRECT; 98 | hash->capacity = 0; 99 | hash->size = (size_t)new_hash; 100 | 101 | hash = new_hash; 102 | } 103 | return hash; 104 | } 105 | 106 | static inline void 107 | _cvm_set_hash_in_register(VMState *vm, size_t reg, uintptr_t key, Value val) 108 | { 109 | Hash *obj = _cvm_get_hash_in_register(vm, reg); 110 | hash_set(obj, key, val); 111 | if (hash_need_expand(obj)) { 112 | Hash *new_obj = _cvm_allocate_new_hash(vm, _hash_expand_size(obj), obj->type); 113 | 114 | obj = _cvm_get_hash_in_register(vm, reg); 115 | hash_rehash(new_obj, obj); 116 | 117 | obj->type = HT_REDIRECT; 118 | obj->capacity = 0; 119 | obj->size = (size_t)new_obj; 120 | } 121 | } 122 | 123 | static inline void 124 | cvm_state_push_frame(VMState *vm, VMFunction *function) 125 | { 126 | VMFrame *frame = malloc(sizeof(VMFrame) + (function->register_nr + 1) * sizeof(Value)); 127 | frame->function = function; 128 | frame->pc = 0; 129 | frame->regs[0] = value_from_int(0); 130 | frame->regs[1] = value_from_ptr(vm->global); 131 | for (size_t i = 2; i <= function->register_nr; ++i) { 132 | frame->regs[i] = value_undefined(); 133 | } 134 | 135 | list_prepend(&vm_scene_top(vm)->frames, &frame->_linked); 136 | } 137 | 138 | static inline void 139 | cvm_state_pop_frame(VMState *vm) 140 | { free(list_get(list_unlink(list_head(&vm_scene_top(vm)->frames)), VMFrame, _linked)); } 141 | 142 | static inline void 143 | _cvm_push_scene(VMState *vm) 144 | { 145 | VMScene *scene = malloc(sizeof(VMScene)); 146 | list_init(&scene->frames); 147 | scene->external = _cvm_allocate_new_array(vm, HASH_MIN_CAPACITY); 148 | list_prepend(&vm->scenes, &scene->_linked); 149 | } 150 | 151 | static inline void 152 | _cvm_pop_scene(VMState *vm) 153 | { 154 | VMScene *scene = vm_scene_top(vm); 155 | list_unlink(&scene->_linked); 156 | while (list_size(&scene->frames)) { 157 | free(list_get(list_unlink(list_head(&scene->frames)), VMFrame, _linked)); 158 | } 159 | free(scene); 160 | } 161 | 162 | VMState * 163 | cvm_state_new_from_parse_state(ParseState *state) 164 | { 165 | VMState *vm = malloc(sizeof(VMState)); 166 | vm->string_pool = state->string_pool; state->string_pool = NULL; 167 | vm->young_gen = young_gen_new(); 168 | vm->global = young_gen_new_hash(vm->young_gen, HASH_MIN_CAPACITY, HT_OBJECT); 169 | list_init(&vm->functions); 170 | list_init(&vm->scenes); 171 | 172 | _cvm_push_scene(vm); 173 | 174 | list_move(&vm->functions, &state->functions); 175 | VMFunction *main_function = parse_get_main_function(state); 176 | inst_list_push( 177 | main_function->inst_list, 178 | cvm_inst_new_d_type( 179 | I_HALT, 180 | 0, 0, 0 181 | ) 182 | ); 183 | 184 | list_prepend(&vm->functions, &main_function->_linked); 185 | 186 | cvm_state_push_frame(vm, main_function); 187 | 188 | return vm; 189 | } 190 | 191 | Value 192 | cvm_state_import_from_parse_state(VMState *vm, ParseState *state) 193 | { 194 | vm->string_pool = state->string_pool; state->string_pool = NULL; 195 | 196 | _cvm_push_scene(vm); 197 | while (list_size(&state->functions)) { 198 | list_append(&vm->functions, list_unlink(list_head(&state->functions))); 199 | } 200 | VMFunction *main_function = parse_get_main_function(state); 201 | inst_list_push( 202 | main_function->inst_list, 203 | cvm_inst_new_d_type( 204 | I_HALT, 205 | 0, 0, 0 206 | ) 207 | ); 208 | list_prepend(&vm->functions, &main_function->_linked); 209 | 210 | cvm_state_push_frame(vm, main_function); 211 | Value val = cvm_state_run(vm); 212 | _cvm_pop_scene(vm); 213 | 214 | return val; 215 | } 216 | 217 | VMState * 218 | cvm_state_new(InstList *main_inst_list, StringPool *string_pool) 219 | { 220 | VMState *vm = malloc(sizeof(VMState)); 221 | vm->string_pool = string_pool; 222 | vm->young_gen = young_gen_new(); 223 | vm->global = young_gen_new_hash(vm->young_gen, HASH_MIN_CAPACITY, HT_OBJECT); 224 | list_init(&vm->functions); 225 | list_init(&vm->scenes); 226 | 227 | _cvm_push_scene(vm); 228 | 229 | VMFunction *main_function = malloc(sizeof(VMFunction)); 230 | list_node_init(&main_function->_linked); 231 | main_function->arguments_nr = 0; 232 | main_function->register_nr = 65535; 233 | main_function->capture_list = hash_new(HASH_MIN_CAPACITY); 234 | hash_set_and_update(main_function->capture_list, 0, value_from_int(0)); 235 | 236 | main_function->inst_list = main_inst_list; 237 | if (!main_inst_list) main_function->inst_list = inst_list_new(16); 238 | inst_list_push( 239 | main_function->inst_list, 240 | cvm_inst_new_d_type( 241 | I_HALT, 242 | 0, 0, 0 243 | ) 244 | ); 245 | 246 | list_prepend(&vm->functions, &main_function->_linked); 247 | 248 | cvm_state_push_frame(vm, main_function); 249 | 250 | return vm; 251 | } 252 | 253 | void 254 | cvm_state_destroy(VMState *vm) 255 | { 256 | if (vm->string_pool) { 257 | string_pool_destroy(vm->string_pool); 258 | } 259 | if (vm->young_gen) { 260 | young_gen_destroy(vm->young_gen); 261 | } 262 | while (list_size(&vm->functions)) { 263 | VMFunction *function = list_get(list_unlink(list_head(&vm->functions)), VMFunction, _linked); 264 | if (function->capture_list) { 265 | hash_destroy(function->capture_list); 266 | } 267 | if (function->inst_list) { 268 | inst_list_destroy(function->inst_list); 269 | } 270 | free(function); 271 | } 272 | while (list_size(&vm->scenes)) { 273 | VMScene *scene = vm_scene_top(vm); 274 | list_unlink(&scene->_linked); 275 | while (list_size(&scene->frames)) { 276 | free(list_get(list_unlink(list_head(&scene->frames)), VMFrame, _linked)); 277 | } 278 | free(scene); 279 | } 280 | free(vm); 281 | } 282 | 283 | void 284 | cvm_young_gc(VMState *vm) 285 | { 286 | young_gen_gc_start(vm->young_gen); 287 | 288 | young_gen_gc_mark(vm->young_gen, &vm->global); 289 | 290 | list_for_each(&vm->scenes, scene_node) { 291 | VMScene *scene = list_get(scene_node, VMScene, _linked); 292 | young_gen_gc_mark(vm->young_gen, &scene->external); 293 | list_for_each(&scene->frames, frame_node) { 294 | VMFrame *frame = list_get(frame_node, VMFrame, _linked); 295 | for (size_t i = 0; i <= frame->function->register_nr; ++i) { 296 | if (value_is_ptr(frame->regs[i]) && !value_is_null(frame->regs[i])) { 297 | Hash *hash = value_to_ptr(frame->regs[i]); 298 | while (hash->type == HT_REDIRECT) { 299 | hash = (Hash*)hash->size; 300 | } 301 | young_gen_gc_mark(vm->young_gen, &hash); 302 | frame->regs[i] = value_from_ptr(hash); 303 | } 304 | } 305 | } 306 | } 307 | 308 | young_gen_gc_end(vm->young_gen); 309 | } 310 | 311 | Value 312 | cvm_create_light_function(VMState *vm, light_function func) 313 | { 314 | Hash *ret = _cvm_allocate_new_hash(vm, 0, HT_LIGHTFUNC); 315 | ret->hi_func = func; 316 | return value_from_ptr(ret); 317 | } 318 | 319 | Value 320 | cvm_create_userdata(VMState *vm, void *data, userdata_destructor destructor) 321 | { 322 | Hash *ret; 323 | _cvm_try_alloc(vm, ret, young_gen_new_userdata(vm->young_gen, data, destructor)); 324 | return value_from_ptr(ret); 325 | } 326 | 327 | void 328 | cvm_register_in_global(VMState *vm, Value value, const char *name) 329 | { 330 | CString *key = string_pool_insert_str(&vm->string_pool, name); 331 | _cvm_set_hash_in_register(vm, 1, (uintptr_t) key, value); 332 | } 333 | 334 | static inline void 335 | _cvm_setup_function_frame(VMState *vm, Hash *closure, Hash *args) 336 | { 337 | for (size_t i = 0; i <= closure->hi_closure->arguments_nr; ++i) { 338 | cvm_set_register(vm, i + 1, hash_find(args, i)); 339 | } 340 | 341 | hash_for_each(closure, captured) { 342 | cvm_set_register(vm, captured->key, captured->value); 343 | } 344 | } 345 | 346 | Value 347 | cvm_state_call_function(VMState *vm, Value func_val, Value args_val) 348 | { 349 | if (!value_is_ptr(func_val) || !value_to_ptr(args_val)) { 350 | type_error(vm, "function"); 351 | return value_undefined(); 352 | } 353 | 354 | Hash *func = value_to_ptr(func_val); 355 | 356 | if (func->type == HT_LIGHTFUNC) { 357 | return func->hi_func(vm, args_val); 358 | } 359 | else { 360 | Hash *args = value_to_ptr(args_val); 361 | 362 | _cvm_push_scene(vm); 363 | 364 | cvm_state_push_frame(vm, func->hi_closure); 365 | _cvm_setup_function_frame(vm, func, args); 366 | 367 | Value ret = cvm_state_run(vm); 368 | 369 | _cvm_pop_scene(vm); 370 | 371 | return ret; 372 | } 373 | } 374 | 375 | Hash * 376 | cvm_get_global(VMState *vm) 377 | { return vm->global; } 378 | 379 | Hash * 380 | cvm_create_object(VMState *vm, size_t capacity) 381 | { 382 | Hash *hash = _cvm_allocate_new_hash(vm, capacity, HT_OBJECT); 383 | vm_scene_top(vm)->external = _cvm_set_hash( 384 | vm, 385 | vm_scene_top(vm)->external, 386 | hash_size(vm_scene_top(vm)->external), 387 | value_from_ptr(hash) 388 | ); 389 | return hash; 390 | } 391 | 392 | Hash * 393 | cvm_create_array(VMState *vm, size_t capacity) 394 | { 395 | Hash *hash = _cvm_allocate_new_hash(vm, capacity, HT_ARRAY); 396 | vm_scene_top(vm)->external = _cvm_set_hash( 397 | vm, 398 | vm_scene_top(vm)->external, 399 | hash_size(vm_scene_top(vm)->external), 400 | value_from_ptr(hash) 401 | ); 402 | return hash; 403 | } 404 | 405 | Hash * 406 | cvm_set_hash(VMState *vm, Hash *hash, uintptr_t key, Value val) 407 | { return _cvm_set_hash(vm, hash, key, val); } 408 | 409 | Value 410 | cvm_state_run(VMState *vm) 411 | { 412 | #if USE_COMPUTED_GOTO 413 | static void *LABEL_CONSTANT[] = { 414 | &&i_unknown, 415 | &&i_halt, 416 | &&i_li, 417 | &&i_add, 418 | &&i_sub, 419 | &&i_mul, 420 | &&i_div, 421 | &&i_mod, 422 | &&i_seq, 423 | &&i_slt, 424 | &&i_sle, 425 | &&i_lnot, 426 | &&i_mov, 427 | &&i_bnr, 428 | &&i_j, 429 | &&i_lstr, 430 | &&i_new_obj, 431 | &&i_new_arr, 432 | &&i_set_obj, 433 | &&i_get_obj, 434 | &&i_call, 435 | &&i_ret, 436 | &&i_new_cls, 437 | &&i_undefined, 438 | &&i_null 439 | }; 440 | #endif 441 | 442 | #if USE_COMPUTED_GOTO 443 | #define DISPATCH() \ 444 | frame = vm_frame_top(vm); \ 445 | inst = frame->function->inst_list->insts[frame->pc++]; \ 446 | goto *LABEL_CONSTANT[inst.type] 447 | 448 | VMFrame *frame; 449 | Inst inst; 450 | DISPATCH(); 451 | #else 452 | for (;;) { 453 | VMFrame *frame = vm_frame_top(vm); 454 | Inst inst = frame->function->inst_list->insts[frame->pc++]; 455 | switch (inst.type) { 456 | #endif 457 | 458 | #if USE_COMPUTED_GOTO 459 | i_halt: 460 | #else 461 | case I_HALT: 462 | #endif 463 | return value_undefined(); 464 | 465 | #if USE_COMPUTED_GOTO 466 | i_li: 467 | #else 468 | case I_LI: 469 | #endif 470 | cvm_set_register( 471 | vm, inst.i_rd, 472 | value_from_int(inst.i_imm) 473 | ); 474 | #if USE_COMPUTED_GOTO 475 | DISPATCH(); 476 | i_add: 477 | #else 478 | break; 479 | case I_ADD: 480 | #endif 481 | cvm_set_register( 482 | vm, inst.i_rd, 483 | value_from_int( 484 | value_to_int(cvm_get_register(vm, inst.i_rs)) + 485 | value_to_int(cvm_get_register(vm, inst.i_rt)) 486 | ) 487 | ); 488 | #if USE_COMPUTED_GOTO 489 | DISPATCH(); 490 | i_sub: 491 | #else 492 | break; 493 | case I_SUB: 494 | #endif 495 | cvm_set_register( 496 | vm, inst.i_rd, 497 | value_from_int( 498 | value_to_int(cvm_get_register(vm, inst.i_rs)) - 499 | value_to_int(cvm_get_register(vm, inst.i_rt)) 500 | ) 501 | ); 502 | #if USE_COMPUTED_GOTO 503 | DISPATCH(); 504 | i_mul: 505 | #else 506 | break; 507 | case I_MUL: 508 | #endif 509 | cvm_set_register( 510 | vm, inst.i_rd, 511 | value_from_int( 512 | value_to_int(cvm_get_register(vm, inst.i_rs)) * 513 | value_to_int(cvm_get_register(vm, inst.i_rt)) 514 | ) 515 | ); 516 | #if USE_COMPUTED_GOTO 517 | DISPATCH(); 518 | i_div: 519 | #else 520 | break; 521 | case I_DIV: 522 | #endif 523 | cvm_set_register( 524 | vm, inst.i_rd, 525 | value_from_int( 526 | value_to_int(cvm_get_register(vm, inst.i_rs)) / 527 | value_to_int(cvm_get_register(vm, inst.i_rt)) 528 | ) 529 | ); 530 | #if USE_COMPUTED_GOTO 531 | DISPATCH(); 532 | i_mod: 533 | #else 534 | break; 535 | case I_MOD: 536 | #endif 537 | cvm_set_register( 538 | vm, inst.i_rd, 539 | value_from_int( 540 | value_to_int(cvm_get_register(vm, inst.i_rs)) % 541 | value_to_int(cvm_get_register(vm, inst.i_rt)) 542 | ) 543 | ); 544 | #if USE_COMPUTED_GOTO 545 | DISPATCH(); 546 | i_seq: 547 | #else 548 | break; 549 | case I_SEQ: 550 | #endif 551 | cvm_set_register( 552 | vm, inst.i_rd, 553 | value_from_int( 554 | cvm_get_register(vm, inst.i_rs)._int == 555 | cvm_get_register(vm, inst.i_rt)._int 556 | ) 557 | ); 558 | #if USE_COMPUTED_GOTO 559 | DISPATCH(); 560 | i_slt: 561 | #else 562 | break; 563 | case I_SLT: 564 | #endif 565 | cvm_set_register( 566 | vm, inst.i_rd, 567 | value_from_int( 568 | value_to_int(cvm_get_register(vm, inst.i_rs)) < 569 | value_to_int(cvm_get_register(vm, inst.i_rt)) 570 | ) 571 | ); 572 | #if USE_COMPUTED_GOTO 573 | DISPATCH(); 574 | i_sle: 575 | #else 576 | break; 577 | case I_SLE: 578 | #endif 579 | cvm_set_register( 580 | vm, inst.i_rd, 581 | value_from_int( 582 | value_to_int(cvm_get_register(vm, inst.i_rs)) <= 583 | value_to_int(cvm_get_register(vm, inst.i_rt)) 584 | ) 585 | ); 586 | #if USE_COMPUTED_GOTO 587 | DISPATCH(); 588 | i_lnot: 589 | #else 590 | break; 591 | case I_LNOT: 592 | #endif 593 | cvm_set_register( 594 | vm, inst.i_rd, 595 | value_from_int( 596 | !value_to_int(cvm_get_register(vm, inst.i_rs)) 597 | ) 598 | ); 599 | #if USE_COMPUTED_GOTO 600 | DISPATCH(); 601 | i_mov: 602 | #else 603 | break; 604 | case I_MOV: 605 | #endif 606 | cvm_set_register( 607 | vm, inst.i_rd, 608 | cvm_get_register(vm, inst.i_rs) 609 | ); 610 | #if USE_COMPUTED_GOTO 611 | DISPATCH(); 612 | i_bnr: 613 | #else 614 | break; 615 | case I_BNR: 616 | #endif 617 | { 618 | Value val = cvm_get_register(vm, inst.i_rd); 619 | if ( 620 | value_is_undefined(val) || 621 | value_is_null(val) || 622 | (value_is_int(val) && !value_to_int(val)) 623 | ) { 624 | frame->pc += inst.i_imm; 625 | } 626 | } 627 | #if USE_COMPUTED_GOTO 628 | DISPATCH(); 629 | i_j: 630 | #else 631 | break; 632 | case I_J: 633 | #endif 634 | frame->pc = (size_t)inst.i_imm; 635 | #if USE_COMPUTED_GOTO 636 | DISPATCH(); 637 | i_lstr: 638 | #else 639 | break; 640 | case I_LSTR: 641 | #endif 642 | { 643 | CString *string = (CString*)inst.i_imm; 644 | cvm_set_register( 645 | vm, inst.i_rd, 646 | value_from_string(string) 647 | ); 648 | } 649 | #if USE_COMPUTED_GOTO 650 | DISPATCH(); 651 | i_new_obj: 652 | #else 653 | break; 654 | case I_NEW_OBJ: 655 | #endif 656 | { 657 | Hash *new_obj = _cvm_allocate_new_object(vm, HASH_MIN_CAPACITY); 658 | cvm_set_register( 659 | vm, inst.i_rd, 660 | value_from_ptr(new_obj) 661 | ); 662 | } 663 | #if USE_COMPUTED_GOTO 664 | DISPATCH(); 665 | i_new_arr: 666 | #else 667 | break; 668 | case I_NEW_ARR: 669 | #endif 670 | { 671 | Hash *new_arr = _cvm_allocate_new_array(vm, HASH_MIN_CAPACITY); 672 | cvm_set_register( 673 | vm, inst.i_rd, 674 | value_from_ptr(new_arr) 675 | ); 676 | } 677 | #if USE_COMPUTED_GOTO 678 | DISPATCH(); 679 | i_set_obj: 680 | #else 681 | break; 682 | case I_SET_OBJ: 683 | #endif 684 | { 685 | Hash *obj = _cvm_get_hash_in_register(vm, inst.i_rd); 686 | uintptr_t key; 687 | if (obj->type == HT_OBJECT) { 688 | key = (uintptr_t)value_to_string(cvm_get_register(vm, inst.i_rt)); 689 | } 690 | else { 691 | key = (uintptr_t)value_to_int(cvm_get_register(vm, inst.i_rt)); 692 | } 693 | _cvm_set_hash_in_register(vm, inst.i_rd, key, cvm_get_register(vm, inst.i_rs)); 694 | } 695 | #if USE_COMPUTED_GOTO 696 | DISPATCH(); 697 | i_get_obj: 698 | #else 699 | break; 700 | case I_GET_OBJ: 701 | #endif 702 | { 703 | Hash *obj = _cvm_get_hash_in_register(vm, inst.i_rs); 704 | uintptr_t key; 705 | if (obj->type == HT_OBJECT) { 706 | key = (uintptr_t)value_to_string(cvm_get_register(vm, inst.i_rt)); 707 | } 708 | else { 709 | key = (uintptr_t)value_to_int(cvm_get_register(vm, inst.i_rt)); 710 | } 711 | cvm_set_register( 712 | vm, inst.i_rd, 713 | hash_find(obj, key) 714 | ); 715 | } 716 | #if USE_COMPUTED_GOTO 717 | DISPATCH(); 718 | i_call: 719 | #else 720 | break; 721 | case I_CALL: 722 | #endif 723 | { 724 | Hash *func = _cvm_get_hash_in_register(vm, inst.i_rs); 725 | if (func->type == HT_LIGHTFUNC) { 726 | cvm_set_register( 727 | vm, inst.i_rd, 728 | func->hi_func(vm, cvm_get_register(vm, inst.i_rt)) 729 | ); 730 | } 731 | else if (func->type == HT_CLOSURE) { 732 | Hash *args = _cvm_get_hash_in_register(vm, inst.i_rt); 733 | cvm_state_push_frame(vm, func->hi_closure); 734 | _cvm_setup_function_frame(vm, func, args); 735 | } 736 | else { 737 | hash_type_error(vm, HT_LIGHTFUNC, func->type); 738 | } 739 | } 740 | #if USE_COMPUTED_GOTO 741 | DISPATCH(); 742 | i_ret: 743 | #else 744 | break; 745 | case I_RET: 746 | #endif 747 | { 748 | Value ret_val = cvm_get_register(vm, inst.i_rd); 749 | cvm_state_pop_frame(vm); 750 | if (!list_size(&vm_scene_top(vm)->frames)) { return ret_val; } 751 | Inst original_inst = vm_frame_top(vm)->function->inst_list->insts[vm_frame_top(vm)->pc - 1]; 752 | cvm_set_register(vm, original_inst.i_rd, ret_val); 753 | } 754 | #if USE_COMPUTED_GOTO 755 | DISPATCH(); 756 | i_new_cls: 757 | #else 758 | break; 759 | case I_NEW_CLS: 760 | #endif 761 | { 762 | VMFunction *func = (VMFunction*)inst.i_imm; 763 | Hash *closure = _cvm_allocate_new_hash(vm, hash_size(func->capture_list), HT_CLOSURE); 764 | closure->hi_closure = func; 765 | cvm_set_register( 766 | vm, inst.i_rd, 767 | value_from_ptr(closure) 768 | ); 769 | // should not trigger gc 770 | hash_for_each(func->capture_list, capture) { 771 | hash_set( 772 | closure, 773 | (uintptr_t)value_to_int(capture->value), 774 | cvm_get_register(vm, capture->key) 775 | ); 776 | } 777 | } 778 | #if USE_COMPUTED_GOTO 779 | DISPATCH(); 780 | i_undefined: 781 | #else 782 | break; 783 | case I_UNDEFINED: 784 | #endif 785 | cvm_set_register(vm, inst.i_rd, value_undefined()); 786 | #if USE_COMPUTED_GOTO 787 | DISPATCH(); 788 | i_null: 789 | #else 790 | break; 791 | case I_NULL: 792 | #endif 793 | cvm_set_register(vm, inst.i_rd, value_null()); 794 | #if USE_COMPUTED_GOTO 795 | DISPATCH(); 796 | i_unknown: 797 | #else 798 | break; 799 | default: 800 | #endif 801 | error_f("Unknown VM instrument (offset %d)", frame->pc - 1); 802 | return value_undefined(); 803 | #if USE_COMPUTED_GOTO 804 | #undef DISPATCH 805 | #else 806 | } 807 | } 808 | #endif 809 | } 810 | -------------------------------------------------------------------------------- /src/cvm.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/4/16. 3 | // 4 | 5 | #ifndef CRIPT_CVM_H 6 | #define CRIPT_CVM_H 7 | 8 | #include 9 | 10 | #include "value.h" 11 | #include "string_pool.h" 12 | #include "parse.h" 13 | 14 | #include "inst.h" 15 | #include "inst_list.h" 16 | 17 | #include "list.h" 18 | 19 | typedef struct YoungGen YoungGen; 20 | 21 | typedef struct VMFunction 22 | { 23 | LinkedNode _linked; 24 | 25 | size_t arguments_nr; 26 | size_t register_nr; 27 | Hash *capture_list; 28 | InstList *inst_list; 29 | } VMFunction; 30 | 31 | typedef struct VMFrame 32 | { 33 | LinkedNode _linked; 34 | 35 | VMFunction *function; 36 | size_t pc; 37 | Value regs[0]; 38 | } VMFrame; 39 | 40 | typedef struct VMScene 41 | { 42 | LinkedNode _linked; 43 | LinkedList frames; 44 | Hash *external; 45 | } VMScene; 46 | 47 | typedef struct VMState 48 | { 49 | StringPool *string_pool; 50 | YoungGen *young_gen; 51 | Hash *global; 52 | 53 | LinkedList functions; 54 | LinkedList scenes; 55 | } VMState; 56 | 57 | VMState *cvm_state_new_from_parse_state(ParseState *state); 58 | VMState *cvm_state_new(InstList *main_inst_list, StringPool *string_pool); 59 | void cvm_state_destroy(VMState *vm); 60 | 61 | Value cvm_state_import_from_parse_state(VMState *vm, ParseState *state); 62 | 63 | Value cvm_state_run(VMState *vm); 64 | Value cvm_state_call_function(VMState *vm, Value func, Value args); 65 | 66 | Value cvm_get_register(VMState *vm, unsigned int reg_id); 67 | void cvm_set_register(VMState *vm, unsigned int reg_id, Value value); 68 | 69 | void cvm_young_gc(VMState *vm); 70 | 71 | Value cvm_create_light_function(VMState *vm, light_function func); 72 | Value cvm_create_userdata(VMState *vm, void *data, userdata_destructor destructor); 73 | 74 | Hash *cvm_get_global(VMState *vm); 75 | Hash *cvm_create_object(VMState *vm, size_t capacity); 76 | Hash *cvm_create_array(VMState *vm, size_t capacity); 77 | Hash *cvm_set_hash(VMState *vm, Hash *hash, uintptr_t key, Value val); 78 | 79 | void cvm_register_in_global(VMState *vm, Value value, const char *name); 80 | 81 | static inline Value 82 | cvm_get_cstring_value(VMState *vm, const char *string) 83 | { return value_from_string(string_pool_insert_str(&vm->string_pool, string)); } 84 | 85 | #endif //CRIPT_CVM_H 86 | -------------------------------------------------------------------------------- /src/error.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/4/16. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "error.h" 10 | 11 | #define MESSAGE_MAXIMUM_LENGTH 1024 12 | 13 | void 14 | error_handle(const char *file, int line, const char *msg) 15 | { 16 | fprintf(stderr, "ERROR: %s\n thrown at %s:%d\n\n", msg, file, line); 17 | __builtin_trap(); 18 | } 19 | 20 | void 21 | error_handle_f(const char *file, int line, const char *fmt, ...) 22 | { 23 | char msg[MESSAGE_MAXIMUM_LENGTH]; 24 | va_list args; 25 | 26 | va_start(args, fmt); 27 | vsnprintf(msg, MESSAGE_MAXIMUM_LENGTH, fmt, args); 28 | error_handle(file, line, msg); 29 | } 30 | 31 | void 32 | warn_handle(const char *file, int line, const char *msg) 33 | { 34 | fprintf(stderr, "WARN: %s\n thrown at %s:%d\n\n", msg, file, line); 35 | } 36 | 37 | void 38 | warn_handle_f(const char *file, int line, const char *fmt, ...) 39 | { 40 | char msg[MESSAGE_MAXIMUM_LENGTH]; 41 | va_list args; 42 | 43 | va_start(args, fmt); 44 | vsnprintf(msg, MESSAGE_MAXIMUM_LENGTH, fmt, args); 45 | warn_handle(file, line, msg); 46 | } 47 | 48 | void 49 | info_handle(const char *file, int line, const char *msg) 50 | { 51 | // fprintf(stdout, "INFO: %s\n thrown at %s:%d\n\n", msg, file, line); 52 | } 53 | 54 | void 55 | info_handle_f(const char *file, int line, const char *fmt, ...) 56 | { 57 | char msg[MESSAGE_MAXIMUM_LENGTH]; 58 | va_list args; 59 | 60 | va_start(args, fmt); 61 | vsnprintf(msg, MESSAGE_MAXIMUM_LENGTH, fmt, args); 62 | info_handle(file, line, msg); 63 | } 64 | -------------------------------------------------------------------------------- /src/error.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/4/16. 3 | // 4 | 5 | #ifndef CRIPT_ERROR_H 6 | #define CRIPT_ERROR_H 7 | 8 | #define error_f(fmt, ...) error_handle_f(__FILE__, __LINE__, fmt, __VA_ARGS__) 9 | #define error(msg) error_handle(__FILE__, __LINE__, msg); 10 | 11 | #define warn_f(fmt, ...) warn_handle_f(__FILE__, __LINE__, fmt, __VA_ARGS__) 12 | #define warn(msg) warn_handle(__FILE__, __LINE__, msg); 13 | 14 | #define info_f(fmt, ...) info_handle_f(__FILE__, __LINE__, fmt, __VA_ARGS__) 15 | #define info(msg) info_handle(__FILE__, __LINE__, msg); 16 | 17 | void error_handle_f(const char *file, int line, const char *fmt, ...); 18 | void error_handle(const char *file, int line, const char *msg); 19 | 20 | void warn_handle_f(const char *file, int line, const char *fmt, ...); 21 | void warn_handle(const char *file, int line, const char *msg); 22 | 23 | void info_handle_f(const char *file, int line, const char *fmt, ...); 24 | void info_handle(const char *file, int line, const char *msg); 25 | 26 | #endif //CRIPT_ERROR_H 27 | -------------------------------------------------------------------------------- /src/hash.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/7/16. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "hash.h" 10 | #include "hash_helper.h" 11 | #include "hash_internal.h" 12 | 13 | /** 14 | * Hash 15 | * 16 | * if value in a slot is null, means open hash chain ends here 17 | * if value in a slot is undefined, means this slot is deleted 18 | */ 19 | 20 | Hash * 21 | hash_new(size_t capacity) 22 | { 23 | if (capacity < HASH_MIN_CAPACITY) capacity = HASH_MIN_CAPACITY; 24 | return hash_init(malloc(_hash_total_size(capacity)), capacity, HT_OBJECT); 25 | } 26 | 27 | Hash * 28 | hash_init(void *hash, size_t capacity, int type) 29 | { 30 | if (!hash) return NULL; 31 | assert(1 << __builtin_ctz(capacity) == capacity); 32 | 33 | Hash *ret = hash; 34 | ret->type = type; 35 | ret->capacity = capacity; 36 | ret->size = 0; 37 | memset((char*)ret + sizeof(Hash), 0, _hash_content_size(capacity)); 38 | return ret; 39 | } 40 | 41 | void 42 | hash_destroy(Hash *hash) 43 | { free(hash); } 44 | 45 | Value 46 | hash_find(Hash *hash, uintptr_t key) 47 | { 48 | uintptr_t index = key & (hash->capacity - 1); 49 | 50 | while (hash->content[index].key != key && !value_is_null(hash->content[index].value)) { 51 | index = _hash_next_index(index, hash->capacity); 52 | } 53 | 54 | if (value_is_null(hash->content[index].value)) { 55 | return value_undefined(); 56 | } 57 | else { 58 | return hash->content[index].value; 59 | } 60 | } 61 | 62 | void 63 | hash_set(Hash *hash, uintptr_t key, Value value) 64 | { 65 | uintptr_t index = key % hash->capacity; 66 | uintptr_t slot = hash->capacity; 67 | 68 | if (value_is_null(value)) { 69 | value = value_undefined(); 70 | } 71 | 72 | while (hash->content[index].key != key && !value_is_null(hash->content[index].value)) { 73 | if (slot == hash->capacity && value_is_undefined(hash->content[index].value)) { 74 | slot = index; 75 | } 76 | index = _hash_next_index(index, hash->capacity); 77 | } 78 | 79 | if (hash->content[index].key == key) { slot = index; } 80 | if (slot == hash->capacity) { slot = index; } 81 | 82 | if ( 83 | !value_is_undefined(value) && 84 | (value_is_null(hash->content[slot].value) || 85 | value_is_undefined(hash->content[slot].value)) 86 | ) { 87 | assert(hash->size != hash->capacity); 88 | hash->size++; 89 | } 90 | 91 | if ( 92 | value_is_undefined(value) && 93 | !value_is_undefined(hash->content[slot].value) && 94 | !value_is_null(hash->content[slot].value) 95 | ) { 96 | assert(hash->size); 97 | hash->size--; 98 | } 99 | 100 | hash->content[slot].value = value; 101 | hash->content[slot].key = key; 102 | } 103 | 104 | void 105 | hash_rehash(Hash *dst, Hash *src) 106 | { 107 | dst->_info = src->_info; 108 | HashNode *ptr, *limit; 109 | for ( 110 | ptr = src->content, limit = src->content + src->capacity; 111 | ptr != limit; 112 | ++ptr 113 | ) { 114 | if (!value_is_undefined(ptr->value) && !value_is_null(ptr->value)) 115 | hash_set(dst, ptr->key, ptr->value); 116 | } 117 | } 118 | 119 | Hash * 120 | hash_expand(Hash *hash) 121 | { 122 | Hash *ret = hash_new(_hash_expand_size(hash)); 123 | hash_rehash(ret, hash); 124 | return ret; 125 | } 126 | 127 | Hash * 128 | hash_shrink(Hash *hash) 129 | { 130 | Hash *ret = hash_new(_hash_shrink_size(hash)); 131 | hash_rehash(ret, hash); 132 | return ret; 133 | } 134 | 135 | -------------------------------------------------------------------------------- /src/hash.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/7/16. 3 | // 4 | 5 | #ifndef CRIPT_HASH_H 6 | #define CRIPT_HASH_H 7 | 8 | #include 9 | 10 | #include "value.h" 11 | 12 | typedef struct HashNode 13 | { 14 | uintptr_t key; 15 | Value value; 16 | } HashNode; 17 | 18 | enum HashType 19 | { 20 | HT_OBJECT, 21 | HT_ARRAY, 22 | HT_REDIRECT, 23 | HT_LIGHTFUNC, 24 | HT_CLOSURE, 25 | HT_USERDATA, 26 | 27 | HT_GC_LEFT, 28 | }; 29 | 30 | static inline const char * 31 | hash_type_to_str(int type) 32 | { 33 | static const char *LITERAL[] = { 34 | "object", 35 | "array", 36 | "redirect", 37 | "lightfunc", 38 | "closure", 39 | "userdata", 40 | "gc_left" 41 | }; 42 | 43 | return LITERAL[type]; 44 | } 45 | 46 | typedef struct VMState VMState; 47 | typedef struct VMFunction VMFunction; 48 | 49 | typedef Value (*light_function)(VMState*, Value); 50 | typedef void (*userdata_destructor)(void *); 51 | 52 | typedef struct Hash 53 | { 54 | int type; 55 | union { 56 | light_function _func; 57 | VMFunction *_closure; 58 | struct { 59 | void *_data; 60 | userdata_destructor _destructor; 61 | } _userdata; 62 | } _info; 63 | size_t capacity; 64 | size_t size; 65 | HashNode content[0]; 66 | } Hash; 67 | 68 | #define hi_func _info._func 69 | #define hi_closure _info._closure 70 | #define hi_u_data _info._userdata._data 71 | #define hi_u_dtor _info._userdata._destructor 72 | 73 | #define HASH_MIN_CAPACITY (16) 74 | 75 | #define hash_capacity(hash) ((hash)->capacity) 76 | #define hash_size(hash) ((hash)->size) 77 | #define hash_need_expand(hash) (hash_size(hash) > (hash_capacity(hash) - ((hash_capacity(hash) >> 2)))) 78 | #define hash_need_shrink(hash) (hash_capacity(hash) > HASH_MIN_CAPACITY && \ 79 | hash_size(hash) < (hash_capacity(hash) >> 1)) 80 | 81 | #define hash_set_and_update(hash, key, value) \ 82 | do { \ 83 | hash_set((hash), (uintptr_t)key, value); \ 84 | if (hash_need_expand(hash)) { Hash *old = hash; (hash) = hash_expand(hash); hash_destroy(old); } \ 85 | if (hash_need_shrink(hash)) { Hash *old = hash; (hash) = hash_shrink(hash); hash_destroy(old); } \ 86 | } while (0) 87 | 88 | #define hash_for_each(hash, node) \ 89 | for ( \ 90 | HashNode *node = (hash)->content, \ 91 | *limit = (hash)->content + (hash)->capacity; \ 92 | node != limit; \ 93 | ++node \ 94 | ) \ 95 | if (!value_is_undefined(node->value) && !value_is_null(node->value)) 96 | 97 | Hash *hash_new(size_t capacity); 98 | void hash_destroy(Hash *hash); 99 | 100 | static inline size_t 101 | hash_normalize_capacity(size_t expect_capacity) 102 | { 103 | if (1 << __builtin_ctz(expect_capacity) == expect_capacity) return expect_capacity; 104 | else return (1 << ((sizeof(size_t) * 8) - __builtin_clz(expect_capacity))); 105 | } 106 | 107 | /** 108 | * find a value of a key in this hash table 109 | * 110 | * @param hash the hash table to find in 111 | * @param key the key of the value to find 112 | * @return that value if found, or an Undefined otherwise 113 | */ 114 | Value hash_find(Hash *hash, uintptr_t key); 115 | 116 | /** 117 | * insert, update or remove a value with a key in this hash table 118 | * 119 | * @param hash the hash table to operate 120 | * @param key 121 | * @param value the new value, if the value is Null of Undefined, then the key is removed 122 | */ 123 | void hash_set(Hash *hash, uintptr_t key, Value value); 124 | 125 | /** 126 | * return a newly expanded hash table, while keep the original hash table unchanged 127 | */ 128 | Hash *hash_expand(Hash *hash); 129 | 130 | /** 131 | * return a newly shrunk hash table, while keep the original hash table unchanged 132 | */ 133 | Hash *hash_shrink(Hash *hash); 134 | 135 | void hash_rehash(Hash *dst, Hash *src); 136 | 137 | #endif //CRIPT_HASH_H 138 | -------------------------------------------------------------------------------- /src/hash_helper.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/16/16. 3 | // 4 | 5 | #ifndef CRIPT_HASH_HELPER_H 6 | #define CRIPT_HASH_HELPER_H 7 | 8 | static inline uintptr_t 9 | _next_index(uintptr_t key, size_t capacity) 10 | { return ((key * 2166136261u) + 16777619u) & (capacity - 1); } 11 | 12 | static inline size_t 13 | _expand_size(size_t original_capacity) 14 | { return (original_capacity << 1); } 15 | 16 | #endif //CRIPT_HASH_HELPER_H 17 | -------------------------------------------------------------------------------- /src/hash_internal.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/18/16. 3 | // 4 | 5 | #ifndef CRIPT_HASH_INTERNAL_H 6 | #define CRIPT_HASH_INTERNAL_H 7 | 8 | #include "hash.h" 9 | #include "hash_helper.h" 10 | 11 | Hash *hash_init(void *hash, size_t capacity, int type); 12 | 13 | static inline size_t 14 | _hash_content_size(size_t capacity) 15 | { return capacity * sizeof(HashNode); } 16 | 17 | static inline size_t 18 | _hash_total_size(size_t capacity) 19 | { return sizeof(Hash) + _hash_content_size(capacity); } 20 | 21 | static inline uintptr_t 22 | _hash_next_index(uintptr_t key, size_t capacity) 23 | { return _next_index(key, capacity); } 24 | 25 | static inline size_t 26 | _hash_expand_size(Hash *hash) 27 | { return _expand_size(hash->capacity); } 28 | 29 | static inline size_t 30 | _hash_shrink_size(Hash *hash) 31 | { return hash->capacity >> 1; } 32 | 33 | #endif //CRIPT_HASH_INTERNAL_H 34 | -------------------------------------------------------------------------------- /src/inst.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/16/16. 3 | // 4 | 5 | #ifndef CRIPT_INST_H 6 | #define CRIPT_INST_H 7 | 8 | enum InstType 9 | { 10 | I_UNKNOWN = 0, 11 | I_HALT, 12 | I_LI, 13 | I_ADD, 14 | I_SUB, 15 | I_MUL, 16 | I_DIV, 17 | I_MOD, 18 | I_SEQ, 19 | I_SLT, 20 | I_SLE, 21 | 22 | I_LNOT, 23 | 24 | I_MOV, 25 | 26 | I_BNR, 27 | I_J, 28 | 29 | I_LSTR, 30 | 31 | I_NEW_OBJ, 32 | I_NEW_ARR, 33 | I_SET_OBJ, // $rd is the ref to a obj, $rs is the new value, $rt is the key 34 | I_GET_OBJ, // $rd is the dst register, $rs is the ref to a obj, $rt is the key 35 | 36 | I_CALL, // $rd is return val, $rs is the function, $rt is an array of [this, ...arguments] 37 | I_RET, 38 | I_NEW_CLS, 39 | 40 | I_UNDEFINED, 41 | I_NULL, 42 | 43 | INST_NR 44 | }; 45 | 46 | typedef struct Inst 47 | { 48 | unsigned int type : 16; 49 | 50 | unsigned int i_rd : 16; 51 | union { 52 | struct { 53 | unsigned int rs : 16; 54 | unsigned int rt : 16; 55 | } _d_type; 56 | struct { 57 | signed int imm : 32; 58 | } _i_type; 59 | } _info; 60 | } Inst; 61 | 62 | #define i_rs _info._d_type.rs 63 | #define i_rt _info._d_type.rt 64 | #define i_imm _info._i_type.imm 65 | 66 | static inline Inst 67 | cvm_inst_new_d_type(unsigned int type, unsigned int rd, unsigned int rs, unsigned int rt) 68 | { Inst ret; ret.type = type; ret.i_rd = rd; ret.i_rs = rs; ret.i_rt = rt; return ret; } 69 | 70 | static inline Inst 71 | cvm_inst_new_i_type(unsigned int type, unsigned int rd, int imm) 72 | { Inst ret; ret.type = type; ret.i_rd = rd; ret.i_imm = imm; return ret; } 73 | 74 | #endif //CRIPT_INST_H 75 | -------------------------------------------------------------------------------- /src/inst_list.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/16/16. 3 | // 4 | 5 | #include 6 | 7 | #include "inst_list.h" 8 | 9 | #define CVM_LIST_DEFAULT_CAPACITY 16 10 | 11 | static inline size_t 12 | cvm_list_insts_size(size_t capacity) 13 | { return capacity * sizeof(Inst); } 14 | 15 | static inline size_t 16 | cvm_list_resize_capacity(size_t original) 17 | { return original ? (original + (original >> 1)) : CVM_LIST_DEFAULT_CAPACITY; } 18 | 19 | InstList * 20 | inst_list_new(size_t capacity) 21 | { 22 | size_t insts_size = cvm_list_insts_size(capacity); 23 | 24 | InstList *ret = malloc(sizeof(InstList) + insts_size); 25 | if (!ret) return NULL; 26 | 27 | memset((char *)(ret) + sizeof(InstList), 0, insts_size); 28 | 29 | ret->capacity = capacity; 30 | ret->count = 0; 31 | 32 | return ret; 33 | } 34 | 35 | InstList * 36 | inst_list_resize(InstList *list, size_t capacity) 37 | { 38 | size_t insts_size = cvm_list_insts_size(capacity); 39 | size_t original_size = cvm_list_insts_size(list->capacity); 40 | 41 | list = realloc(list, sizeof(InstList) + insts_size); 42 | if (capacity > list->capacity) { 43 | memset( 44 | (char*)(list) + original_size + sizeof(InstList), 45 | 0, 46 | insts_size - original_size 47 | ); 48 | } 49 | list->capacity = capacity; 50 | 51 | return list; 52 | } 53 | 54 | InstList * 55 | inst_list_append(InstList *list, Inst inst) 56 | { 57 | if (list->count == list->capacity) { 58 | list = inst_list_resize(list, cvm_list_resize_capacity(list->capacity)); 59 | } 60 | 61 | list->insts[list->count++] = inst; 62 | return list; 63 | } 64 | 65 | void 66 | inst_list_destroy(InstList *list) 67 | { free(list); } 68 | 69 | -------------------------------------------------------------------------------- /src/inst_list.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/16/16. 3 | // 4 | 5 | #ifndef CRIPT_INST_LIST_H 6 | #define CRIPT_INST_LIST_H 7 | 8 | #include 9 | 10 | #include "inst.h" 11 | 12 | typedef struct InstList 13 | { 14 | size_t capacity; 15 | size_t count; 16 | Inst insts[0]; 17 | } InstList; 18 | 19 | InstList *inst_list_new(size_t capacity); 20 | InstList *inst_list_resize(InstList *list, size_t capacity); 21 | InstList *inst_list_append(InstList *list, Inst inst); 22 | void inst_list_destroy(InstList *list); 23 | #define inst_list_push(list, inst) list = inst_list_append(list, inst) 24 | 25 | #endif //CRIPT_INST_LIST_H 26 | -------------------------------------------------------------------------------- /src/inst_output.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/17/16. 3 | // 4 | 5 | #include 6 | 7 | #include "inst_output.h" 8 | 9 | const char INST_NAME[] = 10 | "unknown " 11 | "halt " 12 | "li " 13 | "add " 14 | "sub " 15 | "mul " 16 | "div " 17 | "mod " 18 | "seq " 19 | "slt " 20 | "sle " 21 | 22 | "lnot " 23 | "mov " 24 | 25 | "bnr " 26 | "j " 27 | 28 | "lstr " 29 | 30 | "newobj " 31 | "newarr " 32 | "setobj " 33 | "getobj " 34 | 35 | "call " 36 | "ret " 37 | "newcls " 38 | 39 | "undef " 40 | "null " 41 | ; 42 | 43 | _Static_assert((8 * INST_NR + 1 == sizeof(INST_NAME)), "Keep INST_NAME synced with insts"); 44 | -------------------------------------------------------------------------------- /src/inst_output.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/17/16. 3 | // 4 | 5 | #ifndef CRIPT_INST_OUTPUT_H 6 | #define CRIPT_INST_OUTPUT_H 7 | 8 | #include 9 | 10 | #include "inst.h" 11 | #include "inst_list.h" 12 | #include "value.h" 13 | #include "string_pool.h" 14 | #include "cvm.h" 15 | 16 | extern const char INST_NAME[]; 17 | 18 | static inline void 19 | output_inst(FILE *fout, Inst inst) 20 | { 21 | fprintf(fout, "%8.8s", INST_NAME + (inst.type << 3)); 22 | switch (inst.type) { 23 | case I_LI: 24 | case I_BNR: 25 | fprintf(fout, "$%d,\t%d\n", inst.i_rd, inst.i_imm); 26 | break; 27 | case I_ADD: 28 | case I_SUB: 29 | case I_MUL: 30 | case I_DIV: 31 | case I_MOD: 32 | case I_SEQ: 33 | case I_SLT: 34 | case I_SLE: 35 | case I_SET_OBJ: 36 | case I_GET_OBJ: 37 | case I_CALL: 38 | fprintf(fout, "$%d,\t$%d,\t$%d\n", inst.i_rd, inst.i_rs, inst.i_rt); 39 | break; 40 | case I_J: 41 | fprintf(fout, "%d\n", inst.i_imm); 42 | break; 43 | case I_LNOT: 44 | case I_MOV: 45 | fprintf(fout, "$%d,\t$%d\n", inst.i_rd, inst.i_rs); 46 | break; 47 | case I_LSTR: 48 | { 49 | CString *string = (CString*)(inst.i_imm); 50 | fprintf(fout, "$%d,\t\"%.*s\"\n", inst.i_rd, string->length, string->content); 51 | break; 52 | } 53 | case I_NEW_OBJ: 54 | case I_NEW_ARR: 55 | case I_RET: 56 | case I_UNDEFINED: 57 | case I_NULL: 58 | fprintf(fout, "$%d\n", inst.i_rd); 59 | break; 60 | case I_NEW_CLS: 61 | fprintf(fout, "$%d,\t0x%x\n", inst.i_rd, inst.i_imm); 62 | break; 63 | default: 64 | fprintf(fout, "\n"); 65 | break; 66 | } 67 | } 68 | 69 | static inline void 70 | output_inst_list(FILE *fout, InstList *list) 71 | { 72 | Inst *ptr = list->insts, 73 | *limit = list->insts + list->count; 74 | for (; ptr != limit; ++ptr) { 75 | output_inst(fout, *ptr); 76 | } 77 | } 78 | 79 | static inline void 80 | output_vm_state(FILE *fout, VMState *vm) 81 | { 82 | list_for_each(&vm->functions, func_node) { 83 | VMFunction *function = list_get(func_node, VMFunction, _linked); 84 | printf("Function: 0x%x, captured: %d:\n", (size_t)function, hash_size(function->capture_list)); 85 | output_inst_list(stdout, function->inst_list); 86 | printf("capture list:\n"); 87 | hash_for_each(function->capture_list, capture) { 88 | printf(" $%d -> $%d\n", capture->key, value_to_int(capture->value)); 89 | } 90 | printf("\n"); 91 | } 92 | } 93 | 94 | #endif //CRIPT_INST_OUTPUT_H 95 | -------------------------------------------------------------------------------- /src/ir_builder.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/21/16. 3 | // 4 | 5 | #include 6 | 7 | #include "ir_builder.h" 8 | 9 | #define ir_builder_from_bb(bb) \ 10 | container_of(list_from_node(&bb->_linked), IRBuilder, basic_blocks) 11 | 12 | static inline BasicBlock * 13 | _ir_builder_basic_block_new(IRBuilder *builder, BasicBlock *dominator) 14 | { 15 | BasicBlock *ret = malloc(sizeof(BasicBlock)); 16 | list_append(&builder->basic_blocks, &ret->_linked); 17 | ret->dominator = dominator; 18 | ret->inst_list = inst_list_new(16); 19 | ret->entry_point = value_from_ptr((void*)(0xFFFFFFFFu << 2)); 20 | ret->br_reg = 0; 21 | ret->constant_table = hash_new(HASH_MIN_CAPACITY); 22 | ret->numeric_value = hash_new(HASH_MIN_CAPACITY); 23 | hash_set_and_update( 24 | ret->constant_table, 25 | value_from_int(0)._int, 26 | value_from_int(0) 27 | ); 28 | hash_set_and_update( 29 | ret->numeric_value, 30 | 0, 31 | value_from_int(0) 32 | ); 33 | ret->then_bb = NULL; 34 | ret->else_bb = NULL; 35 | return ret; 36 | } 37 | 38 | static inline void 39 | _ir_builder_basic_block_destroy(BasicBlock *bb) 40 | { 41 | inst_list_destroy(bb->inst_list); 42 | hash_destroy(bb->constant_table); 43 | hash_destroy(bb->numeric_value); 44 | free(bb); 45 | } 46 | 47 | IRBuilder * 48 | ir_builder_new(size_t reserved_register) 49 | { 50 | IRBuilder *ret = malloc(sizeof(IRBuilder)); 51 | ret->register_nr = reserved_register; 52 | list_init(&ret->basic_blocks); 53 | _ir_builder_basic_block_new(ret, NULL); 54 | return ret; 55 | } 56 | 57 | BasicBlock * 58 | ir_builder_entry(IRBuilder *builder) 59 | { return list_get(list_head(&builder->basic_blocks), BasicBlock, _linked); } 60 | 61 | BasicBlock * 62 | ir_builder_new_basic_block(IRBuilder *builder, BasicBlock *dominator_bb) 63 | { 64 | assert(!dominator_bb || list_from_node(&dominator_bb->_linked) == &builder->basic_blocks); 65 | return _ir_builder_basic_block_new(builder, dominator_bb); 66 | } 67 | 68 | static inline void 69 | _ir_builder_bnr_else(InstList **inst_list, BasicBlock *bb, BasicBlock *dst) 70 | { 71 | int bnr_index = (*inst_list)->count; 72 | if (value_is_ptr(bb->else_bb->entry_point)) { 73 | inst_list_push( 74 | (*inst_list), 75 | cvm_inst_new_i_type( 76 | I_BNR, 77 | bb->br_reg, 78 | (int)value_to_ptr(dst->entry_point) 79 | ) 80 | ); 81 | dst->entry_point = value_from_ptr((void*)(bnr_index << 2)); 82 | } 83 | else { 84 | inst_list_push( 85 | (*inst_list), 86 | cvm_inst_new_i_type( 87 | I_BNR, 88 | bb->br_reg, 89 | value_to_int(dst->entry_point) - bnr_index - 1 90 | ) 91 | ); 92 | } 93 | } 94 | 95 | static inline void 96 | _ir_builder_jump_to(InstList **inst_list, BasicBlock *bb, BasicBlock *dst) 97 | { 98 | if (!dst) { 99 | // cannot allocate new register here 100 | inst_list_push( 101 | *inst_list, 102 | cvm_inst_new_d_type( 103 | I_UNDEFINED, 104 | 1, 0, 0 105 | ) 106 | ); 107 | inst_list_push( 108 | *inst_list, 109 | cvm_inst_new_d_type( 110 | I_RET, 111 | 1, 0, 0 112 | ) 113 | ); 114 | } 115 | else if (!list_next(&bb->_linked) || dst != list_get(list_next(&bb->_linked), BasicBlock, _linked)) { 116 | if (value_is_ptr(dst->entry_point)) { 117 | size_t j_index = (*inst_list)->count; 118 | inst_list_push( 119 | *inst_list, 120 | cvm_inst_new_i_type( 121 | I_J, 122 | 0, 123 | (int)value_to_ptr(dst->entry_point) 124 | ) 125 | ); 126 | dst->entry_point = value_from_ptr((void*)(j_index << 2)); 127 | } 128 | else { 129 | inst_list_push( 130 | *inst_list, 131 | cvm_inst_new_i_type( 132 | I_J, 133 | 0, 134 | value_to_int(dst->entry_point) 135 | ) 136 | ); 137 | } 138 | } 139 | } 140 | 141 | InstList * 142 | ir_builder_destroy(IRBuilder *builder) 143 | { 144 | InstList *ret = inst_list_new(16); 145 | list_for_each(&builder->basic_blocks, node) { 146 | BasicBlock *bb = list_get(node, BasicBlock, _linked); 147 | size_t entry_point = ret->count; 148 | 149 | if (value_is_ptr(bb->entry_point)) { 150 | int inst_to_fill = (int)value_to_ptr(bb->entry_point) >> 2; 151 | while (inst_to_fill >= 0) { 152 | Inst *inst = ret->insts + inst_to_fill; 153 | inst_to_fill = inst->i_imm >> 2; 154 | if (inst->type == I_J) { 155 | inst->i_imm = entry_point; 156 | } 157 | else { 158 | inst->i_imm = entry_point - (inst - ret->insts) - 1; 159 | } 160 | } 161 | } 162 | 163 | bb->entry_point = value_from_int(entry_point); 164 | 165 | for ( 166 | Inst *ptr = bb->inst_list->insts, 167 | *limit = bb->inst_list->insts + bb->inst_list->count; 168 | ptr != limit; 169 | ++ptr 170 | ) { 171 | inst_list_push(ret, *ptr); 172 | } 173 | 174 | if (bb->br_reg) { 175 | _ir_builder_bnr_else(&ret, bb, bb->else_bb); 176 | _ir_builder_jump_to(&ret, bb, bb->then_bb); 177 | } 178 | else { 179 | _ir_builder_jump_to(&ret, bb, bb->else_bb); 180 | } 181 | } 182 | 183 | while (list_size(&builder->basic_blocks)) { 184 | _ir_builder_basic_block_destroy( 185 | list_get(list_unlink(list_head(&builder->basic_blocks)), BasicBlock, _linked) 186 | ); 187 | } 188 | 189 | return ret; 190 | } 191 | 192 | size_t 193 | ir_builder_allocate_register(IRBuilder *builder) 194 | { return ++(builder->register_nr); } 195 | 196 | #define assert_basic_block_not_end(bb) \ 197 | assert(!bb->br_reg && !bb->else_bb) 198 | 199 | void 200 | ir_builder_br(BasicBlock *bb, size_t rd, BasicBlock *then_bb, BasicBlock *else_bb) 201 | { 202 | assert_basic_block_not_end(bb); 203 | bb->br_reg = rd; 204 | bb->then_bb = then_bb; 205 | bb->else_bb = else_bb; 206 | } 207 | 208 | void 209 | ir_builder_j(BasicBlock *bb, BasicBlock *successor_bb) 210 | { 211 | assert_basic_block_not_end(bb); 212 | bb->br_reg = 0; 213 | bb->else_bb = successor_bb; 214 | } 215 | 216 | static inline int 217 | _ir_builder_find_in_constant_table(BasicBlock *bb, Value constant) 218 | { 219 | BasicBlock *begin_bb = bb; 220 | while (bb) { 221 | Value result = hash_find(bb->constant_table, (uintptr_t)constant._int); 222 | if (!value_is_undefined(result)) { 223 | while (begin_bb) { 224 | Value path_result = hash_find(begin_bb->constant_table, (uintptr_t)constant._int); 225 | if (value_is_undefined(path_result)) { 226 | hash_set_and_update( 227 | begin_bb->constant_table, 228 | (uintptr_t)constant._int, 229 | result 230 | ); 231 | begin_bb = begin_bb->dominator; 232 | } 233 | else { break; } 234 | } 235 | return value_to_int(result); 236 | } 237 | bb = bb->dominator; 238 | } 239 | return -1; 240 | } 241 | 242 | static inline Value 243 | _ir_builder_find_in_numeric_table(BasicBlock *bb, size_t reg) 244 | { 245 | BasicBlock *begin_bb = bb; 246 | while (bb) { 247 | Value result = hash_find(bb->numeric_value, (uintptr_t)reg); 248 | if (!value_is_undefined(result)) { 249 | while (begin_bb) { 250 | Value path_result = hash_find(begin_bb->numeric_value, (uintptr_t)reg); 251 | if (value_is_undefined(path_result)) { 252 | hash_set_and_update( 253 | begin_bb->numeric_value, 254 | (uintptr_t)reg, 255 | result 256 | ); 257 | begin_bb = begin_bb->dominator; 258 | } 259 | else { break; } 260 | } 261 | return result; 262 | } 263 | bb = bb->dominator; 264 | } 265 | return value_undefined(); 266 | } 267 | 268 | size_t 269 | ir_builder_li(BasicBlock *bb, int imm) 270 | { 271 | int constant = _ir_builder_find_in_constant_table(bb, value_from_int(imm)); 272 | if (constant >= 0) { return (size_t)constant; } 273 | 274 | assert_basic_block_not_end(bb); 275 | 276 | size_t ret = ir_builder_allocate_register(ir_builder_from_bb(bb)); 277 | inst_list_push( 278 | bb->inst_list, 279 | cvm_inst_new_i_type( 280 | I_LI, 281 | ret, 282 | imm 283 | ) 284 | ); 285 | hash_set_and_update( 286 | bb->constant_table, 287 | (uintptr_t)(value_from_int(imm)._int), 288 | value_from_int(ret) 289 | ); 290 | hash_set_and_update( 291 | bb->numeric_value, 292 | ret, 293 | value_from_int(imm) 294 | ); 295 | return ret; 296 | } 297 | 298 | size_t 299 | ir_builder_lstr(BasicBlock *bb, CString *string) 300 | { 301 | int constant = _ir_builder_find_in_constant_table(bb, value_from_string(string)); 302 | if (constant >= 0) { return (size_t)constant; } 303 | 304 | assert_basic_block_not_end(bb); 305 | 306 | size_t ret = ir_builder_allocate_register(ir_builder_from_bb(bb)); 307 | inst_list_push( 308 | bb->inst_list, 309 | cvm_inst_new_i_type( 310 | I_LSTR, 311 | ret, 312 | (int)string 313 | ) 314 | ); 315 | hash_set_and_update( 316 | bb->constant_table, 317 | (uintptr_t)(value_from_string(string)._int), 318 | value_from_int(ret) 319 | ); 320 | return ret; 321 | } 322 | 323 | #define _ir_fold_constant(bb, rs, rt, op) \ 324 | do { \ 325 | Value const_l = _ir_builder_find_in_numeric_table(bb, rs), \ 326 | const_r = _ir_builder_find_in_numeric_table(bb, rt); \ 327 | if (value_is_int(const_l) && value_is_int(const_r)) { \ 328 | return ir_builder_li(bb, value_to_int(const_l) op value_to_int(const_r)); \ 329 | } \ 330 | } while (0) 331 | 332 | size_t 333 | ir_builder_add(BasicBlock *bb, size_t rs, size_t rt) 334 | { 335 | assert_basic_block_not_end(bb); 336 | _ir_fold_constant(bb, rs, rt, +); 337 | 338 | size_t ret = ir_builder_allocate_register(ir_builder_from_bb(bb)); 339 | inst_list_push( 340 | bb->inst_list, 341 | cvm_inst_new_d_type( 342 | I_ADD, 343 | ret, 344 | rs, 345 | rt 346 | ) 347 | ); 348 | return ret; 349 | } 350 | 351 | size_t 352 | ir_builder_sub(BasicBlock *bb, size_t rs, size_t rt) 353 | { 354 | assert_basic_block_not_end(bb); 355 | _ir_fold_constant(bb, rs, rt, -); 356 | 357 | size_t ret = ir_builder_allocate_register(ir_builder_from_bb(bb)); 358 | inst_list_push( 359 | bb->inst_list, 360 | cvm_inst_new_d_type( 361 | I_SUB, 362 | ret, 363 | rs, 364 | rt 365 | ) 366 | ); 367 | return ret; 368 | } 369 | 370 | size_t 371 | ir_builder_mul(BasicBlock *bb, size_t rs, size_t rt) 372 | { 373 | assert_basic_block_not_end(bb); 374 | _ir_fold_constant(bb, rs, rt, *); 375 | 376 | size_t ret = ir_builder_allocate_register(ir_builder_from_bb(bb)); 377 | inst_list_push( 378 | bb->inst_list, 379 | cvm_inst_new_d_type( 380 | I_MUL, 381 | ret, 382 | rs, 383 | rt 384 | ) 385 | ); 386 | return ret; 387 | } 388 | 389 | size_t 390 | ir_builder_div(BasicBlock *bb, size_t rs, size_t rt) 391 | { 392 | assert_basic_block_not_end(bb); 393 | _ir_fold_constant(bb, rs, rt, /); 394 | 395 | size_t ret = ir_builder_allocate_register(ir_builder_from_bb(bb)); 396 | inst_list_push( 397 | bb->inst_list, 398 | cvm_inst_new_d_type( 399 | I_DIV, 400 | ret, 401 | rs, 402 | rt 403 | ) 404 | ); 405 | return ret; 406 | } 407 | 408 | size_t 409 | ir_builder_mod(BasicBlock *bb, size_t rs, size_t rt) 410 | { 411 | assert_basic_block_not_end(bb); 412 | _ir_fold_constant(bb, rs, rt, %); 413 | 414 | size_t ret = ir_builder_allocate_register(ir_builder_from_bb(bb)); 415 | inst_list_push( 416 | bb->inst_list, 417 | cvm_inst_new_d_type( 418 | I_MOD, 419 | ret, 420 | rs, 421 | rt 422 | ) 423 | ); 424 | return ret; 425 | } 426 | 427 | size_t 428 | ir_builder_seq(BasicBlock *bb, size_t rs, size_t rt) 429 | { 430 | assert_basic_block_not_end(bb); 431 | _ir_fold_constant(bb, rs, rt, ==); 432 | 433 | size_t ret = ir_builder_allocate_register(ir_builder_from_bb(bb)); 434 | inst_list_push( 435 | bb->inst_list, 436 | cvm_inst_new_d_type( 437 | I_SEQ, 438 | ret, 439 | rs, 440 | rt 441 | ) 442 | ); 443 | return ret; 444 | } 445 | 446 | size_t 447 | ir_builder_slt(BasicBlock *bb, size_t rs, size_t rt) 448 | { 449 | assert_basic_block_not_end(bb); 450 | _ir_fold_constant(bb, rs, rt, <); 451 | 452 | size_t ret = ir_builder_allocate_register(ir_builder_from_bb(bb)); 453 | inst_list_push( 454 | bb->inst_list, 455 | cvm_inst_new_d_type( 456 | I_SLT, 457 | ret, 458 | rs, 459 | rt 460 | ) 461 | ); 462 | return ret; 463 | } 464 | 465 | size_t 466 | ir_builder_sle(BasicBlock *bb, size_t rs, size_t rt) 467 | { 468 | assert_basic_block_not_end(bb); 469 | _ir_fold_constant(bb, rs, rt, <=); 470 | 471 | size_t ret = ir_builder_allocate_register(ir_builder_from_bb(bb)); 472 | inst_list_push( 473 | bb->inst_list, 474 | cvm_inst_new_d_type( 475 | I_SLE, 476 | ret, 477 | rs, 478 | rt 479 | ) 480 | ); 481 | return ret; 482 | } 483 | 484 | size_t 485 | ir_builder_lnot(BasicBlock *bb, size_t rs) 486 | { 487 | assert_basic_block_not_end(bb); 488 | 489 | size_t ret = ir_builder_allocate_register(ir_builder_from_bb(bb)); 490 | inst_list_push( 491 | bb->inst_list, 492 | cvm_inst_new_d_type( 493 | I_LNOT, 494 | ret, 495 | rs, 496 | 0 497 | ) 498 | ); 499 | return ret; 500 | } 501 | 502 | size_t 503 | ir_builder_mov(BasicBlock *bb, size_t rs) 504 | { 505 | assert_basic_block_not_end(bb); 506 | 507 | size_t ret = ir_builder_allocate_register(ir_builder_from_bb(bb)); 508 | inst_list_push( 509 | bb->inst_list, 510 | cvm_inst_new_d_type( 511 | I_MOV, 512 | ret, 513 | rs, 514 | 0 515 | ) 516 | ); 517 | 518 | Value constant = _ir_builder_find_in_numeric_table(bb, rs); 519 | if (value_is_int(constant)) { 520 | hash_set_and_update( 521 | bb->numeric_value, 522 | ret, 523 | constant 524 | ); 525 | } 526 | 527 | return ret; 528 | } 529 | 530 | void 531 | ir_builder_mov_upper(BasicBlock *bb, size_t rd, size_t rs) 532 | { 533 | assert_basic_block_not_end(bb); 534 | 535 | inst_list_push( 536 | bb->inst_list, 537 | cvm_inst_new_d_type( 538 | I_MOV, 539 | rd, 540 | rs, 541 | 0 542 | ) 543 | ); 544 | 545 | Value constant = _ir_builder_find_in_numeric_table(bb, rs); 546 | if (value_is_int(constant)) { 547 | hash_set_and_update( 548 | bb->numeric_value, 549 | rd, 550 | constant 551 | ); 552 | } 553 | } 554 | 555 | size_t 556 | ir_builder_new_obj(BasicBlock *bb) 557 | { 558 | assert_basic_block_not_end(bb); 559 | 560 | size_t ret = ir_builder_allocate_register(ir_builder_from_bb(bb)); 561 | inst_list_push( 562 | bb->inst_list, 563 | cvm_inst_new_d_type( 564 | I_NEW_OBJ, 565 | ret, 0, 0 566 | ) 567 | ); 568 | return ret; 569 | } 570 | 571 | size_t 572 | ir_builder_new_arr(BasicBlock *bb) 573 | { 574 | assert_basic_block_not_end(bb); 575 | 576 | size_t ret = ir_builder_allocate_register(ir_builder_from_bb(bb)); 577 | inst_list_push( 578 | bb->inst_list, 579 | cvm_inst_new_d_type( 580 | I_NEW_ARR, 581 | ret, 0, 0 582 | ) 583 | ); 584 | return ret; 585 | } 586 | 587 | void 588 | ir_builder_set_obj(BasicBlock *bb, size_t r_obj, size_t r_val, size_t r_key) 589 | { 590 | assert_basic_block_not_end(bb); 591 | 592 | inst_list_push( 593 | bb->inst_list, 594 | cvm_inst_new_d_type( 595 | I_SET_OBJ, 596 | r_obj, 597 | r_val, 598 | r_key 599 | ) 600 | ); 601 | } 602 | 603 | size_t 604 | ir_builder_get_obj(BasicBlock *bb, size_t r_obj, size_t r_key) 605 | { 606 | assert_basic_block_not_end(bb); 607 | 608 | size_t ret = ir_builder_allocate_register(ir_builder_from_bb(bb)); 609 | inst_list_push( 610 | bb->inst_list, 611 | cvm_inst_new_d_type( 612 | I_GET_OBJ, 613 | ret, 614 | r_obj, 615 | r_key 616 | ) 617 | ); 618 | return ret; 619 | } 620 | 621 | size_t 622 | ir_builder_call(BasicBlock *bb, size_t r_func, size_t r_arg) 623 | { 624 | assert_basic_block_not_end(bb); 625 | 626 | size_t ret = ir_builder_allocate_register(ir_builder_from_bb(bb)); 627 | inst_list_push( 628 | bb->inst_list, 629 | cvm_inst_new_d_type( 630 | I_CALL, 631 | ret, 632 | r_func, 633 | r_arg 634 | ) 635 | ); 636 | return ret; 637 | } 638 | 639 | void 640 | ir_builder_ret(BasicBlock *bb, size_t r_val) 641 | { 642 | assert_basic_block_not_end(bb); 643 | 644 | inst_list_push( 645 | bb->inst_list, 646 | cvm_inst_new_d_type( 647 | I_RET, 648 | r_val, 649 | 0, 0 650 | ) 651 | ); 652 | } 653 | 654 | size_t 655 | ir_builder_new_cls(BasicBlock *bb, VMFunction *func) 656 | { 657 | assert_basic_block_not_end(bb); 658 | 659 | size_t ret = ir_builder_allocate_register(ir_builder_from_bb(bb)); 660 | inst_list_push( 661 | bb->inst_list, 662 | cvm_inst_new_i_type( 663 | I_NEW_CLS, 664 | ret, 665 | (int)func 666 | ) 667 | ); 668 | return ret; 669 | } 670 | 671 | size_t 672 | ir_builder_undefined(BasicBlock *bb) 673 | { 674 | assert_basic_block_not_end(bb); 675 | 676 | size_t ret = ir_builder_allocate_register(ir_builder_from_bb(bb)); 677 | inst_list_push( 678 | bb->inst_list, 679 | cvm_inst_new_d_type( 680 | I_UNDEFINED, 681 | ret, 682 | 0, 0 683 | ) 684 | ); 685 | return ret; 686 | } 687 | 688 | size_t 689 | ir_builder_null(BasicBlock *bb) 690 | { 691 | assert_basic_block_not_end(bb); 692 | 693 | size_t ret = ir_builder_allocate_register(ir_builder_from_bb(bb)); 694 | inst_list_push( 695 | bb->inst_list, 696 | cvm_inst_new_d_type( 697 | I_NULL, 698 | ret, 699 | 0, 0 700 | ) 701 | ); 702 | return ret; 703 | } 704 | 705 | void 706 | ir_builder_halt(BasicBlock *bb) 707 | { 708 | assert_basic_block_not_end(bb); 709 | 710 | inst_list_push( 711 | bb->inst_list, 712 | cvm_inst_new_d_type( 713 | I_HALT, 714 | 0, 0, 0 715 | ) 716 | ); 717 | } 718 | -------------------------------------------------------------------------------- /src/ir_builder.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/21/16. 3 | // 4 | 5 | #ifndef CRIPT_IR_BUILDER_H 6 | #define CRIPT_IR_BUILDER_H 7 | 8 | #include "inst.h" 9 | #include "inst_list.h" 10 | #include "string_pool.h" 11 | #include "value.h" 12 | #include "hash.h" 13 | 14 | #include "list.h" 15 | 16 | typedef struct BasicBlock 17 | { 18 | LinkedNode _linked; 19 | 20 | struct BasicBlock *dominator; 21 | InstList *inst_list; 22 | Value entry_point; 23 | size_t br_reg; 24 | Hash *constant_table; 25 | Hash *numeric_value; 26 | struct BasicBlock *then_bb; 27 | struct BasicBlock *else_bb; 28 | } BasicBlock; 29 | 30 | typedef struct IRBuilder 31 | { 32 | size_t register_nr; 33 | LinkedList basic_blocks; 34 | } IRBuilder; 35 | 36 | IRBuilder *ir_builder_new(size_t reserved_register); 37 | BasicBlock *ir_builder_entry(IRBuilder *builder); 38 | BasicBlock *ir_builder_new_basic_block(IRBuilder *builder, BasicBlock *dominator_bb); 39 | InstList *ir_builder_destroy(IRBuilder *builder); 40 | size_t ir_builder_allocate_register(IRBuilder *builder); 41 | 42 | void ir_builder_br(BasicBlock *bb, size_t rd, BasicBlock *then_bb, BasicBlock *else_bb); 43 | void ir_builder_j(BasicBlock *bb, BasicBlock *successor_bb); 44 | 45 | size_t ir_builder_li(BasicBlock *bb, int imm); 46 | size_t ir_builder_lstr(BasicBlock *bb, CString *string); 47 | size_t ir_builder_add(BasicBlock *bb, size_t rs, size_t rt); 48 | size_t ir_builder_sub(BasicBlock *bb, size_t rs, size_t rt); 49 | size_t ir_builder_mul(BasicBlock *bb, size_t rs, size_t rt); 50 | size_t ir_builder_div(BasicBlock *bb, size_t rs, size_t rt); 51 | size_t ir_builder_mod(BasicBlock *bb, size_t rs, size_t rt); 52 | size_t ir_builder_seq(BasicBlock *bb, size_t rs, size_t rt); 53 | size_t ir_builder_slt(BasicBlock *bb, size_t rs, size_t rt); 54 | size_t ir_builder_sle(BasicBlock *bb, size_t rs, size_t rt); 55 | size_t ir_builder_lnot(BasicBlock *bb, size_t rs); 56 | size_t ir_builder_mov(BasicBlock *bb, size_t rs); 57 | void ir_builder_mov_upper(BasicBlock *bb, size_t rd, size_t rs); 58 | size_t ir_builder_new_obj(BasicBlock *bb); 59 | size_t ir_builder_new_arr(BasicBlock *bb); 60 | void ir_builder_set_obj(BasicBlock *bb, size_t r_obj, size_t r_val, size_t r_key); 61 | size_t ir_builder_get_obj(BasicBlock *bb, size_t r_obj, size_t r_key); 62 | size_t ir_builder_call(BasicBlock *bb, size_t r_func, size_t r_reg); 63 | void ir_builder_ret(BasicBlock *bb, size_t r_val); 64 | size_t ir_builder_new_cls(BasicBlock *bb, VMFunction *func); 65 | size_t ir_builder_undefined(BasicBlock *bb); 66 | size_t ir_builder_null(BasicBlock *bb); 67 | void ir_builder_halt(BasicBlock *bb); 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /src/libs.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/19/16. 3 | // 4 | 5 | #include 6 | 7 | #include "error.h" 8 | #include "libs.h" 9 | 10 | #define register_function(vm, func, name) \ 11 | cvm_register_in_global(vm, cvm_create_light_function(vm, func), name) 12 | 13 | #define register_value(vm, value, name) \ 14 | cvm_register_in_global(vm, value, name) 15 | 16 | static Value 17 | _lib_print(VMState *vm, Value value) 18 | { 19 | Hash *args = value_to_ptr(value); 20 | 21 | for (uintptr_t i = 1; i < hash_size(args); ++i) { 22 | Value val = hash_find(args, i); 23 | 24 | if (value_is_int(val)) { 25 | printf("%d", value_to_int(val)); 26 | } 27 | else { 28 | CString *string = value_to_string(val); 29 | printf("%.*s", string->length, string->content); 30 | } 31 | } 32 | 33 | return value_undefined(); 34 | } 35 | 36 | static Value 37 | _lib_println(VMState *vm, Value value) 38 | { 39 | Hash *args = value_to_ptr(value); 40 | 41 | for (uintptr_t i = 1; i < hash_size(args); ++i) { 42 | Value val = hash_find(args, i); 43 | 44 | if (value_is_int(val)) { 45 | printf("%d", value_to_int(val)); 46 | } 47 | else { 48 | CString *string = value_to_string(val); 49 | printf("%.*s", string->length, string->content); 50 | } 51 | } 52 | printf("\n"); 53 | 54 | return value_undefined(); 55 | } 56 | 57 | static Value 58 | _lib_foreach(VMState *vm, Value value) 59 | { 60 | Hash *args = value_to_ptr(value); 61 | Hash *array = value_to_ptr(hash_find(args, 1)); 62 | Value callback = hash_find(args, 2); 63 | 64 | for (size_t i = 0; i < hash_size(array); ++i) { 65 | Hash *cb_arg = cvm_create_array(vm, HASH_MIN_CAPACITY); 66 | 67 | cb_arg = cvm_set_hash(vm, cb_arg, 0, value_from_ptr(cvm_get_global(vm))); 68 | cb_arg = cvm_set_hash(vm, cb_arg, 1, hash_find(array, i)); 69 | 70 | cvm_state_call_function(vm, callback, value_from_ptr(cb_arg)); 71 | } 72 | 73 | return value_undefined(); 74 | } 75 | 76 | static Value 77 | _lib_random(VMState *vm, Value value) 78 | { return value_from_int(random()); } 79 | 80 | static Value 81 | _lib_import(VMState *vm, Value value) 82 | { 83 | Hash *args = value_to_ptr(value); 84 | CString *path = value_to_string(hash_find(args, 1)); 85 | ParseState *state = parse_state_expand_from_file(vm, path->content); 86 | parse(state); 87 | Value ret = cvm_state_import_from_parse_state(vm, state); 88 | parse_state_destroy(state); 89 | return ret; 90 | } 91 | 92 | static Value 93 | _lib_concat(VMState *vm, Value value) 94 | { 95 | Hash *args = value_to_ptr(value); 96 | 97 | char buffer[256], 98 | *ptr = buffer; 99 | 100 | for (size_t i = 1; i < hash_size(args); ++i) { 101 | CString *string = value_to_string(hash_find(args, i)); 102 | strncpy(ptr, string->content, 255 - (ptr - buffer)); 103 | ptr += string->length; 104 | if (ptr - buffer >= 255) { 105 | ptr = buffer + 255; 106 | break; 107 | } 108 | } 109 | 110 | return value_from_string(string_pool_insert_vec(&vm->string_pool, buffer, ptr - buffer)); 111 | } 112 | 113 | static Value 114 | _lib_typeof(VMState *vm, Value value) 115 | { 116 | Hash *args = value_to_ptr(value); 117 | Value query_val = hash_find(args, 1); 118 | 119 | if (value_is_int(query_val)) { 120 | return cvm_get_cstring_value(vm, "integer"); 121 | } 122 | else if (value_is_string(query_val)) { 123 | return cvm_get_cstring_value(vm, "string"); 124 | } 125 | else if (value_is_null(query_val)) { 126 | return cvm_get_cstring_value(vm, "null"); 127 | } 128 | else if (value_is_undefined(query_val)) { 129 | return cvm_get_cstring_value(vm, "undefined"); 130 | } 131 | else { 132 | Hash *hash = value_to_ptr(query_val); 133 | while (hash->type == HT_REDIRECT) hash = (Hash*)hash->size; 134 | assert(hash->type != HT_GC_LEFT); 135 | 136 | switch (hash->type) { 137 | case HT_OBJECT: return cvm_get_cstring_value(vm, "object"); 138 | case HT_ARRAY: return cvm_get_cstring_value(vm, "array"); 139 | case HT_LIGHTFUNC: return cvm_get_cstring_value(vm, "light_function"); 140 | case HT_CLOSURE: return cvm_get_cstring_value(vm, "closure"); 141 | case HT_USERDATA: return cvm_get_cstring_value(vm, "userdata"); 142 | default: assert(0); return value_undefined(); 143 | } 144 | } 145 | } 146 | 147 | static Value 148 | _lib_sizeof(VMState *vm, Value value) 149 | { 150 | Hash *args = value_to_ptr(value); 151 | Value query_val = hash_find(args, 1); 152 | 153 | if (value_is_string(query_val)) { 154 | CString *string = value_to_string(query_val); 155 | return value_from_int(string->length); 156 | } 157 | else if (value_is_ptr(query_val)) { 158 | Hash *hash = value_to_ptr(query_val); 159 | while (hash->type == HT_REDIRECT) hash = (Hash*)hash->size; 160 | assert(hash->type != HT_GC_LEFT); 161 | 162 | switch (hash->type) { 163 | case HT_OBJECT: 164 | case HT_ARRAY: 165 | return value_from_int(hash_size(hash)); 166 | case HT_LIGHTFUNC: 167 | case HT_CLOSURE: 168 | case HT_USERDATA: 169 | return value_from_int(-1); 170 | default: assert(0); 171 | } 172 | } 173 | 174 | return value_from_int(-1); 175 | } 176 | 177 | static Value 178 | _lib_to_string(VMState *vm, Value value) 179 | { 180 | Hash *args = value_to_ptr(value); 181 | Value query_val = hash_find(args, 1); 182 | 183 | if (value_is_int(query_val)) { 184 | char buffer[30]; 185 | sprintf(buffer, "%d", value_to_int(query_val)); 186 | return cvm_get_cstring_value(vm, buffer); 187 | } 188 | else { 189 | return _lib_typeof(vm, value); 190 | } 191 | } 192 | 193 | static Value 194 | _lib_parse_number(VMState *vm, Value value) 195 | { 196 | Hash *args = value_to_ptr(value); 197 | CString *string = value_to_string(hash_find(args, 1)); 198 | int ret_val; 199 | sscanf(string->content, "%d", &ret_val); 200 | return value_from_int(ret_val); 201 | } 202 | 203 | static Value 204 | _lib_char_at(VMState *vm, Value value) 205 | { 206 | Hash *args = value_to_ptr(value); 207 | CString *string = value_to_string(hash_find(args, 1)); 208 | int index = value_to_int(hash_find(args, 2)); 209 | 210 | if (index < 0 || index >= string->length) { 211 | error_f("Out of range when get char_at(%.*s, %d)", string->length, string->content, index); 212 | return value_undefined(); 213 | } 214 | 215 | char buffer[2]; 216 | buffer[0] = string->content[index]; 217 | buffer[1] = 0; 218 | 219 | return cvm_get_cstring_value(vm, buffer); 220 | } 221 | 222 | static Value 223 | _lib_char_code_at(VMState *vm, Value value) 224 | { 225 | Hash *args = value_to_ptr(value); 226 | CString *string = value_to_string(hash_find(args, 1)); 227 | int index = value_to_int(hash_find(args, 2)); 228 | 229 | if (index < 0 || index >= string->length) { 230 | error_f("Out of range when get char_at(%.*s, %d)", string->length, string->content, index); 231 | return value_undefined(); 232 | } 233 | 234 | return value_from_int(string->content[index]); 235 | } 236 | 237 | void 238 | lib_register(VMState *vm) 239 | { 240 | register_value(vm, value_null(), "null"); 241 | register_value(vm, value_undefined(), "undefined"); 242 | 243 | register_function(vm, _lib_char_at, "char_at"); 244 | register_function(vm, _lib_char_code_at, "char_code_at"); 245 | register_function(vm, _lib_concat, "concat"); 246 | register_function(vm, _lib_foreach, "foreach"); 247 | register_function(vm, _lib_import, "import"); 248 | register_function(vm, _lib_parse_number, "parse_number"); 249 | register_function(vm, _lib_print, "print"); 250 | register_function(vm, _lib_println, "println"); 251 | register_function(vm, _lib_random, "random"); 252 | register_function(vm, _lib_sizeof, "sizeof"); 253 | register_function(vm, _lib_to_string, "to_string"); 254 | register_function(vm, _lib_typeof, "typeof"); 255 | } 256 | -------------------------------------------------------------------------------- /src/libs.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/19/16. 3 | // 4 | 5 | #ifndef CRIPT_LIBS_H 6 | #define CRIPT_LIBS_H 7 | 8 | #include "cvm.h" 9 | 10 | void lib_register(VMState *vm); 11 | 12 | #endif //CRIPT_LIBS_H 13 | -------------------------------------------------------------------------------- /src/list.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/4/16. 3 | // 4 | 5 | #ifndef CRIPT_LIST_H 6 | #define CRIPT_LIST_H 7 | 8 | #include 9 | 10 | #ifndef container_of 11 | #define container_of(ptr, type, mem) \ 12 | ({ const typeof(((type*)0)->mem) *__mptr = (ptr); \ 13 | (type*)((char*)__mptr - offsetof(type, mem)); }) 14 | #endif 15 | 16 | typedef struct LinkedNode 17 | { 18 | struct LinkedNode **prev; 19 | struct LinkedNode *next; 20 | struct LinkedList *list; 21 | } LinkedNode; 22 | 23 | typedef struct LinkedList 24 | { 25 | struct LinkedNode *head; 26 | struct LinkedNode **tail; 27 | size_t size; 28 | } LinkedList; 29 | 30 | #define list_get(ptr, type, list) container_of(ptr, type, list) 31 | #define list_from_node(node) ((node)->list) 32 | #define list_node_linked(node) (list_from_node(node)) 33 | 34 | #define list_head(list) ((list)->head) 35 | #define list_tail(list) container_of((list)->tail, LinkedNode, next) 36 | #define list_size(list) ((list)->size) 37 | #define list_next(node) ((node)->next) 38 | #define list_prev(node) \ 39 | (((node)->prev == &((node)->list->head)) \ 40 | ? NULL \ 41 | : container_of((node)->prev, LinkedNode, next)) 42 | 43 | #define list_for_each(list, n) \ 44 | for (LinkedNode *n = list_head(list); n; n = list_next(n)) 45 | 46 | #define list_for_each_r(list, n) \ 47 | for (LinkedNode *n = list_tail(list); n; n = list_prev(n)) 48 | 49 | static inline 50 | void list_node_init(LinkedNode *node) 51 | { node->list = NULL; } 52 | 53 | static inline 54 | void list_init(LinkedList *list) 55 | { 56 | list->head = NULL; 57 | list->tail = &(list->head); 58 | list->size = 0; 59 | } 60 | 61 | static inline LinkedNode * 62 | list_append(LinkedList *list, LinkedNode *node) 63 | { 64 | node->prev = list->tail; 65 | node->next = NULL; 66 | node->list = list; 67 | *(list->tail) = node; 68 | list->tail = &(node->next); 69 | 70 | ++list->size; 71 | 72 | return node; 73 | } 74 | 75 | static inline LinkedNode * 76 | list_prepend(LinkedList *list, LinkedNode *node) 77 | { 78 | node->next = list->head; 79 | node->prev = &(list->head); 80 | node->list = list; 81 | list->head = node; 82 | if (node->next) { 83 | node->next->prev = &(node->next); 84 | } 85 | else { 86 | list->tail = &(node->next); 87 | } 88 | 89 | ++list->size; 90 | 91 | return node; 92 | } 93 | 94 | static inline LinkedNode * 95 | list_before(LinkedNode *old_node, LinkedNode *new_node) 96 | { 97 | new_node->prev = old_node->prev; 98 | *(new_node->prev) = new_node; 99 | new_node->next = old_node; 100 | old_node->prev = &(new_node->next); 101 | new_node->list = old_node->list; 102 | 103 | ++old_node->list->size; 104 | 105 | return new_node; 106 | } 107 | 108 | static inline LinkedNode * 109 | list_after(LinkedNode *old_node, LinkedNode *new_node) 110 | { 111 | new_node->next = old_node->next; 112 | new_node->prev = &(old_node->next); 113 | old_node->next = new_node; 114 | new_node->list = old_node->list; 115 | if (new_node->next) { 116 | new_node->next->prev = &(new_node->next); 117 | } 118 | else { 119 | old_node->list->tail = &(new_node->next); 120 | } 121 | 122 | ++old_node->list->size; 123 | 124 | return new_node; 125 | } 126 | 127 | static inline LinkedNode * 128 | list_unlink(LinkedNode *node) 129 | { 130 | *(node->prev) = node->next; 131 | if (node->next) { 132 | node->next->prev = node->prev; 133 | } 134 | else { 135 | node->list->tail = node->prev; 136 | } 137 | --node->list->size; 138 | node->list = NULL; 139 | return node; 140 | } 141 | 142 | static inline void 143 | list_move(LinkedList *dst, LinkedList *src) 144 | { 145 | list_init(dst); 146 | while (list_size(src)) { 147 | list_append(dst, list_unlink(list_head(src))); 148 | } 149 | } 150 | 151 | #endif //CRIPT_LIST_H 152 | -------------------------------------------------------------------------------- /src/parse.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/16/16. 3 | // 4 | 5 | #ifndef CRIPT_PARSE_H 6 | #define CRIPT_PARSE_H 7 | 8 | #include "list.h" 9 | 10 | #include "inst_list.h" 11 | #include "string_pool.h" 12 | #include "hash.h" 13 | 14 | typedef struct VMFunction VMFunction; 15 | 16 | typedef struct ParseState 17 | { 18 | const char *filename; 19 | int line; 20 | int column; 21 | 22 | const char *content; 23 | int should_free_content; 24 | size_t content_length; 25 | 26 | StringPool *string_pool; 27 | 28 | const char *current; 29 | const char *limit; 30 | 31 | int peaking_token; 32 | intptr_t peaking_value; 33 | 34 | LinkedList functions; 35 | 36 | LinkedList function_stack; 37 | } ParseState; 38 | 39 | ParseState *parse_state_new_from_string(const char *content); 40 | ParseState *parse_state_new_from_file(const char *path); 41 | ParseState *parse_state_expand_from_file(VMState *vm, const char *path); 42 | void parse_state_destroy(ParseState *state); 43 | 44 | VMFunction *parse_get_main_function(ParseState *state); 45 | 46 | void parse(ParseState *state); 47 | 48 | #endif //CRIPT_PARSE_H 49 | -------------------------------------------------------------------------------- /src/parse_internal.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/16/16. 3 | // 4 | 5 | #ifndef CRIPT_PARSE_INTERNAL_H 6 | #define CRIPT_PARSE_INTERNAL_H 7 | 8 | #include "list.h" 9 | #include "ir_builder.h" 10 | 11 | enum Token 12 | { 13 | TOK_EOF = -1, 14 | TOK_UNKNOWN = 0, 15 | TOK_ID = 256, 16 | TOK_NUM, 17 | TOK_STRING, 18 | 19 | TOK_GE, 20 | TOK_LE, 21 | TOK_EQ, 22 | TOK_NE, 23 | 24 | TOK_AND, 25 | TOK_OR, 26 | 27 | TOK_SHL, 28 | TOK_SHR, 29 | }; 30 | 31 | enum ReservedWord 32 | { 33 | R_ZERO, 34 | R_LET = 1, 35 | R_IF, 36 | R_ELSE, 37 | R_WHILE, 38 | R_FUNCTION, 39 | R_RETURN, 40 | R_HALT, 41 | 42 | RESERVED_WORD_NR 43 | }; 44 | 45 | typedef struct ParseScope 46 | { 47 | LinkedNode _linked; 48 | 49 | Hash *symbol_table; 50 | Hash *upper_table; 51 | } ParseScope; 52 | 53 | typedef struct FunctionScope 54 | { 55 | LinkedNode _linked; 56 | 57 | LinkedList scopes; 58 | size_t arguments_nr; 59 | Hash *capture_list; 60 | IRBuilder *builder; 61 | BasicBlock *current_bb; 62 | } FunctionScope; 63 | 64 | int _lex_peak(ParseState *state); 65 | int _lex_next(ParseState *state); 66 | 67 | #define function_stack_top(state) (list_get(list_head(&state->function_stack), FunctionScope, _linked)) 68 | #define scope_stack_top(state) (list_get(list_head(&function_stack_top(state)->scopes), ParseScope, _linked)) 69 | 70 | static inline size_t 71 | _parse_allocate_reg_for_func(FunctionScope *func_scope) 72 | { return ir_builder_allocate_register(func_scope->builder); } 73 | 74 | static inline Value 75 | _parse_capture_variable(ParseState *state, ParseScope *scope, CString *string) 76 | { 77 | FunctionScope *function = container_of(list_from_node(&scope->_linked), FunctionScope, scopes); 78 | intptr_t reg = 0; 79 | Value result; 80 | if (!value_is_undefined(result = hash_find(scope->symbol_table, (uintptr_t)string))) { 81 | reg = value_to_int(result); 82 | } 83 | else { 84 | reg = value_to_int(hash_find(scope->upper_table, (uintptr_t)string)); 85 | } 86 | 87 | if (reg < 0) { return value_from_int(reg); } 88 | 89 | function = list_get(list_prev(&function->_linked), FunctionScope, _linked); 90 | while (function) { 91 | size_t new_reg = _parse_allocate_reg_for_func(function); 92 | hash_set_and_update(function->capture_list, reg, value_from_int(new_reg)); 93 | 94 | ParseScope *base_scope = list_get(list_tail(&function->scopes), ParseScope, _linked); 95 | hash_set_and_update( 96 | base_scope->symbol_table, 97 | (uintptr_t)string, 98 | value_from_int(new_reg) 99 | ); 100 | 101 | ParseScope *top_scope = list_get(list_head(&function->scopes), ParseScope, _linked); 102 | if (top_scope != base_scope) { 103 | hash_set_and_update( 104 | top_scope->upper_table, 105 | (uintptr_t)string, 106 | value_from_int(new_reg) 107 | ); 108 | } 109 | 110 | reg = new_reg; 111 | function = list_get(list_prev(&function->_linked), FunctionScope, _linked); 112 | } 113 | 114 | return value_from_int(reg); 115 | } 116 | 117 | static inline Value 118 | _parse_symbol_table_lookup(ParseState *state, CString *string) 119 | { 120 | Value result; 121 | list_for_each(&state->function_stack, function_node) { 122 | FunctionScope *function = list_get(function_node, FunctionScope, _linked); 123 | list_for_each(&function->scopes, node) { 124 | ParseScope *scope = list_get(node, ParseScope, _linked); 125 | if (!value_is_undefined(result = hash_find(scope->symbol_table, (uintptr_t)string))) { 126 | if (function != function_stack_top(state)) { 127 | return _parse_capture_variable(state, scope, string); 128 | } 129 | else { 130 | return result; 131 | } 132 | } 133 | if (!value_is_undefined(result = hash_find(scope->upper_table, (uintptr_t)string))) { 134 | if (function != function_stack_top(state)) { 135 | return _parse_capture_variable(state, scope, string); 136 | } 137 | else { 138 | return result; 139 | } 140 | } 141 | } 142 | } 143 | return value_undefined(); 144 | } 145 | 146 | static inline ParseScope * 147 | _parse_find_define_scope(ParseState *state, CString *string) 148 | { 149 | FunctionScope *function = function_stack_top(state); 150 | list_for_each(&function->scopes, node) { 151 | ParseScope *scope = list_get(node, ParseScope, _linked); 152 | if (!value_is_undefined(hash_find(scope->symbol_table, (uintptr_t)string))) return scope; 153 | } 154 | return NULL; 155 | } 156 | 157 | #define _parse_exists_in_symbol_table(state, string) \ 158 | !value_is_undefined(_parse_symbol_table_lookup(state, (CString*)string)) 159 | 160 | #define _parse_find_in_symbol_table(state, string) \ 161 | value_to_int(_parse_symbol_table_lookup(state, (CString*)string)) 162 | 163 | #define _parse_symbol_table_lookup_current(state, string) \ 164 | hash_find(scope_stack_top(state)->symbol_table, (uintptr_t)string) 165 | 166 | #define _parse_exists_in_symbol_table_current(state, string) \ 167 | !value_is_undefined(_parse_symbol_table_lookup_current(state, (CString*)string)) 168 | 169 | #endif //CRIPT_PARSE_INTERNAL_H 170 | -------------------------------------------------------------------------------- /src/string_pool.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/16/16. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "list.h" 10 | #include "string_pool.h" 11 | #include "hash_helper.h" 12 | 13 | #define STRING_PAGE_SIZE 4096 14 | #define STRING_POOL_DEFAULT_CAPACITY 16 15 | 16 | typedef struct StringPage 17 | { 18 | LinkedNode _linked; 19 | size_t allocated; 20 | char content[0]; 21 | } StringPage; 22 | 23 | typedef struct StringNode 24 | { 25 | uintptr_t key; 26 | CString *string; 27 | } StringNode; 28 | 29 | struct StringPool 30 | { 31 | LinkedList string_pages; 32 | size_t capacity; 33 | size_t size; 34 | StringNode content[0]; 35 | }; 36 | 37 | static inline uintptr_t 38 | string_hash_vec(const char *vec, size_t size) 39 | { 40 | uintptr_t ret = size; 41 | 42 | while (size > sizeof(size_t)) { 43 | ret = (ret << 7) + ret; 44 | ret ^= *(size_t*)(vec); 45 | 46 | vec += sizeof(size_t); 47 | size -= sizeof(size_t); 48 | } 49 | 50 | while (size--) { 51 | ret = (ret << 3) + ret; 52 | ret ^= *vec++; 53 | } 54 | 55 | return ret; 56 | } 57 | 58 | static inline uintptr_t 59 | _string_next_index(uintptr_t key, size_t capacity) 60 | { return _next_index(key, capacity); } 61 | 62 | static inline size_t 63 | _string_expand_size(size_t original_capacity) 64 | { return _expand_size((original_capacity)); } 65 | 66 | static inline int 67 | _string_hash_need_expand(size_t size, size_t capacity) 68 | { return size > (capacity - (capacity >> 1)); } 69 | 70 | static inline void 71 | _string_hash_insert(StringPool *pool, uintptr_t key, CString *string) 72 | { 73 | uintptr_t index = key % pool->capacity; 74 | StringNode *node = NULL; 75 | 76 | while ((node = pool->content + index)->string) { 77 | index = _string_next_index(index, pool->capacity); 78 | } 79 | 80 | node->key = key; 81 | node->string = string; 82 | } 83 | 84 | static inline StringPage * 85 | _new_string_page() 86 | { 87 | StringPage *ret = (StringPage*)malloc(STRING_PAGE_SIZE); 88 | list_node_init(&ret->_linked); 89 | ret->allocated = sizeof(StringPage); 90 | return ret; 91 | } 92 | 93 | #define _string_pool_hash_size(capacity) (sizeof(StringNode) * (capacity)) 94 | #define _string_pool_size(capacity) (sizeof(StringPool) + _string_pool_hash_size(capacity)) 95 | 96 | static inline StringPool * 97 | _string_pool_construct(size_t capacity) 98 | { 99 | StringPool *ret = (StringPool*)malloc(_string_pool_size(capacity)); 100 | 101 | list_init(&ret->string_pages); 102 | ret->capacity = capacity; 103 | ret->size = 0; 104 | memset(ret->content, 0, _string_pool_hash_size(capacity)); 105 | 106 | StringPage *first_page = _new_string_page(); 107 | list_append(&ret->string_pages, &first_page->_linked); 108 | 109 | return ret; 110 | } 111 | 112 | StringPool * 113 | string_pool_new() 114 | { return _string_pool_construct(STRING_POOL_DEFAULT_CAPACITY); } 115 | 116 | void 117 | string_pool_destroy(StringPool *pool) 118 | { 119 | while (list_size(&pool->string_pages)) { 120 | free(list_get(list_unlink(list_head(&pool->string_pages)), StringPage, _linked)); 121 | } 122 | free(pool); 123 | } 124 | 125 | StringPool * 126 | _string_expand_pool(StringPool *original) 127 | { 128 | StringPool *ret = _string_pool_construct(_string_expand_size(original->capacity)); 129 | 130 | list_move(&ret->string_pages, &original->string_pages); 131 | 132 | StringNode *node = original->content, 133 | *limit = original->content + original->capacity; 134 | for (; node != limit; ++node) { 135 | if (node->string) { 136 | _string_hash_insert(ret, node->key, node->string); 137 | } 138 | } 139 | 140 | free(original); 141 | return ret; 142 | } 143 | 144 | CString * 145 | _string_pool_find_hash(const StringPool *pool, const char *vec, size_t size, uintptr_t hash) 146 | { 147 | const StringNode *node = NULL; 148 | uintptr_t index = hash % pool->capacity; 149 | while ((node = pool->content + index)->string) { 150 | if (node->key == hash && size == node->string->length && !memcmp(vec, node->string->content, size)) { 151 | return node->string; 152 | } 153 | index = _string_next_index(index, pool->capacity); 154 | } 155 | return NULL; 156 | } 157 | 158 | CString * 159 | string_pool_find_vec(const StringPool *pool, const char *vec, size_t size) 160 | { 161 | uintptr_t hash = string_hash_vec(vec, size); 162 | return _string_pool_find_hash(pool, vec, size, hash); 163 | } 164 | 165 | static inline CString * 166 | _string_allocate_in_pool(StringPool *pool, size_t size) 167 | { 168 | StringPage *page = list_get(list_head(&pool->string_pages), StringPage, _linked); 169 | if (STRING_PAGE_SIZE - page->allocated < size + sizeof(CString)) { 170 | page = _new_string_page(); 171 | list_prepend(&pool->string_pages, &page->_linked); 172 | } 173 | CString *ret = (CString*)((char *)page + page->allocated); 174 | page->allocated += (size + sizeof(CString) + sizeof(size_t)) & -sizeof(size_t); // for alignment 175 | return ret; 176 | } 177 | 178 | CString * 179 | string_pool_insert_vec(StringPool **pool_ptr, const char *vec, size_t size) 180 | { 181 | assert(size < MAX_SHORT_STRING_LENGTH); 182 | 183 | StringPool *pool = *pool_ptr; 184 | uintptr_t hash = string_hash_vec(vec, size); 185 | 186 | CString *find_result = _string_pool_find_hash(pool, vec, size, hash); 187 | if (find_result) { return find_result; } 188 | 189 | if (_string_hash_need_expand(pool->size + 1, pool->capacity)) { 190 | pool = *pool_ptr = _string_expand_pool(pool); 191 | } 192 | 193 | CString *string = _string_allocate_in_pool(pool, size + 1); 194 | string->length = size; 195 | memcpy(string->content, vec, size); 196 | string->content[string->length] = 0; // tailing zero 197 | 198 | _string_hash_insert(pool, hash, string); 199 | 200 | pool->size++; 201 | return string; 202 | } 203 | 204 | void 205 | string_pool_dump(FILE *fout, StringPool *pool) 206 | { 207 | StringNode *node = pool->content, 208 | *limit = pool->content + pool->capacity; 209 | 210 | for (; node != limit; ++node) { 211 | if (node->string) { 212 | fprintf(fout, "0x%x(%d): %.*s\n", 213 | (uint)node->string, 214 | (uint)node->string, 215 | node->string->length, 216 | node->string->content 217 | ); 218 | } 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /src/string_pool.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/16/16. 3 | // 4 | 5 | #ifndef CRIPT_CSTRING_H 6 | #define CRIPT_CSTRING_H 7 | 8 | #include 9 | #include 10 | 11 | #include "hash.h" 12 | 13 | typedef struct CString 14 | { 15 | size_t length; 16 | char content[0]; 17 | } CString; 18 | 19 | #define MAX_SHORT_STRING_LENGTH (256) 20 | 21 | typedef struct StringPool StringPool; 22 | 23 | StringPool *string_pool_new(); 24 | void string_pool_destroy(StringPool *pool); 25 | 26 | CString *string_pool_find_vec(const StringPool *pool, const char *vec, size_t size); 27 | 28 | static inline CString * 29 | string_pool_find_str(const StringPool *pool, const char *str) 30 | { return string_pool_find_vec(pool, str, strlen(str)); } 31 | 32 | CString *string_pool_insert_vec(StringPool **pool_ptr, const char *vec, size_t size); 33 | 34 | static inline CString * 35 | string_pool_insert_str(StringPool **pool_ptr, const char *str) 36 | { return string_pool_insert_vec(pool_ptr, str, strlen(str)); } 37 | 38 | void string_pool_dump(FILE *fout, StringPool *pool); 39 | 40 | #endif //CRIPT_CSTRING_H 41 | -------------------------------------------------------------------------------- /src/value.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/4/16. 3 | // 4 | 5 | #include "value.h" 6 | -------------------------------------------------------------------------------- /src/value.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/4/16. 3 | // 4 | 5 | #ifndef CRIPT_VALUE_H 6 | #define CRIPT_VALUE_H 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | typedef struct CString CString; 13 | 14 | typedef union Value 15 | { 16 | intptr_t _int; 17 | CString *_str; 18 | void *_ptr; 19 | } Value; 20 | 21 | enum ValueTag 22 | { 23 | T_POINTER = 0, 24 | T_NUMBER = 1, 25 | T_STRING = 2, 26 | T_UNDEFINED = 3 27 | }; 28 | 29 | static inline int 30 | value_is_int(Value val) 31 | { return (val._int & 3) == T_NUMBER; } 32 | 33 | static inline intptr_t 34 | value_to_int(Value val) 35 | { 36 | assert(value_is_int(val)); 37 | return val._int >> 2; 38 | } 39 | 40 | static inline Value 41 | value_from_int(intptr_t value) 42 | { Value ret; ret._int = (value << 2) + T_NUMBER; return ret; } 43 | 44 | static inline int 45 | value_is_string(Value val) 46 | { return (val._int & 3) == T_STRING; } 47 | 48 | static inline Value 49 | value_from_string(CString *str) 50 | { Value ret; ret._str = (CString*)((intptr_t)str | T_STRING); return ret; } 51 | 52 | static inline CString * 53 | value_to_string(Value val) 54 | { 55 | assert(value_is_string(val)); 56 | return (CString*)((intptr_t)(val._str) & -3); 57 | } 58 | 59 | static inline int 60 | value_is_ptr(Value val) 61 | { return (val._int & 3) == T_POINTER; } 62 | 63 | static inline void * 64 | value_to_ptr(Value val) 65 | { 66 | assert(value_is_ptr(val)); 67 | return (void*)((intptr_t)val._ptr & -3); 68 | } 69 | 70 | static inline Value 71 | value_from_ptr(void *ptr) 72 | { Value ret; ret._ptr = (void*)((intptr_t)ptr + T_POINTER); return ret; } 73 | 74 | static inline int 75 | value_is_null(Value val) 76 | { return (value_is_ptr(val) && !value_to_ptr(val)); } 77 | 78 | static inline Value 79 | value_null() 80 | { return value_from_ptr(NULL); } 81 | 82 | static inline int 83 | value_is_undefined(Value value) 84 | { return (value._int & T_UNDEFINED) == T_UNDEFINED; } 85 | 86 | static inline Value 87 | value_undefined() 88 | { Value ret; ret._int = T_UNDEFINED; return ret; } 89 | 90 | #endif //CRIPT_VALUE_H 91 | -------------------------------------------------------------------------------- /src/young_gen.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/18/16. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "young_gen.h" 10 | #include "hash_internal.h" 11 | #include "error.h" 12 | 13 | struct YoungGenBlock 14 | { 15 | size_t allocated; 16 | size_t object_count; 17 | unsigned char bitmap[(YOUNG_GEN_BLOCK_SIZE / sizeof(void*) + 7) / 8]; 18 | unsigned char content[0]; 19 | }; 20 | 21 | static inline void 22 | _young_gen_set_bitmap(YoungGenBlock *block, size_t offset) 23 | { 24 | size_t index = (offset / sizeof(void*) / 8), 25 | bit = (offset / sizeof(void*)) & 7; 26 | block->bitmap[index] |= 1 << bit; 27 | } 28 | 29 | static inline void 30 | _young_gen_unset_bitmap(YoungGenBlock *block, size_t offset) 31 | { 32 | size_t index = (offset / sizeof(void*) / 8), 33 | bit = offset & 7; 34 | block->bitmap[index] &= ~(1 << bit); 35 | } 36 | 37 | static inline Hash * 38 | _young_gen_get_last_hash_from_bitmap(YoungGenBlock *block, size_t index, unsigned char *bitmap) 39 | { 40 | if (!*bitmap) { return NULL; } 41 | 42 | size_t last_bit = __builtin_ctz(*bitmap); 43 | *bitmap &= ~(1 << last_bit); 44 | 45 | return (Hash*)((size_t)block + (index * 8 + last_bit) * sizeof(void*)); 46 | } 47 | 48 | static inline YoungGenBlock * 49 | _young_gen_block_new() 50 | { 51 | YoungGenBlock *ret; 52 | if (posix_memalign((void**)&ret, YOUNG_GEN_BLOCK_SIZE, YOUNG_GEN_BLOCK_SIZE)) { 53 | return NULL; 54 | } 55 | ret->allocated = sizeof(YoungGenBlock); 56 | ret->object_count = 0; 57 | memset(ret->bitmap, 0, sizeof(ret->bitmap)); 58 | return ret; 59 | } 60 | 61 | static inline void 62 | _young_gen_block_destroy(YoungGenBlock *block) 63 | { free(block); } 64 | 65 | YoungGen * 66 | young_gen_new() 67 | { 68 | YoungGen *ret = (YoungGen*)malloc(sizeof(YoungGen)); 69 | 70 | ret->current = _young_gen_block_new(); 71 | ret->replacement = _young_gen_block_new(); 72 | 73 | return ret; 74 | } 75 | 76 | void 77 | young_gen_destroy(YoungGen *young_gen) 78 | { 79 | _young_gen_block_destroy(young_gen->current); 80 | _young_gen_block_destroy(young_gen->replacement); 81 | free(young_gen); 82 | } 83 | 84 | static inline void * 85 | _young_gen_allocate(YoungGenBlock *block, size_t size) 86 | { 87 | if (block->allocated + size > YOUNG_GEN_BLOCK_SIZE) { 88 | return NULL; 89 | } 90 | void *ret = block->content + block->allocated; 91 | block->allocated += size; 92 | block->object_count ++; 93 | return ret; 94 | } 95 | 96 | Hash * 97 | young_gen_new_hash(YoungGen *young_gen, size_t capacity, int type) 98 | { 99 | capacity = hash_normalize_capacity(capacity); 100 | size_t hash_total_size = _hash_total_size(capacity); 101 | return hash_init( 102 | _young_gen_allocate(young_gen->current, hash_total_size), 103 | capacity, type 104 | ); 105 | } 106 | 107 | Hash * 108 | young_gen_new_userdata(YoungGen *young_gen, void *data, userdata_destructor destructor) 109 | { 110 | size_t total_size = _hash_total_size(0); 111 | Hash *ret = _young_gen_allocate(young_gen->current, total_size); 112 | if (!ret) { return NULL; } 113 | ret->type = HT_USERDATA; 114 | ret->capacity = 0; 115 | ret->size = 0; 116 | ret->hi_u_data = data; 117 | ret->hi_u_dtor = destructor; 118 | _young_gen_set_bitmap(young_gen->current, (size_t)ret - (size_t)young_gen->current); 119 | return ret; 120 | } 121 | 122 | void 123 | young_gen_gc_start(YoungGen *young_gen) 124 | { 125 | info_f("start young gen gc, current heap size %d, object count %d", 126 | young_gen_heap_size(young_gen), 127 | young_gen_object_nr(young_gen) 128 | ); 129 | young_gen->gc_start_time = clock(); 130 | } 131 | 132 | void 133 | young_gen_gc_mark(YoungGen *young_gen, Hash **target) 134 | { 135 | Hash *hash = *target; 136 | if (hash->type == HT_GC_LEFT) { 137 | *target = (Hash*)hash->size; 138 | return; 139 | } 140 | 141 | if ((YoungGenBlock*)((uintptr_t)hash & -YOUNG_GEN_BLOCK_SIZE) != young_gen->current) { 142 | return; 143 | } 144 | 145 | if (hash_need_shrink(hash)) { 146 | size_t new_size = _hash_total_size(_hash_shrink_size(hash)); 147 | *target = _young_gen_allocate(young_gen->replacement, new_size); 148 | hash_init(*target, _hash_shrink_size(hash), hash->type); 149 | hash_rehash(*target, hash); 150 | (*target)->_info = hash->_info; 151 | } 152 | else { 153 | size_t total_size = _hash_total_size(hash->capacity); 154 | memcpy( 155 | (*target = _young_gen_allocate(young_gen->replacement, total_size)), 156 | hash, 157 | total_size 158 | ); 159 | 160 | if (hash->type == HT_USERDATA) { 161 | _young_gen_unset_bitmap(young_gen->current, (size_t)hash - (size_t)young_gen->current); 162 | _young_gen_set_bitmap(young_gen->replacement, (size_t)*target - (size_t)young_gen->replacement); 163 | } 164 | } 165 | 166 | hash->type = HT_GC_LEFT; 167 | hash->capacity = 0; 168 | hash->size = (size_t)*target; 169 | 170 | hash_for_each(*target, node) { 171 | if (value_is_ptr(node->value)) { 172 | Hash *child_hash = value_to_ptr(node->value); 173 | 174 | while (child_hash->type == HT_REDIRECT) { 175 | child_hash = (Hash*)child_hash->size; 176 | } 177 | 178 | if (child_hash->type != HT_GC_LEFT) { 179 | switch (child_hash->type) { 180 | case HT_OBJECT: 181 | case HT_ARRAY: 182 | case HT_CLOSURE: 183 | case HT_LIGHTFUNC: 184 | case HT_USERDATA: 185 | young_gen_gc_mark(young_gen, (Hash**)(&node->value)); 186 | break; 187 | default: 188 | assert(0); 189 | } 190 | } 191 | else { 192 | node->value = value_from_ptr((Hash*)child_hash->size); 193 | } 194 | } 195 | } 196 | } 197 | 198 | static inline void 199 | _young_gen_gc_destruct_userdata(YoungGenBlock *block) 200 | { 201 | for (size_t i = 0; i < sizeof(block->bitmap) / sizeof(block->bitmap[0]); ++i) { 202 | Hash *hash = NULL; 203 | while ((hash = _young_gen_get_last_hash_from_bitmap(block, i, block->bitmap + i))) { 204 | if (hash->type == HT_USERDATA) { 205 | hash->hi_u_dtor(hash->hi_u_data); 206 | } 207 | } 208 | } 209 | } 210 | 211 | void 212 | young_gen_gc_end(YoungGen *young_gen) 213 | { 214 | _young_gen_gc_destruct_userdata(young_gen->current); 215 | 216 | YoungGenBlock *block = young_gen->current; 217 | young_gen->current = young_gen->replacement; 218 | young_gen->replacement = block; 219 | 220 | young_gen->replacement->allocated = sizeof(YoungGenBlock); 221 | young_gen->replacement->object_count = 0; 222 | memset(young_gen->replacement->bitmap, 0, sizeof(young_gen->replacement->bitmap)); 223 | 224 | info_f("completed young gen gc in %dms, current heap size %d, object count %d", 225 | (clock() - young_gen->gc_start_time) / (CLOCKS_PER_SEC / 1000), 226 | young_gen_heap_size(young_gen), 227 | young_gen_object_nr(young_gen) 228 | ); 229 | } 230 | 231 | size_t 232 | young_gen_heap_size(YoungGen *young_gen) 233 | { return young_gen->current->allocated; } 234 | 235 | size_t 236 | young_gen_object_nr(YoungGen *young_gen) 237 | { return young_gen->current->object_count; } 238 | -------------------------------------------------------------------------------- /src/young_gen.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/18/16. 3 | // 4 | 5 | #ifndef CRIPT_YOUNG_GC_H 6 | #define CRIPT_YOUNG_GC_H 7 | 8 | #include 9 | #include 10 | 11 | #include "hash.h" 12 | #include "cvm.h" 13 | 14 | #define YOUNG_GEN_BLOCK_SIZE (32 * 1024 * 1024) 15 | 16 | typedef struct YoungGenBlock YoungGenBlock; 17 | 18 | typedef struct YoungGen 19 | { 20 | YoungGenBlock *current; 21 | YoungGenBlock *replacement; 22 | clock_t gc_start_time; 23 | } YoungGen; 24 | 25 | YoungGen *young_gen_new(); 26 | void young_gen_destroy(YoungGen *young_gen); 27 | 28 | Hash *young_gen_new_hash(YoungGen *young_gen, size_t capacity, int type); 29 | Hash *young_gen_new_userdata(YoungGen *young_gen, void *data, userdata_destructor destructor); 30 | 31 | void young_gen_gc_start(YoungGen *young_gen); 32 | void young_gen_gc_mark(YoungGen *young_gen, Hash **target); 33 | void young_gen_gc_end(YoungGen *young_gen); 34 | 35 | size_t young_gen_heap_size(YoungGen *young_gen); 36 | size_t young_gen_object_nr(YoungGen *young_gen); 37 | 38 | #endif // CRIPT_YOUNG_GC_H 39 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | enable_testing() 2 | 3 | include_directories(${cript_SOURCE_DIR}/src) 4 | include_directories(${cript_SOURCE_DIR}/test/cutest-1.5) 5 | 6 | add_executable(test_main cutest-1.5/CuTest.c test_main.c cvm_test.c hash_test.c cstring_test.c parser_test.c young_gen_test.c code_test.c ir_builder_test.c) 7 | target_link_libraries(test_main libcript) 8 | 9 | add_test(test_main test_main) 10 | -------------------------------------------------------------------------------- /test/code_test.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/19/16. 3 | // 4 | 5 | #include "CuTest.h" 6 | #include "parse.h" 7 | #include "cvm.h" 8 | #include "math.h" 9 | 10 | #include "inst_output.h" 11 | 12 | static inline VMState * 13 | _vm_from_code_snippet(const char *code) 14 | { 15 | ParseState *state = parse_state_new_from_string(code); 16 | parse(state); 17 | 18 | VMState *vm = cvm_state_new_from_parse_state(state); 19 | 20 | parse_state_destroy(state); 21 | 22 | return vm; 23 | } 24 | 25 | static Value _print(VMState *vm, Value value) 26 | { 27 | Hash *args = value_to_ptr(value); 28 | 29 | for (uintptr_t i = 1; i < hash_size(args); ++i) { 30 | Value val = hash_find(args, i); 31 | 32 | if (value_is_int(val)) { 33 | printf("%d ", value_to_int(val)); 34 | } 35 | else { 36 | CString *string = value_to_string(val); 37 | printf("%.*s ", string->length, string->content); 38 | } 39 | } 40 | 41 | printf("\n"); 42 | return value_undefined(); 43 | } 44 | 45 | static Value _random(VMState *vm, Value value) 46 | { return value_from_int(random()); } 47 | 48 | static Value _assert(VMState *vm, Value value) 49 | { 50 | Hash *args = value_to_ptr(value); 51 | if (!value_to_int(hash_find(args, 1))) { 52 | printf("C Assert Failed!\n"); 53 | __builtin_trap(); 54 | } 55 | return value_undefined(); 56 | } 57 | 58 | void 59 | code_light_function_test(CuTest *tc) 60 | { 61 | static const char TEST_CONTENT[] = 62 | "let print_result = global.print('light function tested', 1, 2, 3);" 63 | ; 64 | 65 | VMState *vm = _vm_from_code_snippet(TEST_CONTENT); 66 | 67 | Value print = cvm_create_light_function(vm, _print); 68 | cvm_register_in_global(vm, print, "print"); 69 | 70 | cvm_state_run(vm); 71 | (void)tc; 72 | } 73 | 74 | void 75 | code_left_hand_function_call_test(CuTest *tc) 76 | { 77 | static const char TEST_CONTENT[] = 78 | "global.print('left hand function call tested', 2, 3, 4);" 79 | ; 80 | 81 | VMState *vm = _vm_from_code_snippet(TEST_CONTENT); 82 | 83 | Value print = cvm_create_light_function(vm, _print); 84 | cvm_register_in_global(vm, print, "print"); 85 | 86 | cvm_state_run(vm); 87 | (void)tc; 88 | } 89 | 90 | void 91 | code_pass_function_around_test(CuTest *tc) 92 | { 93 | static const char TEST_CONTENT[] = 94 | "let print_func = global.print;\n" 95 | "print_func('pass function around tested', 3, 4, 5);\n" 96 | ; 97 | 98 | VMState *vm = _vm_from_code_snippet(TEST_CONTENT); 99 | 100 | Value print = cvm_create_light_function(vm, _print); 101 | cvm_register_in_global(vm, print, "print"); 102 | 103 | cvm_state_run(vm); 104 | (void)tc; 105 | } 106 | 107 | void 108 | code_sort_test(CuTest *tc) 109 | { 110 | static const char TEST_CONTENT[] = 111 | "let sort = function (array, len) {\n" 112 | " let i = 0,\n" 113 | " j = 0;\n" 114 | " while (i < len) {\n" 115 | " j = i + 1;\n" 116 | " while (j < len) {\n" 117 | " if (array[i] > array[j]) {\n" 118 | " let t = array[i];\n" 119 | " array[i] = array[j];\n" 120 | " array[j] = t;\n" 121 | " }\n" 122 | " j = j + 1;\n" 123 | " }\n" 124 | " i = i + 1;\n" 125 | " }\n" 126 | "};\n" 127 | "\n" 128 | "let i = 0;\n" 129 | "let array = [];\n" 130 | "while (i < 10) {\n" 131 | " array[i] = global.random() % 10 + 10;\n" 132 | " i = i + 1;\n" 133 | "}\n" 134 | "sort(array, 10);\n" 135 | "i = 0;\n" 136 | "while (i < 9) {\n" 137 | " global.assert(array[i] <= array[i+1]);\n" 138 | " i = i + 1;\n" 139 | "}\n" 140 | ; 141 | 142 | VMState *vm = _vm_from_code_snippet(TEST_CONTENT); 143 | 144 | cvm_register_in_global(vm, cvm_create_light_function(vm, _print), "print"); 145 | cvm_register_in_global(vm, cvm_create_light_function(vm, _random), "random"); 146 | cvm_register_in_global(vm, cvm_create_light_function(vm, _assert), "assert"); 147 | 148 | cvm_state_run(vm); 149 | (void)tc; 150 | } 151 | 152 | void 153 | code_closure_test(CuTest *tc) 154 | { 155 | static const char TEST_CONTENT[] = 156 | "let create_closure = function (num) {\n" 157 | " let ret = [];\n" 158 | " let i = 0;\n" 159 | " while (i < num) {\n" 160 | " ret[i] = function () {\n" 161 | " return i;\n" 162 | " };\n" 163 | " i = i + 1;\n" 164 | " }\n" 165 | " return ret;\n" 166 | "};\n" 167 | "let closures = create_closure(10);\n" 168 | "let i = 0;\n" 169 | "while (i < 10) {\n" 170 | " global.assert(closures[i]() == i);\n" 171 | " i = i + 1;\n" 172 | "}\n" 173 | ; 174 | 175 | VMState *vm = _vm_from_code_snippet(TEST_CONTENT); 176 | 177 | cvm_register_in_global(vm, cvm_create_light_function(vm, _print), "print"); 178 | cvm_register_in_global(vm, cvm_create_light_function(vm, _random), "random"); 179 | cvm_register_in_global(vm, cvm_create_light_function(vm, _assert), "assert"); 180 | 181 | cvm_state_run(vm); 182 | (void)tc; 183 | } 184 | 185 | typedef struct LongString 186 | { 187 | size_t length; 188 | char content[0]; 189 | } LongString; 190 | 191 | static void 192 | _long_string_destructor(void *ptr) 193 | { 194 | LongString *long_str = ptr; 195 | printf("long string destructed: %.*ss\n", long_str->length, long_str->content); 196 | free(ptr); 197 | } 198 | 199 | static Value _new_long_string(VMState *vm, Value value) 200 | { 201 | Hash *args = value_to_ptr(value); 202 | CString *string = value_to_string(hash_find(args, 1)); 203 | LongString *long_string = malloc(sizeof(LongString) + string->length + 1); 204 | long_string->length = string->length; 205 | strncpy(long_string->content, string->content, string->length); 206 | 207 | return cvm_create_userdata(vm, long_string, _long_string_destructor); 208 | } 209 | 210 | void 211 | code_userdata_test(CuTest *tc) 212 | { 213 | static const char TEST_CONTENT[] = 214 | "let test_func = function () {\n" 215 | " let t = global.new_long_string('test_long_string');\n" 216 | "};\n" 217 | "test_func();\n" 218 | "let i = 0;\n" 219 | "while (i < 300000) {\n" 220 | " let a = {};\n" 221 | " i = i + 1;\n" 222 | "}\n" 223 | ; 224 | 225 | VMState *vm = _vm_from_code_snippet(TEST_CONTENT); 226 | 227 | cvm_register_in_global(vm, cvm_create_light_function(vm, _new_long_string), "new_long_string"); 228 | 229 | cvm_state_run(vm); 230 | (void)tc; 231 | } 232 | 233 | static Value _for_each(VMState *vm, Value args_val) 234 | { 235 | Hash *args = value_to_ptr(args_val); 236 | Hash *array = value_to_ptr(hash_find(args, 1)); 237 | Value func = hash_find(args, 2); 238 | 239 | for (size_t i = 0; i < hash_size(array); ++i) { 240 | Hash *arg_for_child = cvm_create_array(vm, HASH_MIN_CAPACITY); 241 | 242 | arg_for_child = cvm_set_hash(vm, arg_for_child, 0, value_from_ptr(cvm_get_global(vm))); 243 | arg_for_child = cvm_set_hash(vm, arg_for_child, 1, hash_find(array, i)); 244 | 245 | cvm_state_call_function(vm, func, value_from_ptr(arg_for_child)); 246 | } 247 | 248 | return value_undefined(); 249 | } 250 | 251 | void 252 | code_foreach_test(CuTest *tc) 253 | { 254 | static const char TEST_CONTENT[] = 255 | "let array = [];\n" 256 | "let i = 0;\n" 257 | "while (i < 10) {\n" 258 | " array[i] = i;\n" 259 | " i = i + 1;\n" 260 | "}\n" 261 | "global.for_each(array, function (x) {\n" 262 | " global.print(x);\n" 263 | "});\n" 264 | ; 265 | 266 | VMState *vm = _vm_from_code_snippet(TEST_CONTENT); 267 | 268 | cvm_register_in_global(vm, cvm_create_light_function(vm, _for_each), "for_each"); 269 | cvm_register_in_global(vm, cvm_create_light_function(vm, _print), "print"); 270 | 271 | cvm_state_run(vm); 272 | (void)tc; 273 | } 274 | 275 | CuSuite * 276 | code_test_suite(void) 277 | { 278 | CuSuite *suite = CuSuiteNew(); 279 | 280 | SUITE_ADD_TEST(suite, code_light_function_test); 281 | SUITE_ADD_TEST(suite, code_left_hand_function_call_test); 282 | SUITE_ADD_TEST(suite, code_pass_function_around_test); 283 | SUITE_ADD_TEST(suite, code_sort_test); 284 | SUITE_ADD_TEST(suite, code_closure_test); 285 | SUITE_ADD_TEST(suite, code_userdata_test); 286 | SUITE_ADD_TEST(suite, code_foreach_test); 287 | 288 | return suite; 289 | } 290 | -------------------------------------------------------------------------------- /test/cstring_test.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/16/16. 3 | // 4 | 5 | #include "CuTest.h" 6 | #include "string_pool.h" 7 | 8 | void 9 | cstring_test_construct(CuTest *tc) 10 | { 11 | StringPool *uut = string_pool_new(); 12 | 13 | string_pool_destroy(uut); 14 | 15 | (void)tc; 16 | } 17 | 18 | void 19 | cstring_test_insert(CuTest *tc) 20 | { 21 | static const char TEST_STR[] = "Hello World"; 22 | static const char TEST_STR2[] = "Hello Cript"; 23 | 24 | StringPool *uut = string_pool_new(); 25 | 26 | CString *inserted = string_pool_insert_str(&uut, TEST_STR); 27 | CuAssertIntEquals(tc, 0, strncmp(TEST_STR, inserted->content, inserted->length)); 28 | 29 | inserted = string_pool_insert_vec(&uut, TEST_STR2, strlen(TEST_STR2)); 30 | CuAssertIntEquals(tc, 0, strncmp(TEST_STR2, inserted->content, inserted->length)); 31 | 32 | string_pool_destroy(uut); 33 | } 34 | 35 | void 36 | cstring_test_find(CuTest *tc) 37 | { 38 | static char TEST_STR[] = "Hello World"; 39 | static char TEST_STR2[] = "Hello Cript"; 40 | 41 | StringPool *uut = string_pool_new(); 42 | 43 | string_pool_insert_str(&uut, TEST_STR); 44 | string_pool_insert_str(&uut, TEST_STR2); 45 | 46 | CString *find_result; 47 | 48 | find_result = string_pool_find_str(uut, TEST_STR); 49 | CuAssertTrue(tc, find_result != NULL); 50 | CuAssertIntEquals(tc, 0, strncmp(TEST_STR, find_result->content, strlen(TEST_STR))); 51 | 52 | find_result = string_pool_find_str(uut, TEST_STR2); 53 | CuAssertTrue(tc, find_result != NULL); 54 | CuAssertIntEquals(tc, 0, strncmp(TEST_STR2, find_result->content, strlen(TEST_STR2))); 55 | 56 | string_pool_destroy(uut); 57 | } 58 | 59 | void 60 | cstring_test_rehash(CuTest *tc) 61 | { 62 | StringPool *uut = string_pool_new(); 63 | 64 | int i; 65 | for (i = 0; i < 16; ++i) { 66 | CString *inserted = string_pool_insert_vec(&uut, (char*)&i, sizeof(int)); 67 | CuAssertIntEquals(tc, i, *(int*)inserted->content); 68 | } 69 | 70 | for (i = 0; i < 16; ++i) { 71 | CString *find_result = string_pool_find_vec(uut, (char*)&i, sizeof(int)); 72 | CuAssertTrue(tc, find_result != NULL); 73 | CuAssertIntEquals(tc, i, *(int*)find_result->content); 74 | } 75 | 76 | string_pool_destroy(uut); 77 | } 78 | 79 | void 80 | cstring_test_new_page(CuTest *tc) 81 | { 82 | static char TEST_STR[255]; 83 | 84 | StringPool *uut = string_pool_new(); 85 | 86 | int i; 87 | for (i = 0; i < 16; ++i) { 88 | *(int*)TEST_STR = i; 89 | CString *inserted = string_pool_insert_vec(&uut, TEST_STR, 255); 90 | CuAssertIntEquals(tc, i, *(int*)inserted->content); 91 | } 92 | 93 | for (i = 0; i < 16; ++i) { 94 | *(int*)TEST_STR = i; 95 | CString *find_result = string_pool_find_vec(uut, TEST_STR, 255); 96 | CuAssertIntEquals(tc, i, *(int*)find_result->content); 97 | } 98 | 99 | string_pool_destroy(uut); 100 | } 101 | 102 | void 103 | cstring_test_same_node(CuTest *tc) 104 | { 105 | StringPool *uut = string_pool_new(); 106 | 107 | CString *results[16]; 108 | 109 | int i; 110 | for (i = 0; i < 16; ++i) { 111 | results[i] = string_pool_insert_vec(&uut, (char*)&i, sizeof(int)); 112 | } 113 | 114 | for (i = 0; i < 16; ++i) { 115 | CuAssertTrue(tc, results[i] == string_pool_insert_vec(&uut, (char*)&i, sizeof(int))); 116 | } 117 | 118 | string_pool_destroy(uut); 119 | } 120 | 121 | void 122 | cstring_test_empty_string(CuTest *tc) 123 | { 124 | StringPool *uut = string_pool_new(); 125 | 126 | CString *results[16]; 127 | 128 | int i; 129 | for (i = 1; i < 16; ++i) { 130 | results[i] = string_pool_insert_vec(&uut, (char*)&i, sizeof(int)); 131 | } 132 | 133 | results[0] = string_pool_insert_vec(&uut, (char*)&i, 0); 134 | 135 | for (i = 1; i < 16; ++i) { 136 | CuAssertTrue(tc, results[i] == string_pool_insert_vec(&uut, (char*)&i, sizeof(int))); 137 | } 138 | 139 | CuAssertTrue(tc, results[0] == string_pool_find_vec(uut, NULL, 0)); 140 | 141 | string_pool_destroy(uut); 142 | } 143 | 144 | CuSuite * 145 | cstring_test_suite(void) 146 | { 147 | CuSuite *suite = CuSuiteNew(); 148 | 149 | SUITE_ADD_TEST(suite, cstring_test_construct); 150 | SUITE_ADD_TEST(suite, cstring_test_insert); 151 | SUITE_ADD_TEST(suite, cstring_test_find); 152 | SUITE_ADD_TEST(suite, cstring_test_rehash); 153 | SUITE_ADD_TEST(suite, cstring_test_new_page); 154 | SUITE_ADD_TEST(suite, cstring_test_same_node); 155 | SUITE_ADD_TEST(suite, cstring_test_empty_string); 156 | 157 | return suite; 158 | } 159 | -------------------------------------------------------------------------------- /test/cutest-1.5/AllTests.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "CuTest.h" 4 | 5 | CuSuite* CuGetSuite(); 6 | CuSuite* CuStringGetSuite(); 7 | 8 | void RunAllTests(void) 9 | { 10 | CuString *output = CuStringNew(); 11 | CuSuite* suite = CuSuiteNew(); 12 | 13 | CuSuiteAddSuite(suite, CuGetSuite()); 14 | CuSuiteAddSuite(suite, CuStringGetSuite()); 15 | 16 | CuSuiteRun(suite); 17 | CuSuiteSummary(suite, output); 18 | CuSuiteDetails(suite, output); 19 | printf("%s\n", output->buffer); 20 | } 21 | 22 | int main(void) 23 | { 24 | RunAllTests(); 25 | } 26 | -------------------------------------------------------------------------------- /test/cutest-1.5/CuTest.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "CuTest.h" 9 | 10 | /*-------------------------------------------------------------------------* 11 | * CuStr 12 | *-------------------------------------------------------------------------*/ 13 | 14 | char* CuStrAlloc(int size) 15 | { 16 | char* newStr = (char*) malloc( sizeof(char) * (size) ); 17 | return newStr; 18 | } 19 | 20 | char* CuStrCopy(const char* old) 21 | { 22 | int len = strlen(old); 23 | char* newStr = CuStrAlloc(len + 1); 24 | strcpy(newStr, old); 25 | return newStr; 26 | } 27 | 28 | /*-------------------------------------------------------------------------* 29 | * CuString 30 | *-------------------------------------------------------------------------*/ 31 | 32 | void CuStringInit(CuString* str) 33 | { 34 | str->length = 0; 35 | str->size = STRING_MAX; 36 | str->buffer = (char*) malloc(sizeof(char) * str->size); 37 | str->buffer[0] = '\0'; 38 | } 39 | 40 | CuString* CuStringNew(void) 41 | { 42 | CuString* str = (CuString*) malloc(sizeof(CuString)); 43 | str->length = 0; 44 | str->size = STRING_MAX; 45 | str->buffer = (char*) malloc(sizeof(char) * str->size); 46 | str->buffer[0] = '\0'; 47 | return str; 48 | } 49 | 50 | void CuStringDelete(CuString *str) 51 | { 52 | if (!str) return; 53 | free(str->buffer); 54 | free(str); 55 | } 56 | 57 | void CuStringResize(CuString* str, int newSize) 58 | { 59 | str->buffer = (char*) realloc(str->buffer, sizeof(char) * newSize); 60 | str->size = newSize; 61 | } 62 | 63 | void CuStringAppend(CuString* str, const char* text) 64 | { 65 | int length; 66 | 67 | if (text == NULL) { 68 | text = "NULL"; 69 | } 70 | 71 | length = strlen(text); 72 | if (str->length + length + 1 >= str->size) 73 | CuStringResize(str, str->length + length + 1 + STRING_INC); 74 | str->length += length; 75 | strcat(str->buffer, text); 76 | } 77 | 78 | void CuStringAppendChar(CuString* str, char ch) 79 | { 80 | char text[2]; 81 | text[0] = ch; 82 | text[1] = '\0'; 83 | CuStringAppend(str, text); 84 | } 85 | 86 | void CuStringAppendFormat(CuString* str, const char* format, ...) 87 | { 88 | va_list argp; 89 | char buf[HUGE_STRING_LEN]; 90 | va_start(argp, format); 91 | vsprintf(buf, format, argp); 92 | va_end(argp); 93 | CuStringAppend(str, buf); 94 | } 95 | 96 | void CuStringInsert(CuString* str, const char* text, int pos) 97 | { 98 | int length = strlen(text); 99 | if (pos > str->length) 100 | pos = str->length; 101 | if (str->length + length + 1 >= str->size) 102 | CuStringResize(str, str->length + length + 1 + STRING_INC); 103 | memmove(str->buffer + pos + length, str->buffer + pos, (str->length - pos) + 1); 104 | str->length += length; 105 | memcpy(str->buffer + pos, text, length); 106 | } 107 | 108 | /*-------------------------------------------------------------------------* 109 | * CuTest 110 | *-------------------------------------------------------------------------*/ 111 | 112 | void CuTestInit(CuTest* t, const char* name, TestFunction function) 113 | { 114 | t->name = CuStrCopy(name); 115 | t->failed = 0; 116 | t->ran = 0; 117 | t->message = NULL; 118 | t->function = function; 119 | t->jumpBuf = NULL; 120 | } 121 | 122 | CuTest* CuTestNew(const char* name, TestFunction function) 123 | { 124 | CuTest* tc = CU_ALLOC(CuTest); 125 | CuTestInit(tc, name, function); 126 | return tc; 127 | } 128 | 129 | void CuTestDelete(CuTest *t) 130 | { 131 | if (!t) return; 132 | free(t->name); 133 | free(t); 134 | } 135 | 136 | void CuTestRun(CuTest* tc) 137 | { 138 | jmp_buf buf; 139 | tc->jumpBuf = &buf; 140 | if (setjmp(buf) == 0) 141 | { 142 | tc->ran = 1; 143 | (tc->function)(tc); 144 | } 145 | tc->jumpBuf = 0; 146 | } 147 | 148 | static void CuFailInternal(CuTest* tc, const char* file, int line, CuString* string) 149 | { 150 | char buf[HUGE_STRING_LEN]; 151 | 152 | sprintf(buf, "%s:%d: ", file, line); 153 | CuStringInsert(string, buf, 0); 154 | 155 | tc->failed = 1; 156 | tc->message = string->buffer; 157 | if (tc->jumpBuf != 0) longjmp(*(tc->jumpBuf), 0); 158 | } 159 | 160 | void CuFail_Line(CuTest* tc, const char* file, int line, const char* message2, const char* message) 161 | { 162 | CuString string; 163 | 164 | CuStringInit(&string); 165 | if (message2 != NULL) 166 | { 167 | CuStringAppend(&string, message2); 168 | CuStringAppend(&string, ": "); 169 | } 170 | CuStringAppend(&string, message); 171 | CuFailInternal(tc, file, line, &string); 172 | } 173 | 174 | void CuAssert_Line(CuTest* tc, const char* file, int line, const char* message, int condition) 175 | { 176 | if (condition) return; 177 | CuFail_Line(tc, file, line, NULL, message); 178 | } 179 | 180 | void CuAssertStrEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, 181 | const char* expected, const char* actual) 182 | { 183 | CuString string; 184 | if ((expected == NULL && actual == NULL) || 185 | (expected != NULL && actual != NULL && 186 | strcmp(expected, actual) == 0)) 187 | { 188 | return; 189 | } 190 | 191 | CuStringInit(&string); 192 | if (message != NULL) 193 | { 194 | CuStringAppend(&string, message); 195 | CuStringAppend(&string, ": "); 196 | } 197 | CuStringAppend(&string, "expected <"); 198 | CuStringAppend(&string, expected); 199 | CuStringAppend(&string, "> but was <"); 200 | CuStringAppend(&string, actual); 201 | CuStringAppend(&string, ">"); 202 | CuFailInternal(tc, file, line, &string); 203 | } 204 | 205 | void CuAssertIntEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, 206 | int expected, int actual) 207 | { 208 | char buf[STRING_MAX]; 209 | if (expected == actual) return; 210 | sprintf(buf, "expected <%d> but was <%d>", expected, actual); 211 | CuFail_Line(tc, file, line, message, buf); 212 | } 213 | 214 | void CuAssertDblEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, 215 | double expected, double actual, double delta) 216 | { 217 | char buf[STRING_MAX]; 218 | if (fabs(expected - actual) <= delta) return; 219 | sprintf(buf, "expected <%f> but was <%f>", expected, actual); 220 | 221 | CuFail_Line(tc, file, line, message, buf); 222 | } 223 | 224 | void CuAssertPtrEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, 225 | void* expected, void* actual) 226 | { 227 | char buf[STRING_MAX]; 228 | if (expected == actual) return; 229 | sprintf(buf, "expected pointer <0x%p> but was <0x%p>", expected, actual); 230 | CuFail_Line(tc, file, line, message, buf); 231 | } 232 | 233 | 234 | /*-------------------------------------------------------------------------* 235 | * CuSuite 236 | *-------------------------------------------------------------------------*/ 237 | 238 | void CuSuiteInit(CuSuite* testSuite) 239 | { 240 | testSuite->count = 0; 241 | testSuite->failCount = 0; 242 | memset(testSuite->list, 0, sizeof(testSuite->list)); 243 | } 244 | 245 | CuSuite* CuSuiteNew(void) 246 | { 247 | CuSuite* testSuite = CU_ALLOC(CuSuite); 248 | CuSuiteInit(testSuite); 249 | return testSuite; 250 | } 251 | 252 | void CuSuiteDelete(CuSuite *testSuite) 253 | { 254 | unsigned int n; 255 | for (n=0; n < MAX_TEST_CASES; n++) 256 | { 257 | if (testSuite->list[n]) 258 | { 259 | CuTestDelete(testSuite->list[n]); 260 | } 261 | } 262 | free(testSuite); 263 | 264 | } 265 | 266 | void CuSuiteAdd(CuSuite* testSuite, CuTest *testCase) 267 | { 268 | assert(testSuite->count < MAX_TEST_CASES); 269 | testSuite->list[testSuite->count] = testCase; 270 | testSuite->count++; 271 | } 272 | 273 | void CuSuiteAddSuite(CuSuite* testSuite, CuSuite* testSuite2) 274 | { 275 | int i; 276 | for (i = 0 ; i < testSuite2->count ; ++i) 277 | { 278 | CuTest* testCase = testSuite2->list[i]; 279 | CuSuiteAdd(testSuite, testCase); 280 | } 281 | } 282 | 283 | void CuSuiteRun(CuSuite* testSuite) 284 | { 285 | int i; 286 | for (i = 0 ; i < testSuite->count ; ++i) 287 | { 288 | CuTest* testCase = testSuite->list[i]; 289 | CuTestRun(testCase); 290 | if (testCase->failed) { testSuite->failCount += 1; } 291 | } 292 | } 293 | 294 | void CuSuiteSummary(CuSuite* testSuite, CuString* summary) 295 | { 296 | int i; 297 | for (i = 0 ; i < testSuite->count ; ++i) 298 | { 299 | CuTest* testCase = testSuite->list[i]; 300 | CuStringAppend(summary, testCase->failed ? "F" : "."); 301 | } 302 | CuStringAppend(summary, "\n\n"); 303 | } 304 | 305 | void CuSuiteDetails(CuSuite* testSuite, CuString* details) 306 | { 307 | int i; 308 | int failCount = 0; 309 | 310 | if (testSuite->failCount == 0) 311 | { 312 | int passCount = testSuite->count - testSuite->failCount; 313 | const char* testWord = passCount == 1 ? "test" : "tests"; 314 | CuStringAppendFormat(details, "OK (%d %s)\n", passCount, testWord); 315 | } 316 | else 317 | { 318 | if (testSuite->failCount == 1) 319 | CuStringAppend(details, "There was 1 failure:\n"); 320 | else 321 | CuStringAppendFormat(details, "There were %d failures:\n", testSuite->failCount); 322 | 323 | for (i = 0 ; i < testSuite->count ; ++i) 324 | { 325 | CuTest* testCase = testSuite->list[i]; 326 | if (testCase->failed) 327 | { 328 | failCount++; 329 | CuStringAppendFormat(details, "%d) %s: %s\n", 330 | failCount, testCase->name, testCase->message); 331 | } 332 | } 333 | CuStringAppend(details, "\n!!!FAILURES!!!\n"); 334 | 335 | CuStringAppendFormat(details, "Runs: %d ", testSuite->count); 336 | CuStringAppendFormat(details, "Passes: %d ", testSuite->count - testSuite->failCount); 337 | CuStringAppendFormat(details, "Fails: %d\n", testSuite->failCount); 338 | } 339 | } 340 | -------------------------------------------------------------------------------- /test/cutest-1.5/CuTest.h: -------------------------------------------------------------------------------- 1 | #ifndef CU_TEST_H 2 | #define CU_TEST_H 3 | 4 | #include 5 | #include 6 | 7 | #define CUTEST_VERSION "CuTest 1.5" 8 | 9 | /* CuString */ 10 | 11 | char* CuStrAlloc(int size); 12 | char* CuStrCopy(const char* old); 13 | 14 | #define CU_ALLOC(TYPE) ((TYPE*) malloc(sizeof(TYPE))) 15 | 16 | #define HUGE_STRING_LEN 8192 17 | #define STRING_MAX 256 18 | #define STRING_INC 256 19 | 20 | typedef struct 21 | { 22 | int length; 23 | int size; 24 | char* buffer; 25 | } CuString; 26 | 27 | void CuStringInit(CuString* str); 28 | CuString* CuStringNew(void); 29 | void CuStringRead(CuString* str, const char* path); 30 | void CuStringAppend(CuString* str, const char* text); 31 | void CuStringAppendChar(CuString* str, char ch); 32 | void CuStringAppendFormat(CuString* str, const char* format, ...); 33 | void CuStringInsert(CuString* str, const char* text, int pos); 34 | void CuStringResize(CuString* str, int newSize); 35 | void CuStringDelete(CuString* str); 36 | 37 | /* CuTest */ 38 | 39 | typedef struct CuTest CuTest; 40 | 41 | typedef void (*TestFunction)(CuTest *); 42 | 43 | struct CuTest 44 | { 45 | char* name; 46 | TestFunction function; 47 | int failed; 48 | int ran; 49 | const char* message; 50 | jmp_buf *jumpBuf; 51 | }; 52 | 53 | void CuTestInit(CuTest* t, const char* name, TestFunction function); 54 | CuTest* CuTestNew(const char* name, TestFunction function); 55 | void CuTestRun(CuTest* tc); 56 | void CuTestDelete(CuTest *t); 57 | 58 | /* Internal versions of assert functions -- use the public versions */ 59 | void CuFail_Line(CuTest* tc, const char* file, int line, const char* message2, const char* message); 60 | void CuAssert_Line(CuTest* tc, const char* file, int line, const char* message, int condition); 61 | void CuAssertStrEquals_LineMsg(CuTest* tc, 62 | const char* file, int line, const char* message, 63 | const char* expected, const char* actual); 64 | void CuAssertIntEquals_LineMsg(CuTest* tc, 65 | const char* file, int line, const char* message, 66 | int expected, int actual); 67 | void CuAssertDblEquals_LineMsg(CuTest* tc, 68 | const char* file, int line, const char* message, 69 | double expected, double actual, double delta); 70 | void CuAssertPtrEquals_LineMsg(CuTest* tc, 71 | const char* file, int line, const char* message, 72 | void* expected, void* actual); 73 | 74 | /* public assert functions */ 75 | 76 | #define CuFail(tc, ms) CuFail_Line( (tc), __FILE__, __LINE__, NULL, (ms)) 77 | #define CuAssert(tc, ms, cond) CuAssert_Line((tc), __FILE__, __LINE__, (ms), (cond)) 78 | #define CuAssertTrue(tc, cond) CuAssert_Line((tc), __FILE__, __LINE__, "assert failed", (cond)) 79 | 80 | #define CuAssertStrEquals(tc,ex,ac) CuAssertStrEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac)) 81 | #define CuAssertStrEquals_Msg(tc,ms,ex,ac) CuAssertStrEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(ac)) 82 | #define CuAssertIntEquals(tc,ex,ac) CuAssertIntEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac)) 83 | #define CuAssertIntEquals_Msg(tc,ms,ex,ac) CuAssertIntEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(ac)) 84 | #define CuAssertDblEquals(tc,ex,ac,dl) CuAssertDblEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac),(dl)) 85 | #define CuAssertDblEquals_Msg(tc,ms,ex,ac,dl) CuAssertDblEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(ac),(dl)) 86 | #define CuAssertPtrEquals(tc,ex,ac) CuAssertPtrEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac)) 87 | #define CuAssertPtrEquals_Msg(tc,ms,ex,ac) CuAssertPtrEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(ac)) 88 | 89 | #define CuAssertPtrNotNull(tc,p) CuAssert_Line((tc),__FILE__,__LINE__,"null pointer unexpected",(p != NULL)) 90 | #define CuAssertPtrNotNullMsg(tc,msg,p) CuAssert_Line((tc),__FILE__,__LINE__,(msg),(p != NULL)) 91 | 92 | /* CuSuite */ 93 | 94 | #define MAX_TEST_CASES 1024 95 | 96 | #define SUITE_ADD_TEST(SUITE,TEST) CuSuiteAdd(SUITE, CuTestNew(#TEST, TEST)) 97 | 98 | typedef struct 99 | { 100 | int count; 101 | CuTest* list[MAX_TEST_CASES]; 102 | int failCount; 103 | 104 | } CuSuite; 105 | 106 | 107 | void CuSuiteInit(CuSuite* testSuite); 108 | CuSuite* CuSuiteNew(void); 109 | void CuSuiteDelete(CuSuite *testSuite); 110 | void CuSuiteAdd(CuSuite* testSuite, CuTest *testCase); 111 | void CuSuiteAddSuite(CuSuite* testSuite, CuSuite* testSuite2); 112 | void CuSuiteRun(CuSuite* testSuite); 113 | void CuSuiteSummary(CuSuite* testSuite, CuString* summary); 114 | void CuSuiteDetails(CuSuite* testSuite, CuString* details); 115 | 116 | #endif /* CU_TEST_H */ 117 | -------------------------------------------------------------------------------- /test/cutest-1.5/CuTestTest.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "CuTest.h" 8 | 9 | /*-------------------------------------------------------------------------* 10 | * Helper functions 11 | *-------------------------------------------------------------------------*/ 12 | 13 | #define CompareAsserts(tc, message, expected, actual) X_CompareAsserts((tc), __FILE__, __LINE__, (message), (expected), (actual)) 14 | 15 | static void X_CompareAsserts(CuTest* tc, const char *file, int line, const char* message, const char* expected, const char* actual) 16 | { 17 | int mismatch; 18 | if (expected == NULL || actual == NULL) { 19 | mismatch = (expected != NULL || actual != NULL); 20 | } else { 21 | const char *front = __FILE__ ":"; 22 | const size_t frontLen = strlen(front); 23 | const size_t expectedLen = strlen(expected); 24 | 25 | const char *matchStr = actual; 26 | 27 | mismatch = (strncmp(matchStr, front, frontLen) != 0); 28 | if (!mismatch) { 29 | matchStr = strchr(matchStr + frontLen, ':'); 30 | mismatch |= (matchStr == NULL || strncmp(matchStr, ": ", 2)); 31 | if (!mismatch) { 32 | matchStr += 2; 33 | mismatch |= (strncmp(matchStr, expected, expectedLen) != 0); 34 | } 35 | } 36 | } 37 | 38 | CuAssert_Line(tc, file, line, message, !mismatch); 39 | } 40 | 41 | /*-------------------------------------------------------------------------* 42 | * CuString Test 43 | *-------------------------------------------------------------------------*/ 44 | 45 | void TestCuStringNew(CuTest* tc) 46 | { 47 | CuString* str = CuStringNew(); 48 | CuAssertTrue(tc, 0 == str->length); 49 | CuAssertTrue(tc, 0 != str->size); 50 | CuAssertStrEquals(tc, "", str->buffer); 51 | } 52 | 53 | 54 | void TestCuStringAppend(CuTest* tc) 55 | { 56 | CuString* str = CuStringNew(); 57 | CuStringAppend(str, "hello"); 58 | CuAssertIntEquals(tc, 5, str->length); 59 | CuAssertStrEquals(tc, "hello", str->buffer); 60 | CuStringAppend(str, " world"); 61 | CuAssertIntEquals(tc, 11, str->length); 62 | CuAssertStrEquals(tc, "hello world", str->buffer); 63 | } 64 | 65 | 66 | void TestCuStringAppendNULL(CuTest* tc) 67 | { 68 | CuString* str = CuStringNew(); 69 | CuStringAppend(str, NULL); 70 | CuAssertIntEquals(tc, 4, str->length); 71 | CuAssertStrEquals(tc, "NULL", str->buffer); 72 | } 73 | 74 | 75 | void TestCuStringAppendChar(CuTest* tc) 76 | { 77 | CuString* str = CuStringNew(); 78 | CuStringAppendChar(str, 'a'); 79 | CuStringAppendChar(str, 'b'); 80 | CuStringAppendChar(str, 'c'); 81 | CuStringAppendChar(str, 'd'); 82 | CuAssertIntEquals(tc, 4, str->length); 83 | CuAssertStrEquals(tc, "abcd", str->buffer); 84 | } 85 | 86 | 87 | void TestCuStringInserts(CuTest* tc) 88 | { 89 | CuString* str = CuStringNew(); 90 | CuStringAppend(str, "world"); 91 | CuAssertIntEquals(tc, 5, str->length); 92 | CuAssertStrEquals(tc, "world", str->buffer); 93 | CuStringInsert(str, "hell", 0); 94 | CuAssertIntEquals(tc, 9, str->length); 95 | CuAssertStrEquals(tc, "hellworld", str->buffer); 96 | CuStringInsert(str, "o ", 4); 97 | CuAssertIntEquals(tc, 11, str->length); 98 | CuAssertStrEquals(tc, "hello world", str->buffer); 99 | CuStringInsert(str, "!", 11); 100 | CuAssertIntEquals(tc, 12, str->length); 101 | CuAssertStrEquals(tc, "hello world!", str->buffer); 102 | } 103 | 104 | 105 | void TestCuStringResizes(CuTest* tc) 106 | { 107 | CuString* str = CuStringNew(); 108 | int i; 109 | for(i = 0 ; i < STRING_MAX ; ++i) 110 | { 111 | CuStringAppend(str, "aa"); 112 | } 113 | CuAssertTrue(tc, STRING_MAX * 2 == str->length); 114 | CuAssertTrue(tc, STRING_MAX * 2 <= str->size); 115 | } 116 | 117 | CuSuite* CuStringGetSuite(void) 118 | { 119 | CuSuite* suite = CuSuiteNew(); 120 | 121 | SUITE_ADD_TEST(suite, TestCuStringNew); 122 | SUITE_ADD_TEST(suite, TestCuStringAppend); 123 | SUITE_ADD_TEST(suite, TestCuStringAppendNULL); 124 | SUITE_ADD_TEST(suite, TestCuStringAppendChar); 125 | SUITE_ADD_TEST(suite, TestCuStringInserts); 126 | SUITE_ADD_TEST(suite, TestCuStringResizes); 127 | 128 | return suite; 129 | } 130 | 131 | /*-------------------------------------------------------------------------* 132 | * CuTest Test 133 | *-------------------------------------------------------------------------*/ 134 | 135 | void TestPasses(CuTest* tc) 136 | { 137 | CuAssert(tc, "test should pass", 1 == 0 + 1); 138 | } 139 | 140 | void zTestFails(CuTest* tc) 141 | { 142 | CuAssert(tc, "test should fail", 1 == 1 + 1); 143 | } 144 | 145 | 146 | void TestCuTestNew(CuTest* tc) 147 | { 148 | CuTest* tc2 = CuTestNew("MyTest", TestPasses); 149 | CuAssertStrEquals(tc, "MyTest", tc2->name); 150 | CuAssertTrue(tc, !tc2->failed); 151 | CuAssertTrue(tc, tc2->message == NULL); 152 | CuAssertTrue(tc, tc2->function == TestPasses); 153 | CuAssertTrue(tc, tc2->ran == 0); 154 | CuAssertTrue(tc, tc2->jumpBuf == NULL); 155 | } 156 | 157 | 158 | void TestCuTestInit(CuTest *tc) 159 | { 160 | CuTest tc2; 161 | CuTestInit(&tc2, "MyTest", TestPasses); 162 | CuAssertStrEquals(tc, "MyTest", tc2.name); 163 | CuAssertTrue(tc, !tc2.failed); 164 | CuAssertTrue(tc, tc2.message == NULL); 165 | CuAssertTrue(tc, tc2.function == TestPasses); 166 | CuAssertTrue(tc, tc2.ran == 0); 167 | CuAssertTrue(tc, tc2.jumpBuf == NULL); 168 | } 169 | 170 | void TestCuAssert(CuTest* tc) 171 | { 172 | CuTest tc2; 173 | CuTestInit(&tc2, "MyTest", TestPasses); 174 | 175 | CuAssert(&tc2, "test 1", 5 == 4 + 1); 176 | CuAssertTrue(tc, !tc2.failed); 177 | CuAssertTrue(tc, tc2.message == NULL); 178 | 179 | CuAssert(&tc2, "test 2", 0); 180 | CuAssertTrue(tc, tc2.failed); 181 | CompareAsserts(tc, "CuAssert didn't fail", "test 2", tc2.message); 182 | 183 | CuAssert(&tc2, "test 3", 1); 184 | CuAssertTrue(tc, tc2.failed); 185 | CompareAsserts(tc, "CuAssert didn't fail", "test 2", tc2.message); 186 | 187 | CuAssert(&tc2, "test 4", 0); 188 | CuAssertTrue(tc, tc2.failed); 189 | CompareAsserts(tc, "CuAssert didn't fail", "test 4", tc2.message); 190 | 191 | } 192 | 193 | void TestCuAssertPtrEquals_Success(CuTest* tc) 194 | { 195 | CuTest tc2; 196 | int x; 197 | 198 | CuTestInit(&tc2, "MyTest", TestPasses); 199 | 200 | /* test success case */ 201 | CuAssertPtrEquals(&tc2, &x, &x); 202 | CuAssertTrue(tc, ! tc2.failed); 203 | CuAssertTrue(tc, NULL == tc2.message); 204 | } 205 | 206 | void TestCuAssertPtrEquals_Failure(CuTest* tc) 207 | { 208 | CuTest tc2; 209 | int x; 210 | int* nullPtr = NULL; 211 | char expected_message[STRING_MAX]; 212 | 213 | CuTestInit(&tc2, "MyTest", TestPasses); 214 | 215 | /* test failing case */ 216 | sprintf(expected_message, "expected pointer <0x%p> but was <0x%p>", nullPtr, &x); 217 | CuAssertPtrEquals(&tc2, NULL, &x); 218 | CuAssertTrue(tc, tc2.failed); 219 | CompareAsserts(tc, "CuAssertPtrEquals failed", expected_message, tc2.message); 220 | } 221 | 222 | void TestCuAssertPtrNotNull_Success(CuTest* tc) 223 | { 224 | CuTest tc2; 225 | int x; 226 | 227 | CuTestInit(&tc2, "MyTest", TestPasses); 228 | 229 | /* test success case */ 230 | CuAssertPtrNotNull(&tc2, &x); 231 | CuAssertTrue(tc, ! tc2.failed); 232 | CuAssertTrue(tc, NULL == tc2.message); 233 | } 234 | 235 | void TestCuAssertPtrNotNull_Failure(CuTest* tc) 236 | { 237 | CuTest tc2; 238 | 239 | CuTestInit(&tc2, "MyTest", TestPasses); 240 | 241 | /* test failing case */ 242 | CuAssertPtrNotNull(&tc2, NULL); 243 | CuAssertTrue(tc, tc2.failed); 244 | CompareAsserts(tc, "CuAssertPtrNotNull failed", "null pointer unexpected", tc2.message); 245 | } 246 | 247 | void TestCuTestRun(CuTest* tc) 248 | { 249 | CuTest tc2; 250 | CuTestInit(&tc2, "MyTest", zTestFails); 251 | CuTestRun(&tc2); 252 | 253 | CuAssertStrEquals(tc, "MyTest", tc2.name); 254 | CuAssertTrue(tc, tc2.failed); 255 | CuAssertTrue(tc, tc2.ran); 256 | CompareAsserts(tc, "TestRun failed", "test should fail", tc2.message); 257 | } 258 | 259 | /*-------------------------------------------------------------------------* 260 | * CuSuite Test 261 | *-------------------------------------------------------------------------*/ 262 | 263 | void TestCuSuiteInit(CuTest* tc) 264 | { 265 | CuSuite ts; 266 | CuSuiteInit(&ts); 267 | CuAssertTrue(tc, ts.count == 0); 268 | CuAssertTrue(tc, ts.failCount == 0); 269 | } 270 | 271 | void TestCuSuiteNew(CuTest* tc) 272 | { 273 | CuSuite* ts = CuSuiteNew(); 274 | CuAssertTrue(tc, ts->count == 0); 275 | CuAssertTrue(tc, ts->failCount == 0); 276 | } 277 | 278 | void TestCuSuiteAddTest(CuTest* tc) 279 | { 280 | CuSuite ts; 281 | CuTest tc2; 282 | 283 | CuSuiteInit(&ts); 284 | CuTestInit(&tc2, "MyTest", zTestFails); 285 | 286 | CuSuiteAdd(&ts, &tc2); 287 | CuAssertTrue(tc, ts.count == 1); 288 | 289 | CuAssertStrEquals(tc, "MyTest", ts.list[0]->name); 290 | } 291 | 292 | void TestCuSuiteAddSuite(CuTest* tc) 293 | { 294 | CuSuite* ts1 = CuSuiteNew(); 295 | CuSuite* ts2 = CuSuiteNew(); 296 | 297 | CuSuiteAdd(ts1, CuTestNew("TestFails1", zTestFails)); 298 | CuSuiteAdd(ts1, CuTestNew("TestFails2", zTestFails)); 299 | 300 | CuSuiteAdd(ts2, CuTestNew("TestFails3", zTestFails)); 301 | CuSuiteAdd(ts2, CuTestNew("TestFails4", zTestFails)); 302 | 303 | CuSuiteAddSuite(ts1, ts2); 304 | CuAssertIntEquals(tc, 4, ts1->count); 305 | 306 | CuAssertStrEquals(tc, "TestFails1", ts1->list[0]->name); 307 | CuAssertStrEquals(tc, "TestFails2", ts1->list[1]->name); 308 | CuAssertStrEquals(tc, "TestFails3", ts1->list[2]->name); 309 | CuAssertStrEquals(tc, "TestFails4", ts1->list[3]->name); 310 | } 311 | 312 | void TestCuSuiteRun(CuTest* tc) 313 | { 314 | CuSuite ts; 315 | CuTest tc1, tc2, tc3, tc4; 316 | 317 | CuSuiteInit(&ts); 318 | CuTestInit(&tc1, "TestPasses", TestPasses); 319 | CuTestInit(&tc2, "TestPasses", TestPasses); 320 | CuTestInit(&tc3, "TestFails", zTestFails); 321 | CuTestInit(&tc4, "TestFails", zTestFails); 322 | 323 | CuSuiteAdd(&ts, &tc1); 324 | CuSuiteAdd(&ts, &tc2); 325 | CuSuiteAdd(&ts, &tc3); 326 | CuSuiteAdd(&ts, &tc4); 327 | CuAssertTrue(tc, ts.count == 4); 328 | 329 | CuSuiteRun(&ts); 330 | CuAssertTrue(tc, ts.count - ts.failCount == 2); 331 | CuAssertTrue(tc, ts.failCount == 2); 332 | } 333 | 334 | void TestCuSuiteSummary(CuTest* tc) 335 | { 336 | CuSuite ts; 337 | CuTest tc1, tc2; 338 | CuString summary; 339 | 340 | CuSuiteInit(&ts); 341 | CuTestInit(&tc1, "TestPasses", TestPasses); 342 | CuTestInit(&tc2, "TestFails", zTestFails); 343 | CuStringInit(&summary); 344 | 345 | CuSuiteAdd(&ts, &tc1); 346 | CuSuiteAdd(&ts, &tc2); 347 | CuSuiteRun(&ts); 348 | 349 | CuSuiteSummary(&ts, &summary); 350 | 351 | CuAssertTrue(tc, ts.count == 2); 352 | CuAssertTrue(tc, ts.failCount == 1); 353 | CuAssertStrEquals(tc, ".F\n\n", summary.buffer); 354 | } 355 | 356 | 357 | void TestCuSuiteDetails_SingleFail(CuTest* tc) 358 | { 359 | CuSuite ts; 360 | CuTest tc1, tc2; 361 | CuString details; 362 | const char* front; 363 | const char* back; 364 | 365 | CuSuiteInit(&ts); 366 | CuTestInit(&tc1, "TestPasses", TestPasses); 367 | CuTestInit(&tc2, "TestFails", zTestFails); 368 | CuStringInit(&details); 369 | 370 | CuSuiteAdd(&ts, &tc1); 371 | CuSuiteAdd(&ts, &tc2); 372 | CuSuiteRun(&ts); 373 | 374 | CuSuiteDetails(&ts, &details); 375 | 376 | CuAssertTrue(tc, ts.count == 2); 377 | CuAssertTrue(tc, ts.failCount == 1); 378 | 379 | front = "There was 1 failure:\n" 380 | "1) TestFails: "; 381 | back = "test should fail\n" 382 | "\n!!!FAILURES!!!\n" 383 | "Runs: 2 Passes: 1 Fails: 1\n"; 384 | 385 | CuAssertStrEquals(tc, back, details.buffer + strlen(details.buffer) - strlen(back)); 386 | details.buffer[strlen(front)] = 0; 387 | CuAssertStrEquals(tc, front, details.buffer); 388 | } 389 | 390 | 391 | void TestCuSuiteDetails_SinglePass(CuTest* tc) 392 | { 393 | CuSuite ts; 394 | CuTest tc1; 395 | CuString details; 396 | const char* expected; 397 | 398 | CuSuiteInit(&ts); 399 | CuTestInit(&tc1, "TestPasses", TestPasses); 400 | CuStringInit(&details); 401 | 402 | CuSuiteAdd(&ts, &tc1); 403 | CuSuiteRun(&ts); 404 | 405 | CuSuiteDetails(&ts, &details); 406 | 407 | CuAssertTrue(tc, ts.count == 1); 408 | CuAssertTrue(tc, ts.failCount == 0); 409 | 410 | expected = 411 | "OK (1 test)\n"; 412 | 413 | CuAssertStrEquals(tc, expected, details.buffer); 414 | } 415 | 416 | void TestCuSuiteDetails_MultiplePasses(CuTest* tc) 417 | { 418 | CuSuite ts; 419 | CuTest tc1, tc2; 420 | CuString details; 421 | const char* expected; 422 | 423 | CuSuiteInit(&ts); 424 | CuTestInit(&tc1, "TestPasses", TestPasses); 425 | CuTestInit(&tc2, "TestPasses", TestPasses); 426 | CuStringInit(&details); 427 | 428 | CuSuiteAdd(&ts, &tc1); 429 | CuSuiteAdd(&ts, &tc2); 430 | CuSuiteRun(&ts); 431 | 432 | CuSuiteDetails(&ts, &details); 433 | 434 | CuAssertTrue(tc, ts.count == 2); 435 | CuAssertTrue(tc, ts.failCount == 0); 436 | 437 | expected = 438 | "OK (2 tests)\n"; 439 | 440 | CuAssertStrEquals(tc, expected, details.buffer); 441 | } 442 | 443 | void TestCuSuiteDetails_MultipleFails(CuTest* tc) 444 | { 445 | CuSuite ts; 446 | CuTest tc1, tc2; 447 | CuString details; 448 | const char* front; 449 | const char* mid; 450 | const char* back; 451 | 452 | CuSuiteInit(&ts); 453 | CuTestInit(&tc1, "TestFails1", zTestFails); 454 | CuTestInit(&tc2, "TestFails2", zTestFails); 455 | CuStringInit(&details); 456 | 457 | CuSuiteAdd(&ts, &tc1); 458 | CuSuiteAdd(&ts, &tc2); 459 | CuSuiteRun(&ts); 460 | 461 | CuSuiteDetails(&ts, &details); 462 | 463 | CuAssertTrue(tc, ts.count == 2); 464 | CuAssertTrue(tc, ts.failCount == 2); 465 | 466 | front = 467 | "There were 2 failures:\n" 468 | "1) TestFails1: "; 469 | mid = "test should fail\n" 470 | "2) TestFails2: "; 471 | back = "test should fail\n" 472 | "\n!!!FAILURES!!!\n" 473 | "Runs: 2 Passes: 0 Fails: 2\n"; 474 | 475 | CuAssertStrEquals(tc, back, details.buffer + strlen(details.buffer) - strlen(back)); 476 | CuAssert(tc, "Couldn't find middle", strstr(details.buffer, mid) != NULL); 477 | details.buffer[strlen(front)] = 0; 478 | CuAssertStrEquals(tc, front, details.buffer); 479 | } 480 | 481 | 482 | /*-------------------------------------------------------------------------* 483 | * Misc Test 484 | *-------------------------------------------------------------------------*/ 485 | 486 | void TestCuStrCopy(CuTest* tc) 487 | { 488 | const char* old = "hello world"; 489 | const char* newStr = CuStrCopy(old); 490 | CuAssert(tc, "old is new", strcmp(old, newStr) == 0); 491 | } 492 | 493 | 494 | void TestCuStringAppendFormat(CuTest* tc) 495 | { 496 | int i; 497 | char* text = CuStrAlloc(301); /* long string */ 498 | CuString* str = CuStringNew(); 499 | for (i = 0 ; i < 300 ; ++i) 500 | text[i] = 'a'; 501 | text[300] = '\0'; 502 | CuStringAppendFormat(str, "%s", text); 503 | 504 | /* buffer limit raised to HUGE_STRING_LEN so no overflow */ 505 | 506 | CuAssert(tc, "length of str->buffer is 300", 300 == strlen(str->buffer)); 507 | } 508 | 509 | void TestFail(CuTest* tc) 510 | { 511 | jmp_buf buf; 512 | int pointReached = 0; 513 | CuTest* tc2 = CuTestNew("TestFails", zTestFails); 514 | tc2->jumpBuf = &buf; 515 | if (setjmp(buf) == 0) 516 | { 517 | CuFail(tc2, "hello world"); 518 | pointReached = 1; 519 | } 520 | CuAssert(tc, "point was not reached", pointReached == 0); 521 | } 522 | 523 | void TestAssertStrEquals(CuTest* tc) 524 | { 525 | jmp_buf buf; 526 | CuTest *tc2 = CuTestNew("TestAssertStrEquals", zTestFails); 527 | 528 | const char* expected = "expected but was "; 529 | const char *expectedMsg = "some text: expected but was "; 530 | 531 | tc2->jumpBuf = &buf; 532 | if (setjmp(buf) == 0) 533 | { 534 | CuAssertStrEquals(tc2, "hello", "world"); 535 | } 536 | CuAssertTrue(tc, tc2->failed); 537 | CompareAsserts(tc, "CuAssertStrEquals failed", expected, tc2->message); 538 | if (setjmp(buf) == 0) 539 | { 540 | CuAssertStrEquals_Msg(tc2, "some text", "hello", "world"); 541 | } 542 | CuAssertTrue(tc, tc2->failed); 543 | CompareAsserts(tc, "CuAssertStrEquals failed", expectedMsg, tc2->message); 544 | } 545 | 546 | void TestAssertStrEquals_NULL(CuTest* tc) 547 | { 548 | jmp_buf buf; 549 | CuTest *tc2 = CuTestNew("TestAssertStrEquals_NULL", zTestFails); 550 | 551 | tc2->jumpBuf = &buf; 552 | if (setjmp(buf) == 0) 553 | { 554 | CuAssertStrEquals(tc2, NULL, NULL); 555 | } 556 | CuAssertTrue(tc, !tc2->failed); 557 | CompareAsserts(tc, "CuAssertStrEquals_NULL failed", NULL, tc2->message); 558 | if (setjmp(buf) == 0) 559 | { 560 | CuAssertStrEquals_Msg(tc2, "some text", NULL, NULL); 561 | } 562 | CuAssertTrue(tc, !tc2->failed); 563 | CompareAsserts(tc, "CuAssertStrEquals_NULL failed", NULL, tc2->message); 564 | } 565 | 566 | void TestAssertStrEquals_FailNULLStr(CuTest* tc) 567 | { 568 | jmp_buf buf; 569 | CuTest *tc2 = CuTestNew("TestAssertStrEquals_FailNULLStr", zTestFails); 570 | 571 | const char* expected = "expected but was "; 572 | const char *expectedMsg = "some text: expected but was "; 573 | 574 | tc2->jumpBuf = &buf; 575 | if (setjmp(buf) == 0) 576 | { 577 | CuAssertStrEquals(tc2, "hello", NULL); 578 | } 579 | CuAssertTrue(tc, tc2->failed); 580 | CompareAsserts(tc, "CuAssertStrEquals_FailNULLStr failed", expected, tc2->message); 581 | if (setjmp(buf) == 0) 582 | { 583 | CuAssertStrEquals_Msg(tc2, "some text", "hello", NULL); 584 | } 585 | CuAssertTrue(tc, tc2->failed); 586 | CompareAsserts(tc, "CuAssertStrEquals_FailNULLStr failed", expectedMsg, tc2->message); 587 | } 588 | 589 | void TestAssertStrEquals_FailStrNULL(CuTest* tc) 590 | { 591 | jmp_buf buf; 592 | CuTest *tc2 = CuTestNew("TestAssertStrEquals_FailStrNULL", zTestFails); 593 | 594 | const char* expected = "expected but was "; 595 | const char *expectedMsg = "some text: expected but was "; 596 | 597 | tc2->jumpBuf = &buf; 598 | if (setjmp(buf) == 0) 599 | { 600 | CuAssertStrEquals(tc2, NULL, "hello"); 601 | } 602 | CuAssertTrue(tc, tc2->failed); 603 | CompareAsserts(tc, "CuAssertStrEquals_FailStrNULL failed", expected, tc2->message); 604 | if (setjmp(buf) == 0) 605 | { 606 | CuAssertStrEquals_Msg(tc2, "some text", NULL, "hello"); 607 | } 608 | CuAssertTrue(tc, tc2->failed); 609 | CompareAsserts(tc, "CuAssertStrEquals_FailStrNULL failed", expectedMsg, tc2->message); 610 | } 611 | 612 | void TestAssertIntEquals(CuTest* tc) 613 | { 614 | jmp_buf buf; 615 | CuTest *tc2 = CuTestNew("TestAssertIntEquals", zTestFails); 616 | const char* expected = "expected <42> but was <32>"; 617 | const char* expectedMsg = "some text: expected <42> but was <32>"; 618 | tc2->jumpBuf = &buf; 619 | if (setjmp(buf) == 0) 620 | { 621 | CuAssertIntEquals(tc2, 42, 32); 622 | } 623 | CuAssertTrue(tc, tc2->failed); 624 | CompareAsserts(tc, "CuAssertIntEquals failed", expected, tc2->message); 625 | if (setjmp(buf) == 0) 626 | { 627 | CuAssertIntEquals_Msg(tc2, "some text", 42, 32); 628 | } 629 | CuAssertTrue(tc, tc2->failed); 630 | CompareAsserts(tc, "CuAssertStrEquals failed", expectedMsg, tc2->message); 631 | } 632 | 633 | void TestAssertDblEquals(CuTest* tc) 634 | { 635 | jmp_buf buf; 636 | double x = 3.33; 637 | double y = 10.0 / 3.0; 638 | CuTest *tc2 = CuTestNew("TestAssertDblEquals", zTestFails); 639 | char expected[STRING_MAX]; 640 | char expectedMsg[STRING_MAX]; 641 | sprintf(expected, "expected <%lf> but was <%lf>", x, y); 642 | sprintf(expectedMsg, "some text: expected <%lf> but was <%lf>", x, y); 643 | 644 | CuTestInit(tc2, "TestAssertDblEquals", TestPasses); 645 | 646 | CuAssertDblEquals(tc2, x, x, 0.0); 647 | CuAssertTrue(tc, ! tc2->failed); 648 | CuAssertTrue(tc, NULL == tc2->message); 649 | 650 | CuAssertDblEquals(tc2, x, y, 0.01); 651 | CuAssertTrue(tc, ! tc2->failed); 652 | CuAssertTrue(tc, NULL == tc2->message); 653 | 654 | tc2->jumpBuf = &buf; 655 | if (setjmp(buf) == 0) 656 | { 657 | CuAssertDblEquals(tc2, x, y, 0.001); 658 | } 659 | CuAssertTrue(tc, tc2->failed); 660 | CompareAsserts(tc, "CuAssertDblEquals failed", expected, tc2->message); 661 | tc2->jumpBuf = &buf; 662 | if (setjmp(buf) == 0) 663 | { 664 | CuAssertDblEquals_Msg(tc2, "some text", x, y, 0.001); 665 | } 666 | CuAssertTrue(tc, tc2->failed); 667 | CompareAsserts(tc, "CuAssertDblEquals failed", expectedMsg, tc2->message); 668 | } 669 | 670 | /*-------------------------------------------------------------------------* 671 | * main 672 | *-------------------------------------------------------------------------*/ 673 | 674 | CuSuite* CuGetSuite(void) 675 | { 676 | CuSuite* suite = CuSuiteNew(); 677 | 678 | SUITE_ADD_TEST(suite, TestCuStringAppendFormat); 679 | SUITE_ADD_TEST(suite, TestCuStrCopy); 680 | SUITE_ADD_TEST(suite, TestFail); 681 | SUITE_ADD_TEST(suite, TestAssertStrEquals); 682 | SUITE_ADD_TEST(suite, TestAssertStrEquals_NULL); 683 | SUITE_ADD_TEST(suite, TestAssertStrEquals_FailStrNULL); 684 | SUITE_ADD_TEST(suite, TestAssertStrEquals_FailNULLStr); 685 | SUITE_ADD_TEST(suite, TestAssertIntEquals); 686 | SUITE_ADD_TEST(suite, TestAssertDblEquals); 687 | 688 | SUITE_ADD_TEST(suite, TestCuTestNew); 689 | SUITE_ADD_TEST(suite, TestCuTestInit); 690 | SUITE_ADD_TEST(suite, TestCuAssert); 691 | SUITE_ADD_TEST(suite, TestCuAssertPtrEquals_Success); 692 | SUITE_ADD_TEST(suite, TestCuAssertPtrEquals_Failure); 693 | SUITE_ADD_TEST(suite, TestCuAssertPtrNotNull_Success); 694 | SUITE_ADD_TEST(suite, TestCuAssertPtrNotNull_Failure); 695 | SUITE_ADD_TEST(suite, TestCuTestRun); 696 | 697 | SUITE_ADD_TEST(suite, TestCuSuiteInit); 698 | SUITE_ADD_TEST(suite, TestCuSuiteNew); 699 | SUITE_ADD_TEST(suite, TestCuSuiteAddTest); 700 | SUITE_ADD_TEST(suite, TestCuSuiteAddSuite); 701 | SUITE_ADD_TEST(suite, TestCuSuiteRun); 702 | SUITE_ADD_TEST(suite, TestCuSuiteSummary); 703 | SUITE_ADD_TEST(suite, TestCuSuiteDetails_SingleFail); 704 | SUITE_ADD_TEST(suite, TestCuSuiteDetails_SinglePass); 705 | SUITE_ADD_TEST(suite, TestCuSuiteDetails_MultiplePasses); 706 | SUITE_ADD_TEST(suite, TestCuSuiteDetails_MultipleFails); 707 | 708 | return suite; 709 | } 710 | -------------------------------------------------------------------------------- /test/cutest-1.5/README.txt: -------------------------------------------------------------------------------- 1 | HOW TO USE 2 | 3 | You can use CuTest to create unit tests to drive your development 4 | in the style of Extreme Programming. You can also add unit tests to 5 | existing code to ensure that it works as you suspect. 6 | 7 | Your unit tests are an investment. They let you to change your 8 | code and add new features confidently without worrying about 9 | accidentally breaking earlier features. 10 | 11 | 12 | LICENSING 13 | 14 | For details on licensing see license.txt. 15 | 16 | 17 | GETTING STARTED 18 | 19 | To add unit testing to your C code the only files you need are 20 | CuTest.c and CuTest.h. 21 | 22 | CuTestTest.c and AllTests.c have been included to provide an 23 | example of how to write unit tests and then how to aggregate them 24 | into suites and into a single AllTests.c file. Suites allow you 25 | to put group tests into logical sets. AllTests.c combines all the 26 | suites and runs them. 27 | 28 | You should not have to look inside CuTest.c. Looking in 29 | CuTestTest.c and AllTests.c (for example usage) should be 30 | sufficient. 31 | 32 | After downloading the sources, run your compiler to create an 33 | executable called AllTests.exe. For example, if you are using 34 | Windows with the cl.exe compiler you would type: 35 | 36 | cl.exe AllTests.c CuTest.c CuTestTest.c 37 | AllTests.exe 38 | 39 | This will run all the unit tests associated with CuTest and print 40 | the output on the console. You can replace cl.exe with gcc or 41 | your favorite compiler in the command above. 42 | 43 | 44 | DETAILED EXAMPLE 45 | 46 | Here is a more detailed example. We will work through a simple 47 | test first exercise. The goal is to create a library of string 48 | utilities. First, lets write a function that converts a 49 | null-terminated string to all upper case. 50 | 51 | Ensure that CuTest.c and CuTest.h are accessible from your C 52 | project. Next, create a file called StrUtil.c with these 53 | contents: 54 | 55 | #include "CuTest.h" 56 | 57 | char* StrToUpper(char* str) { 58 | return str; 59 | } 60 | 61 | void TestStrToUpper(CuTest *tc) { 62 | char* input = strdup("hello world"); 63 | char* actual = StrToUpper(input); 64 | char* expected = "HELLO WORLD"; 65 | CuAssertStrEquals(tc, expected, actual); 66 | } 67 | 68 | CuSuite* StrUtilGetSuite() { 69 | CuSuite* suite = CuSuiteNew(); 70 | SUITE_ADD_TEST(suite, TestStrToUpper); 71 | return suite; 72 | } 73 | 74 | Create another file called AllTests.c with these contents: 75 | 76 | #include "CuTest.h" 77 | 78 | CuSuite* StrUtilGetSuite(); 79 | 80 | void RunAllTests(void) { 81 | CuString *output = CuStringNew(); 82 | CuSuite* suite = CuSuiteNew(); 83 | 84 | CuSuiteAddSuite(suite, StrUtilGetSuite()); 85 | 86 | CuSuiteRun(suite); 87 | CuSuiteSummary(suite, output); 88 | CuSuiteDetails(suite, output); 89 | printf("%s\n", output->buffer); 90 | } 91 | 92 | int main(void) { 93 | RunAllTests(); 94 | } 95 | 96 | Then type this on the command line: 97 | 98 | gcc AllTests.c CuTest.c StrUtil.c 99 | 100 | to compile. You can replace gcc with your favorite compiler. 101 | CuTest should be portable enough to handle all Windows and Unix 102 | compilers. Then to run the tests type: 103 | 104 | a.out 105 | 106 | This will print an error because we haven't implemented the 107 | StrToUpper function correctly. We are just returning the string 108 | without changing it to upper case. 109 | 110 | char* StrToUpper(char* str) { 111 | return str; 112 | } 113 | 114 | Rewrite this as follows: 115 | 116 | char* StrToUpper(char* str) { 117 | char* p; 118 | for (p = str ; *p ; ++p) *p = toupper(*p); 119 | return str; 120 | } 121 | 122 | Recompile and run the tests again. The test should pass this 123 | time. 124 | 125 | 126 | WHAT TO DO NEXT 127 | 128 | At this point you might want to write more tests for the 129 | StrToUpper function. Here are some ideas: 130 | 131 | TestStrToUpper_EmptyString : pass in "" 132 | TestStrToUpper_UpperCase : pass in "HELLO WORLD" 133 | TestStrToUpper_MixedCase : pass in "HELLO world" 134 | TestStrToUpper_Numbers : pass in "1234 hello" 135 | 136 | As you write each one of these tests add it to StrUtilGetSuite 137 | function. If you don't the tests won't be run. Later as you write 138 | other functions and write tests for them be sure to include those 139 | in StrUtilGetSuite also. The StrUtilGetSuite function should 140 | include all the tests in StrUtil.c 141 | 142 | Over time you will create another file called FunkyStuff.c 143 | containing other functions unrelated to StrUtil. Follow the same 144 | pattern. Create a FunkyStuffGetSuite function in FunkyStuff.c. 145 | And add FunkyStuffGetSuite to AllTests.c. 146 | 147 | The framework is designed in the way it is so that it is easy to 148 | organize a lot of tests. 149 | 150 | THE BIG PICTURE 151 | 152 | Each individual test corresponds to a CuTest. These are grouped 153 | to form a CuSuite. CuSuites can hold CuTests or other CuSuites. 154 | AllTests.c collects all the CuSuites in the program into a single 155 | CuSuite which it then runs as a single CuSuite. 156 | 157 | The project is open source so feel free to take a peek under the 158 | hood at the CuTest.c file to see how it works. CuTestTest.c 159 | contains tests for CuTest.c. So CuTest tests itself. 160 | 161 | Since AllTests.c has a main() you will need to exclude this when 162 | you are building your product. Here is a nicer way to do this if 163 | you want to avoid messing with multiple builds. Remove the main() 164 | in AllTests.c. Note that it just calls RunAllTests(). Instead 165 | we'll call this directly from the main program. 166 | 167 | Now in the main() of the actual program check to see if the 168 | command line option "--test" was passed. If it was then I call 169 | RunAllTests() from AllTests.c. Otherwise run the real program. 170 | 171 | Shipping the tests with the code can be useful. If you customers 172 | complain about a problem you can ask them to run the unit tests 173 | and send you the output. This can help you to quickly isolate the 174 | piece of your system that is malfunctioning in the customer's 175 | environment. 176 | 177 | CuTest offers a rich set of CuAssert functions. Here is a list: 178 | 179 | void CuAssert(CuTest* tc, char* message, int condition); 180 | void CuAssertTrue(CuTest* tc, int condition); 181 | void CuAssertStrEquals(CuTest* tc, char* expected, char* actual); 182 | void CuAssertIntEquals(CuTest* tc, int expected, int actual); 183 | void CuAssertPtrEquals(CuTest* tc, void* expected, void* actual); 184 | void CuAssertPtrNotNull(CuTest* tc, void* pointer); 185 | 186 | The project is open source and so you can add other more powerful 187 | asserts to make your tests easier to write and more concise. 188 | Please feel free to send me changes you make so that I can 189 | incorporate them into future releases. 190 | 191 | If you see any errors in this document please contact me at 192 | asimjalis@peakprogramming.com. 193 | 194 | 195 | AUTOMATING TEST SUITE GENERATION 196 | 197 | make-tests.sh will grep through all the .c files in the current 198 | directory and generate the code to run all the tests contained in 199 | them. Using this script you don't have to worry about writing 200 | AllTests.c or dealing with any of the other suite code. 201 | 202 | 203 | CREDITS 204 | 205 | These people have contributed useful code changes to the CuTest project. 206 | Thanks! 207 | 208 | - [02.23.2003] Dave Glowacki 209 | - [04.17.2009] Tobias Lippert 210 | - [11.13.2009] Eli Bendersky 211 | - [12.14.2009] Andrew Brown 212 | -------------------------------------------------------------------------------- /test/cutest-1.5/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | CuTest: The Cutest C Unit Testing Framework 7 | 8 | 9 | 10 | 11 | 23 | 26 | 27 |
28 |
29 |
30 | 31 | [ 32 | Download Now 33 | | Download Statistics 34 | | Browse Source 35 | | SourceForge 36 | | Blog 37 | | asimjalis(nospam)gmail.com 38 | ] 39 | 40 |
41 | 42 | 43 |

CuTest: C Unit Testing Framework

44 | 45 | 46 |

Overview

47 | 48 | CuTest is a unit testing library for the C language. It can be 49 | used to do Extreme Programming and Test-First Development in the 50 | C language. It's a fun and cute library that will make your 51 | programming fun and productive. 52 | 53 |

Benefits

54 | 55 |
    56 | 57 |
  • Lower Defects. The tests ensure that your code keeps working 58 | as you make small changes in it. 59 | 60 |
  • Faster Debugging. The tests tell you which subroutine is 61 | broken. You avoid spending hours trying to figure out what's 62 | broken. 63 | 64 |
  • Development Speed. You trust your old code and can keep 65 | adding to it without worrying about bad interactions. If there is 66 | a bad interaction the tests will catch it. 67 | 68 |
  • Permanent Bug Fixes. If every time a bug is reported you 69 | write a quick test, you will guarantee that the bug never 70 | reappears again. 71 | 72 |
  • Fun. As your bug count drops you will begin to enjoy 73 | programming like you've never done before. Running the tests 74 | every few minutes and seeing them pass feels good. 75 | 76 |
77 | 78 |

Features

79 | 80 |
    81 | 82 |
  • Small. Consists of a single .c and .h file. 83 | 84 |
  • Easy to Deploy. Just drop the two files into your source 85 | tree. 86 | 87 |
  • Highly Portable. Works with all major compilers on Windows 88 | (Microsoft, Borland), Linux, Unix, PalmOS. 89 | 90 |
  • Open Source. You can extend it to add more functionality. 91 | The source can be invaluable if you are trying to trace a test 92 | failure. 93 | 94 |
  • Cuteness. Of all the testing frameworks CuTest has the 95 | cutest name :-) 96 | 97 |
98 | 99 | 100 |

Licensing

101 | 102 | CuTest is distributed under the zlib/libpng 104 | license. See license.txt in the distribution for text of license. The 105 | intent of the license is to: 106 | 107 |
    108 | 109 |
  • Keep the license as simple as possible 110 |
  • Encourage the use of CuTest in both free and commercial applications and libraries 111 |
  • Keep the source code together 112 |
  • Give credit to the CuTest contributors for their work 113 | 114 |
115 | 116 | If you find CuTest useful we would like to hear about it. 117 | 118 | 119 |

Getting Started

120 | 121 |

For a detailed tutorial see README in the distribution. This shows you how 122 | to organize your tests and how to autogenerate the AllTests.c file from your 123 | source files. 124 | 125 |

To add unit testing to your C code the only files you need 126 | are CuTest.c and CuTest.h. 127 | 128 |

CuTestTest.c and AllTests.c have been included to provide an 129 | example of how to write unit tests and then how to aggregate them 130 | into suites and into a single AllTests.c file. Suites allow you 131 | to put unit tests for different parts of your code in different 132 | files. AllTests.c combines all the suites and runs them. 133 | 134 |

You should not have to look inside CuTest.c. Looking in 135 | CuTestTest.c (for example usage) should be sufficient. 136 | 137 |

After downloading the sources, run your compiler to create an 138 | executable called AllTests.exe. For example, if you are using 139 | Windows you would type: 140 | 141 |

142 | cl AllTests.c CuTest.c CuTestTest.c
143 | AllTests.exe
144 | 
145 | 146 |

This will run all the unit tests associated with CuTest and 147 | print the output on the console. 148 | 149 |

For more details on how to use the library look at the README file included 150 | with the distribution. 151 | 152 |

Contribute

153 | 154 |

We hope you CuTest saves you time and helps you produce high quality 155 | software. 156 | 157 |

If you find CuTest useful, let us know. Tell us what platform you are using 158 | it on (Windows, Linux, etc), and what kinds of applications you are using it 159 | with. 160 | 161 |

If you would like to contribute documentation or tutorials to this project 162 | please send e-mail. 163 | 164 |
165 |
166 | 167 |


168 | 169 | Copyright © 2002-2003, Asim Jalis (asimjalis(nospam)gmail.com). 171 | All rights reserved. 172 | 173 | 174 | 191 | 192 |

193 | SourceForge.net Logo 194 | 195 | 196 | 197 | -------------------------------------------------------------------------------- /test/cutest-1.5/license.txt: -------------------------------------------------------------------------------- 1 | NOTE 2 | 3 | The license is based on the zlib/libpng license. For more details see 4 | http://www.opensource.org/licenses/zlib-license.html. The intent of the 5 | license is to: 6 | 7 | - keep the license as simple as possible 8 | - encourage the use of CuTest in both free and commercial applications 9 | and libraries 10 | - keep the source code together 11 | - give credit to the CuTest contributors for their work 12 | 13 | If you ship CuTest in source form with your source distribution, the 14 | following license document must be included with it in unaltered form. 15 | If you find CuTest useful we would like to hear about it. 16 | 17 | LICENSE 18 | 19 | Copyright (c) 2003 Asim Jalis 20 | 21 | This software is provided 'as-is', without any express or implied 22 | warranty. In no event will the authors be held liable for any damages 23 | arising from the use of this software. 24 | 25 | Permission is granted to anyone to use this software for any purpose, 26 | including commercial applications, and to alter it and redistribute it 27 | freely, subject to the following restrictions: 28 | 29 | 1. The origin of this software must not be misrepresented; you must not 30 | claim that you wrote the original software. If you use this software in 31 | a product, an acknowledgment in the product documentation would be 32 | appreciated but is not required. 33 | 34 | 2. Altered source versions must be plainly marked as such, and must not 35 | be misrepresented as being the original software. 36 | 37 | 3. This notice may not be removed or altered from any source 38 | distribution. 39 | -------------------------------------------------------------------------------- /test/cutest-1.5/make-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Auto generate single AllTests file for CuTest. 4 | # Searches through all *.c files in the current directory. 5 | # Prints to stdout. 6 | # Author: Asim Jalis 7 | # Date: 01/08/2003 8 | 9 | if test $# -eq 0 ; then FILES=*.c ; else FILES=$* ; fi 10 | 11 | echo ' 12 | 13 | /* This is auto-generated code. Edit at your own peril. */ 14 | #include 15 | #include 16 | 17 | #include "CuTest.h" 18 | 19 | ' 20 | 21 | cat $FILES | grep '^void Test' | 22 | sed -e 's/(.*$//' \ 23 | -e 's/$/(CuTest*);/' \ 24 | -e 's/^/extern /' 25 | 26 | echo \ 27 | ' 28 | 29 | void RunAllTests(void) 30 | { 31 | CuString *output = CuStringNew(); 32 | CuSuite* suite = CuSuiteNew(); 33 | 34 | ' 35 | cat $FILES | grep '^void Test' | 36 | sed -e 's/^void //' \ 37 | -e 's/(.*$//' \ 38 | -e 's/^/ SUITE_ADD_TEST(suite, /' \ 39 | -e 's/$/);/' 40 | 41 | echo \ 42 | ' 43 | CuSuiteRun(suite); 44 | CuSuiteSummary(suite, output); 45 | CuSuiteDetails(suite, output); 46 | printf("%s\n", output->buffer); 47 | CuStringDelete(output); 48 | CuSuiteDelete(suite); 49 | } 50 | 51 | int main(void) 52 | { 53 | RunAllTests(); 54 | } 55 | ' 56 | -------------------------------------------------------------------------------- /test/cutest-1.5/style.css: -------------------------------------------------------------------------------- 1 | BODY { 2 | font-family: "Verdana,Arial,sans-serif"; 3 | font-size: 10pt; 4 | } 5 | PRE.LIST { 6 | background: #CFD9FF; 7 | border: 1ex solid #AAAAE6; 8 | padding: 1ex; 9 | } 10 | 11 | PRE.SNIP { 12 | background: #CFD9FF; 13 | padding: 1ex; 14 | } 15 | 16 | PRE.CONSOLE { 17 | color: #CCCCCC; 18 | background: #000000; 19 | font-family: "Courier New,Courier"; 20 | padding: 1ex; 21 | } 22 | -------------------------------------------------------------------------------- /test/cvm_test.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/5/16. 3 | // 4 | 5 | #include "CuTest.h" 6 | #include "cvm.h" 7 | 8 | void 9 | cvm_test_basic_insts(CuTest *tc) 10 | { 11 | InstList *inst_list = inst_list_new(16); 12 | 13 | inst_list_push(inst_list, cvm_inst_new_i_type(I_LI, 1, 5)); 14 | inst_list_push(inst_list, cvm_inst_new_i_type(I_LI, 2, 10)); 15 | inst_list_push(inst_list, cvm_inst_new_d_type(I_ADD, 3, 1, 2)); 16 | inst_list_push(inst_list, cvm_inst_new_d_type(I_HALT, 0, 0, 0)); 17 | 18 | VMState *vm = cvm_state_new(inst_list, NULL); 19 | cvm_state_run(vm); 20 | 21 | CuAssertIntEquals(tc, 5, value_to_int(cvm_get_register(vm, 1))); 22 | CuAssertIntEquals(tc, 10, value_to_int(cvm_get_register(vm, 2))); 23 | CuAssertIntEquals(tc, 15, value_to_int(cvm_get_register(vm, 3))); 24 | 25 | cvm_state_destroy(vm); 26 | } 27 | 28 | void 29 | cvm_test_branch_insts(CuTest *tc) 30 | { 31 | InstList *inst_list = inst_list_new(16); 32 | 33 | inst_list_push(inst_list, cvm_inst_new_i_type(I_LI, 1, 1)); 34 | inst_list_push(inst_list, cvm_inst_new_i_type(I_LI, 2, 10)); 35 | inst_list_push(inst_list, cvm_inst_new_i_type(I_LI, 3, 0)); 36 | 37 | inst_list_push(inst_list, cvm_inst_new_i_type(I_J, 0, 5)); 38 | inst_list_push(inst_list, cvm_inst_new_d_type(I_ADD, 3, 3, 1)); 39 | inst_list_push(inst_list, cvm_inst_new_d_type(I_SLE, 4, 2, 3)); 40 | inst_list_push(inst_list, cvm_inst_new_i_type(I_BNR, 4, -3)); 41 | 42 | inst_list_push(inst_list, cvm_inst_new_d_type(I_HALT, 0, 0, 0)); 43 | 44 | VMState *vm = cvm_state_new(inst_list, NULL); 45 | cvm_state_run(vm); 46 | 47 | CuAssertIntEquals(tc, 1, value_to_int(cvm_get_register(vm, 1))); 48 | CuAssertIntEquals(tc, 10, value_to_int(cvm_get_register(vm, 2))); 49 | CuAssertIntEquals(tc, 10, value_to_int(cvm_get_register(vm, 3))); 50 | 51 | cvm_state_destroy(vm); 52 | } 53 | 54 | void 55 | cvm_register_zero_should_be_zero_test(CuTest *tc) 56 | { 57 | VMState *vm = cvm_state_new(NULL, NULL); 58 | 59 | CuAssertIntEquals(tc, 0, value_to_int(cvm_get_register(vm, 0))); 60 | 61 | cvm_state_destroy(vm); 62 | } 63 | 64 | void 65 | cvm_object_test(CuTest *tc) 66 | { 67 | InstList *inst_list = inst_list_new(16); 68 | StringPool *string_pool = string_pool_new(); 69 | 70 | CString *test_key = string_pool_insert_str(&string_pool, "_test_key_"); 71 | 72 | inst_list_push(inst_list, cvm_inst_new_d_type(I_NEW_OBJ, 1, 0, 0)); 73 | inst_list_push(inst_list, cvm_inst_new_i_type(I_LSTR, 2, (intptr_t)test_key)); 74 | inst_list_push(inst_list, cvm_inst_new_i_type(I_LI, 3, 10)); 75 | inst_list_push(inst_list, cvm_inst_new_d_type(I_SET_OBJ, 1, 3, 2)); 76 | inst_list_push(inst_list, cvm_inst_new_d_type(I_GET_OBJ, 4, 1, 2)); 77 | 78 | inst_list_push(inst_list, cvm_inst_new_d_type(I_HALT, 0, 0, 0)); 79 | 80 | VMState *vm = cvm_state_new(inst_list, string_pool); 81 | cvm_state_run(vm); 82 | 83 | CuAssertIntEquals(tc, 10, value_to_int(cvm_get_register(vm, 4))); 84 | 85 | cvm_state_destroy(vm); 86 | } 87 | 88 | CuSuite * 89 | cvm_test_suite(void) 90 | { 91 | CuSuite *suite = CuSuiteNew(); 92 | 93 | SUITE_ADD_TEST(suite, cvm_test_basic_insts); 94 | SUITE_ADD_TEST(suite, cvm_test_branch_insts); 95 | SUITE_ADD_TEST(suite, cvm_register_zero_should_be_zero_test); 96 | SUITE_ADD_TEST(suite, cvm_object_test); 97 | 98 | return suite; 99 | } 100 | -------------------------------------------------------------------------------- /test/hash_test.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/7/16. 3 | // 4 | 5 | #include "CuTest.h" 6 | #include "hash.h" 7 | 8 | void 9 | hash_test_basic_insert(CuTest *tc) 10 | { 11 | Hash *uut = hash_new(32); 12 | int i; 13 | 14 | for (i = 0; i < 16; ++i) { 15 | CuAssertIntEquals(tc, i, (int)hash_size(uut)); 16 | hash_set(uut, (uintptr_t)i, value_from_int(i)); 17 | } 18 | 19 | for (i = 0; i < 16; ++i) { 20 | CuAssertTrue(tc, value_is_int(hash_find(uut, (uintptr_t)i))); 21 | CuAssertIntEquals(tc, i, (int)value_to_int(hash_find(uut, (uintptr_t)i))); 22 | } 23 | 24 | for (i = 15; i >= 0; --i) { 25 | CuAssertTrue(tc, value_is_int(hash_find(uut, (uintptr_t)i))); 26 | CuAssertIntEquals(tc, i, (int)value_to_int(hash_find(uut, (uintptr_t)i))); 27 | } 28 | 29 | CuAssertTrue(tc, value_is_undefined(hash_find(uut, 16))); 30 | 31 | hash_destroy(uut); 32 | } 33 | 34 | void 35 | hash_test_basic_remove(CuTest *tc) 36 | { 37 | Hash *uut = hash_new(32); 38 | int i; 39 | 40 | for (i = 0; i < 16; ++i) { 41 | hash_set(uut, (uintptr_t)i, value_from_int(i)); 42 | } 43 | 44 | for (i = 0; i < 16; ++i) { 45 | CuAssertTrue(tc, value_is_int(hash_find(uut, (uintptr_t)i))); 46 | CuAssertIntEquals(tc, i, (int)value_to_int(hash_find(uut, (uintptr_t)i))); 47 | } 48 | 49 | for (i = 0; i < 8; ++i) { 50 | CuAssertIntEquals(tc, 16 - i, (int)hash_size(uut)); 51 | hash_set(uut, (uintptr_t)i, value_undefined()); 52 | } 53 | 54 | for (i = 0; i < 8; ++i) { 55 | CuAssertTrue(tc, value_is_undefined(hash_find(uut, (uintptr_t)i))); 56 | } 57 | 58 | for (i = 8; i < 16; ++i) { 59 | CuAssertTrue(tc, value_is_int(hash_find(uut, (uintptr_t)i))); 60 | CuAssertIntEquals(tc, i, (int)value_to_int(hash_find(uut, (uintptr_t)i))); 61 | } 62 | 63 | hash_destroy(uut); 64 | } 65 | 66 | void 67 | hash_test_conflict_insert(CuTest *tc) 68 | { 69 | Hash *uut = hash_new(32); 70 | int i; 71 | 72 | for (i = 0; i < 16 * 32; i += 32) { 73 | CuAssertIntEquals(tc, i / 32, (int)hash_size(uut)); 74 | hash_set(uut, (uintptr_t)i, value_from_int(i)); 75 | } 76 | 77 | for (i = 0; i < 16 * 32; i += 32) { 78 | CuAssertTrue(tc, value_is_int(hash_find(uut, (uintptr_t)i))); 79 | CuAssertIntEquals(tc, i, (int)value_to_int(hash_find(uut, (uintptr_t)i))); 80 | } 81 | 82 | hash_destroy(uut); 83 | } 84 | 85 | void 86 | hash_test_conflict_remove(CuTest *tc) 87 | { 88 | Hash *uut = hash_new(32); 89 | int i; 90 | 91 | for (i = 0; i < 64 * 32; i += 32) { 92 | CuAssertIntEquals(tc, 0, (int)hash_size(uut)); 93 | hash_set(uut, (uintptr_t)i, value_from_int(i)); 94 | CuAssertIntEquals(tc, 1, (int)hash_size(uut)); 95 | 96 | CuAssertTrue(tc, value_is_int(hash_find(uut, (uintptr_t)i))); 97 | CuAssertIntEquals(tc, i, (int)value_to_int(hash_find(uut, (uintptr_t)i))); 98 | 99 | hash_set(uut, (uintptr_t)i, value_undefined()); 100 | CuAssertIntEquals(tc, 0, (int)hash_size(uut)); 101 | CuAssertTrue(tc, value_is_undefined(hash_find(uut, (uintptr_t)i))); 102 | } 103 | 104 | hash_destroy(uut); 105 | } 106 | 107 | void 108 | hash_test_expand(CuTest *tc) 109 | { 110 | Hash *uut = hash_new(32); 111 | int i; 112 | 113 | for (i = 0; i < 16; ++i) { 114 | hash_set(uut, (uintptr_t)i, value_from_int(i)); 115 | } 116 | CuAssertTrue(tc, !hash_need_expand(uut)); 117 | for (i = 16; i < 30; ++i) { 118 | hash_set(uut, (uintptr_t)i, value_from_int(i)); 119 | } 120 | CuAssertTrue(tc, hash_need_expand(uut)); 121 | 122 | Hash *expanded = hash_expand(uut); 123 | 124 | CuAssertTrue(tc, hash_capacity(expanded) > hash_capacity(uut)); 125 | CuAssertTrue(tc, hash_size(expanded) == hash_size(uut)); 126 | 127 | for (i = 30; i < 36; ++i) { 128 | hash_set(expanded, (uintptr_t)i, value_from_int(i)); 129 | 130 | CuAssertTrue(tc, value_is_undefined(hash_find(uut, (uintptr_t)i))); 131 | CuAssertIntEquals(tc, i, (int)value_to_int(hash_find(expanded, (uintptr_t)i))); 132 | } 133 | 134 | hash_destroy(expanded); 135 | hash_destroy(uut); 136 | } 137 | 138 | CuSuite * 139 | hash_test_suite(void) 140 | { 141 | CuSuite *suite = CuSuiteNew(); 142 | 143 | SUITE_ADD_TEST(suite, hash_test_basic_insert); 144 | SUITE_ADD_TEST(suite, hash_test_basic_remove); 145 | SUITE_ADD_TEST(suite, hash_test_conflict_insert); 146 | SUITE_ADD_TEST(suite, hash_test_conflict_remove); 147 | SUITE_ADD_TEST(suite, hash_test_expand); 148 | 149 | return suite; 150 | } -------------------------------------------------------------------------------- /test/ir_builder_test.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/21/16. 3 | // 4 | 5 | #include "CuTest.h" 6 | #include "parse.h" 7 | #include "cvm.h" 8 | #include "inst_output.h" 9 | 10 | void 11 | ir_builder_constant_sharing_test(CuTest *tc) 12 | { 13 | static const char TEST_CONTENT[] = 14 | "let a = 1 + 2 + 3;\n" 15 | "let b = 1 + 2 + 3;\n" 16 | ; 17 | 18 | ParseState *state = parse_state_new_from_string(TEST_CONTENT); 19 | parse(state); 20 | 21 | VMState *vm = cvm_state_new_from_parse_state(state); 22 | cvm_state_run(vm); 23 | 24 | cvm_state_destroy(vm); 25 | parse_state_destroy(state); 26 | } 27 | 28 | void 29 | ir_builder_constant_folding_test(CuTest *tc) 30 | { 31 | static const char TEST_CONTENT[] = 32 | "let a = 1 + 2 + 3;\n" 33 | "let b = 1 + 2 + 3;\n" 34 | ; 35 | 36 | ParseState *state = parse_state_new_from_string(TEST_CONTENT); 37 | parse(state); 38 | 39 | VMState *vm = cvm_state_new_from_parse_state(state); 40 | //output_vm_state(stdout, vm); 41 | cvm_state_run(vm); 42 | 43 | cvm_state_destroy(vm); 44 | parse_state_destroy(state); 45 | } 46 | 47 | CuSuite * 48 | ir_builder_test_suite(void) 49 | { 50 | CuSuite *suite = CuSuiteNew(); 51 | 52 | SUITE_ADD_TEST(suite, ir_builder_constant_sharing_test); 53 | SUITE_ADD_TEST(suite, ir_builder_constant_folding_test); 54 | 55 | return suite; 56 | } 57 | -------------------------------------------------------------------------------- /test/script_test/assert.cr: -------------------------------------------------------------------------------- 1 | let export = {}; 2 | 3 | export.assert = function (cond, msg) { 4 | msg = msg || ''; 5 | if (!cond) { 6 | global.println('Assert Failed! ', msg); 7 | halt; 8 | } 9 | }; 10 | 11 | export.assertTypeof = function (expected, actual, msg) { 12 | msg = msg || ''; 13 | export.assert( 14 | global.typeof(expected) == 'string', 15 | 'typeof expected should be string' 16 | ); 17 | 18 | export.assert( 19 | global.typeof(actual) == expected, 20 | global.concat( 21 | msg, 22 | ' expected type:', 23 | expected, 24 | ' but actual type: ', 25 | global.typeof(actual) 26 | ) 27 | ); 28 | }; 29 | 30 | export.assertIntEq = function (expected, actual, msg) { 31 | msg = msg || ''; 32 | export.assertTypeof('integer', expected, '`expected`'); 33 | export.assertTypeof('integer', actual, '`actual`'); 34 | 35 | export.assert( 36 | expected == actual, 37 | global.concat( 38 | msg, 39 | ' expect: ', 40 | global.to_string(expected), 41 | ' but actual: ', 42 | global.to_string(actual) 43 | ) 44 | ); 45 | }; 46 | 47 | export.assertStringEq = function (expected, actual, msg) { 48 | msg = msg || ''; 49 | export.assertTypeof('string', expected, '`expected`'); 50 | export.assertTypeof('string', actual, '`actual`'); 51 | 52 | export.assert( 53 | expected == actual, 54 | global.concat( 55 | msg, 56 | ' expect: "', 57 | expected, 58 | '" but actual: "', 59 | actual, 60 | '"' 61 | ) 62 | ); 63 | }; 64 | 65 | return export; 66 | -------------------------------------------------------------------------------- /test/script_test/compare.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let n = 1000000; 4 | let a = []; 5 | for (let i = 0; i < n; ++i) { 6 | a.push(Math.random() % n + n); 7 | } 8 | 9 | a.sort(); 10 | 11 | -------------------------------------------------------------------------------- /test/script_test/quicksort.cr: -------------------------------------------------------------------------------- 1 | global.println('Quicksort example'); 2 | global.println('Should output two lines, first is the random array, second is the sorted one'); 3 | 4 | let n = 1000000; 5 | let array = []; 6 | let i = 0; 7 | 8 | while (i < n) { 9 | array[i] = global.random() % n + n; 10 | i = i + 1; 11 | } 12 | 13 | let output_array = function (array) { 14 | global.foreach(array, function (x) { 15 | global.print(x, ' '); 16 | }); 17 | global.println(); 18 | }; 19 | 20 | // output_array(array); 21 | 22 | let export = {}; 23 | 24 | export.quicksort = function (arr, lo, hi, dep) { 25 | if (lo < hi) { 26 | let p = export.partition(arr, lo, hi); 27 | export.quicksort(arr, lo, p - 1, dep + 1); 28 | export.quicksort(arr, p + 1, hi, dep + 1); 29 | } 30 | }; 31 | 32 | export.partition = function (arr, lo, hi) { 33 | let pivot = arr[hi]; 34 | let i = lo; 35 | let j = lo; 36 | while (j < hi) { 37 | if (arr[j] <= pivot) { 38 | let t = arr[i]; 39 | arr[i] = arr[j]; 40 | arr[j] = t; 41 | i = i + 1; 42 | } 43 | j = j + 1; 44 | } 45 | let t = arr[i]; 46 | arr[i] = arr[hi]; 47 | arr[hi] = t; 48 | return i; 49 | }; 50 | 51 | export.quicksort(array, 0, n - 1, 0); 52 | // output_array(array); 53 | -------------------------------------------------------------------------------- /test/script_test/test.cr: -------------------------------------------------------------------------------- 1 | global.println('Foreach example'); 2 | global.println('shoule print a line with 0 to 99'); 3 | 4 | let i = 0; 5 | let a = []; 6 | while (i < 100) { 7 | a[i] = i; 8 | i = i + 1; 9 | } 10 | 11 | global.foreach(a, function (x) { 12 | global.print(x, ' '); 13 | }); 14 | global.println(); 15 | -------------------------------------------------------------------------------- /test/script_test/test_all.cr: -------------------------------------------------------------------------------- 1 | global.import('test_concat.cr'); 2 | global.import('test_equal.cr'); 3 | global.import('test_oop.cr'); 4 | global.import('test_parse_number.cr'); 5 | global.import('test_sizeof.cr'); 6 | global.import('test_typeof.cr'); 7 | global.import('test_quicksort.cr'); 8 | -------------------------------------------------------------------------------- /test/script_test/test_call_lightfunc.cr: -------------------------------------------------------------------------------- 1 | global.println('Test use light function as callback'); 2 | global.println('should println 0 - 9'); 3 | 4 | let a = [], 5 | i = 0; 6 | while (i < 10) { 7 | a[i] = i; 8 | i = i + 1; 9 | } 10 | 11 | global.foreach(a, global.println); 12 | -------------------------------------------------------------------------------- /test/script_test/test_concat.cr: -------------------------------------------------------------------------------- 1 | global.print('Test concat ...'); 2 | 3 | let assert = global.import('assert.cr'); 4 | 5 | let a = 'hello'; 6 | let b = 'world'; 7 | 8 | let result = global.concat(a, ' ', b); 9 | assert.assertStringEq('hello world', result); 10 | 11 | global.println('Done'); 12 | -------------------------------------------------------------------------------- /test/script_test/test_equal.cr: -------------------------------------------------------------------------------- 1 | global.print("Test equal ..."); 2 | 3 | let assert = global.import('assert.cr'); 4 | 5 | assert.assert(1 == 1); 6 | assert.assert("test" == "test"); 7 | assert.assert(global.undefined == global.undefined); 8 | 9 | let a = {}; 10 | assert.assert(a == a); 11 | assert.assert(global.undefined == a.a); 12 | 13 | let b = []; 14 | assert.assert(b == b); 15 | assert.assert(global.undefined == b[0]); 16 | 17 | let c = a; 18 | assert.assert(a == c); 19 | 20 | global.println('Done'); 21 | -------------------------------------------------------------------------------- /test/script_test/test_import.cr: -------------------------------------------------------------------------------- 1 | 2 | let Imported = global.import('test_imported.cr'); 3 | 4 | Imported.hello(); 5 | Imported.export2.hello2(); 6 | -------------------------------------------------------------------------------- /test/script_test/test_imported.cr: -------------------------------------------------------------------------------- 1 | let export = {}; 2 | 3 | export.hello = function () { 4 | global.println('Hello Import'); 5 | }; 6 | 7 | export.export2 = global.import('test_imported2.cr'); 8 | 9 | return export; 10 | -------------------------------------------------------------------------------- /test/script_test/test_imported2.cr: -------------------------------------------------------------------------------- 1 | let export = {}; 2 | 3 | export.hello2 = function () { 4 | global.println('Hello world!'); 5 | }; 6 | 7 | return export; 8 | -------------------------------------------------------------------------------- /test/script_test/test_oop.cr: -------------------------------------------------------------------------------- 1 | global.print('Test OOP... '); 2 | 3 | let Student = function (name, age) { 4 | let ret = {}; 5 | ret.name = name; 6 | ret.age = age; 7 | 8 | ret.getName = function () { 9 | return this.name; 10 | }; 11 | 12 | ret.getAge = function () { 13 | return this.age; 14 | }; 15 | 16 | return ret; 17 | }; 18 | 19 | let student = Student('Clarkok Zhang', 20); 20 | 21 | let assert = global.import('assert.cr'); 22 | 23 | assert.assertStringEq('Clarkok Zhang', student.getName()); 24 | assert.assertIntEq(20, student.getAge()); 25 | 26 | global.println('Done'); 27 | -------------------------------------------------------------------------------- /test/script_test/test_parse_number.cr: -------------------------------------------------------------------------------- 1 | global.print('Test parse_number... '); 2 | 3 | let assert = global.import('assert.cr'); 4 | 5 | let i = 0; 6 | while (i < 10) { 7 | let s = global.to_string(i); 8 | let num = global.parse_number(s); 9 | assert.assertIntEq(i, num, 'i should equal to num'); 10 | 11 | i = i + 1; 12 | } 13 | 14 | global.println('Done'); 15 | -------------------------------------------------------------------------------- /test/script_test/test_quicksort.cr: -------------------------------------------------------------------------------- 1 | global.print('Test quicksort... '); 2 | 3 | let n = 1000; 4 | let array = []; 5 | let i = 0; 6 | 7 | let count = []; 8 | 9 | while (i < n) { 10 | array[i] = global.random() % n + n; 11 | count[array[i]] = (count[array[i]] || 0) + 1; 12 | i = i + 1; 13 | } 14 | 15 | let export = {}; 16 | 17 | export.quicksort = function (arr, lo, hi, dep) { 18 | if (lo < hi) { 19 | let p = export.partition(arr, lo, hi); 20 | export.quicksort(arr, lo, p - 1, dep + 1); 21 | export.quicksort(arr, p + 1, hi, dep + 1); 22 | } 23 | }; 24 | 25 | export.partition = function (arr, lo, hi) { 26 | let pivot = arr[hi]; 27 | let i = lo; 28 | let j = lo; 29 | while (j < hi) { 30 | if (arr[j] <= pivot) { 31 | let t = arr[i]; 32 | arr[i] = arr[j]; 33 | arr[j] = t; 34 | i = i + 1; 35 | } 36 | j = j + 1; 37 | } 38 | let t = arr[i]; 39 | arr[i] = arr[hi]; 40 | arr[hi] = t; 41 | return i; 42 | }; 43 | 44 | export.quicksort(array, 0, n - 1, 0); 45 | 46 | let assert = global.import('assert.cr'); 47 | i = 1; 48 | assert.assert(count[array[0]]); 49 | count[array[0]] = count[array[0]] - 1; 50 | while (i < n) { 51 | assert.assert(array[i - 1] <= array[i], global.concat('error on ', global.to_string(i))); 52 | assert.assert(count[array[i]]); 53 | count[array[i]] = count[array[i]] - 1; 54 | i = i + 1; 55 | } 56 | 57 | i = 0; 58 | while (i < n) { 59 | assert.assert(!count[array[i]], global.concat('number ', global.to_string(array[i]), ' is ', global.to_string(i))); 60 | i = i + 1; 61 | } 62 | 63 | global.println('Done'); 64 | -------------------------------------------------------------------------------- /test/script_test/test_sizeof.cr: -------------------------------------------------------------------------------- 1 | global.print('Test sizeof... '); 2 | 3 | let sizeof = global.sizeof; 4 | let assert = global.import('assert.cr'); 5 | 6 | assert.assertIntEq(-1, sizeof(1)); 7 | assert.assertIntEq(5, sizeof("12345")); 8 | assert.assertIntEq(0, sizeof({})); 9 | assert.assertIntEq(0, sizeof([])); 10 | assert.assertIntEq(3, sizeof({a : 1, b : 2, c : 3})); 11 | assert.assertIntEq(5, sizeof([1, 2, 3, 4, 5])); 12 | assert.assertIntEq(-1, sizeof(function () {})); 13 | assert.assertIntEq(-1, sizeof(global.println)); 14 | 15 | global.println('Done'); 16 | -------------------------------------------------------------------------------- /test/script_test/test_typeof.cr: -------------------------------------------------------------------------------- 1 | global.print('Test typeof ...'); 2 | 3 | let assert = global.import('assert.cr'); 4 | 5 | let int = 1; 6 | let str = 'string'; 7 | let obj = {}; 8 | let arr = []; 9 | let clos = function () {}; 10 | let func = global.print; 11 | 12 | assert.assertStringEq('integer', global.typeof(int)); 13 | assert.assertStringEq('string', global.typeof(str)); 14 | assert.assertStringEq('object', global.typeof(obj)); 15 | assert.assertStringEq('array', global.typeof(arr)); 16 | assert.assertStringEq('closure', global.typeof(clos)); 17 | assert.assertStringEq('light_function', global.typeof(func)); 18 | 19 | global.println('Done'); 20 | -------------------------------------------------------------------------------- /test/test_main.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/5/16. 3 | // 4 | 5 | #include 6 | 7 | #include "CuTest.h" 8 | 9 | CuSuite *cvm_test_suite(); 10 | CuSuite *hash_test_suite(); 11 | CuSuite *cstring_test_suite(); 12 | CuSuite *parse_test_suite(); 13 | CuSuite *young_gen_test_suite(); 14 | CuSuite *code_test_suite(); 15 | CuSuite *ir_builder_test_suite(); 16 | 17 | int 18 | main() 19 | { 20 | CuString *output = CuStringNew(); 21 | CuSuite *suite = CuSuiteNew(); 22 | 23 | CuSuiteAddSuite(suite, cvm_test_suite()); 24 | CuSuiteAddSuite(suite, hash_test_suite()); 25 | CuSuiteAddSuite(suite, cstring_test_suite()); 26 | CuSuiteAddSuite(suite, parse_test_suite()); 27 | CuSuiteAddSuite(suite, young_gen_test_suite()); 28 | CuSuiteAddSuite(suite, code_test_suite()); 29 | CuSuiteAddSuite(suite, ir_builder_test_suite()); 30 | 31 | CuSuiteRun(suite); 32 | CuSuiteSummary(suite, output); 33 | CuSuiteDetails(suite, output); 34 | 35 | printf("%s\n", output->buffer); 36 | 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /test/young_gen_test.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by c on 4/18/16. 3 | // 4 | 5 | #include "CuTest.h" 6 | 7 | #include "young_gen.h" 8 | 9 | void 10 | young_gen_basic_test(CuTest *tc) 11 | { 12 | YoungGen *young_gen = young_gen_new(); 13 | 14 | Hash *root = young_gen_new_hash(young_gen, HASH_MIN_CAPACITY, HT_OBJECT); 15 | 16 | for (int i = 0; i < 8; ++i) { 17 | Hash *child = young_gen_new_hash(young_gen, HASH_MIN_CAPACITY, HT_OBJECT); 18 | hash_set(root, (uintptr_t)i, value_from_ptr(child)); 19 | } 20 | 21 | size_t heap_size = young_gen_heap_size(young_gen); 22 | Hash *root_old = root; 23 | 24 | for (int i = 0; i < 8; ++i) { 25 | hash_set(root, (uintptr_t)i, value_null()); 26 | } 27 | 28 | young_gen_gc_start(young_gen); 29 | young_gen_gc_mark(young_gen, &root); 30 | young_gen_gc_end(young_gen); 31 | 32 | CuAssertTrue(tc, heap_size > young_gen_heap_size(young_gen)); 33 | CuAssertTrue(tc, root_old != root); 34 | 35 | young_gen_destroy(young_gen); 36 | } 37 | 38 | void 39 | young_gen_data_after_gc_test(CuTest *tc) 40 | { 41 | YoungGen *young_gen = young_gen_new(); 42 | 43 | Hash *root = young_gen_new_hash(young_gen, HASH_MIN_CAPACITY, HT_OBJECT); 44 | 45 | for (int i = 0; i < 8; ++i) { 46 | hash_set(root, (uintptr_t)i, value_from_int(i)); 47 | } 48 | 49 | young_gen_gc_start(young_gen); 50 | young_gen_gc_mark(young_gen, &root); 51 | young_gen_gc_end(young_gen); 52 | 53 | for (int i = 0; i < 8; ++i) { 54 | CuAssertIntEquals(tc, i, value_to_int(hash_find(root, (uintptr_t)i))); 55 | } 56 | 57 | young_gen_destroy(young_gen); 58 | } 59 | 60 | CuSuite * 61 | young_gen_test_suite(void) 62 | { 63 | CuSuite *suite = CuSuiteNew(); 64 | 65 | SUITE_ADD_TEST(suite, young_gen_basic_test); 66 | SUITE_ADD_TEST(suite, young_gen_data_after_gc_test); 67 | 68 | return suite; 69 | } 70 | --------------------------------------------------------------------------------