From c8b2faed3886f4d7e0ca1b313bfad437c91ce94d Mon Sep 17 00:00:00 2001 From: Mohanson Date: Wed, 1 Nov 2023 13:16:51 +0800 Subject: [PATCH] Refactor argument parser using cmdopt (#10) * Refactor by cmdopt --- Makefile | 5 +- include/c-stdlib/src/printf_impl.c | 7 + quickjs/ckb_module.c | 14 ++ quickjs/ckb_module.h | 1 + quickjs/cmdopt.c | 363 +++++++++++++++++++++++++++++ quickjs/cmdopt.h | 52 +++++ quickjs/qjs.c | 195 ++++++++-------- tests/ckb_js_tests/Makefile | 6 +- 8 files changed, 542 insertions(+), 101 deletions(-) create mode 100644 quickjs/cmdopt.c create mode 100644 quickjs/cmdopt.h diff --git a/Makefile b/Makefile index b8353e2..a21ce11 100644 --- a/Makefile +++ b/Makefile @@ -40,7 +40,8 @@ LDFLAGS += -Ldeps/compiler-rt-builtins-riscv/build -lcompiler-rt OBJDIR=build QJS_OBJS=$(OBJDIR)/qjs.o $(OBJDIR)/quickjs.o $(OBJDIR)/libregexp.o $(OBJDIR)/libunicode.o \ - $(OBJDIR)/cutils.o $(OBJDIR)/mocked.o $(OBJDIR)/std_module.o $(OBJDIR)/ckb_module.o $(OBJDIR)/ckb_cell_fs.o $(OBJDIR)/libbf.o + $(OBJDIR)/cutils.o $(OBJDIR)/mocked.o $(OBJDIR)/std_module.o $(OBJDIR)/ckb_module.o $(OBJDIR)/ckb_cell_fs.o \ + $(OBJDIR)/libbf.o $(OBJDIR)/cmdopt.o STD_OBJS=$(OBJDIR)/string_impl.o $(OBJDIR)/malloc_impl.o $(OBJDIR)/math_impl.o \ $(OBJDIR)/math_log_impl.o $(OBJDIR)/math_pow_impl.o $(OBJDIR)/printf_impl.o $(OBJDIR)/stdio_impl.o \ @@ -81,7 +82,7 @@ test: benchmark: make -f tests/benchmark/Makefile - + clean: rm -f build/*.o rm -f build/ckb-js-vm diff --git a/include/c-stdlib/src/printf_impl.c b/include/c-stdlib/src/printf_impl.c index 727803e..e293b13 100644 --- a/include/c-stdlib/src/printf_impl.c +++ b/include/c-stdlib/src/printf_impl.c @@ -1011,3 +1011,10 @@ int ckb_printf(const char *format, ...) { ckb_debug(buf); return ret; } + +int ckb_vprintf(const char *format, va_list va) { + static char buf[CKB_C_STDLIB_PRINTF_BUFFER_SIZE]; + int ret = vsnprintf(buf, CKB_C_STDLIB_PRINTF_BUFFER_SIZE, format, va); + ckb_debug(buf); + return ret; +} diff --git a/quickjs/ckb_module.c b/quickjs/ckb_module.c index 2507eb7..73bf8e1 100644 --- a/quickjs/ckb_module.c +++ b/quickjs/ckb_module.c @@ -577,6 +577,20 @@ int js_init_module_ckb(JSContext *ctx) { #define JS_LOADER_ARGS_SIZE 2 #define BLAKE2B_BLOCK_SIZE 32 +int load_cell_code_info_explicit(size_t *buf_size, size_t *index, const uint8_t* code_hash, uint8_t hash_type) { + int err = 0; + *index = 0; + err = ckb_look_for_dep_with_hash2(code_hash, hash_type, index); + CHECK(err); + + *buf_size = 0; + err = ckb_load_cell_data(NULL, buf_size, 0, *index, CKB_SOURCE_CELL_DEP); + CHECK(err); + CHECK2(*buf_size > 0, -1); +exit: + return err; +} + int load_cell_code_info(size_t *buf_size, size_t *index) { int err = 0; unsigned char script[SCRIPT_SIZE]; diff --git a/quickjs/ckb_module.h b/quickjs/ckb_module.h index d4caf4f..cad861b 100644 --- a/quickjs/ckb_module.h +++ b/quickjs/ckb_module.h @@ -10,6 +10,7 @@ int js_init_module_ckb(JSContext *ctx); int read_local_file(char *buf, int size); +int load_cell_code_info_explicit(size_t *buf_size, size_t *index, const uint8_t* code_hash, uint8_t hash_type); int load_cell_code_info(size_t *buf_size, size_t *index); int load_cell_code(size_t buf_size, size_t index, uint8_t *buf); diff --git a/quickjs/cmdopt.c b/quickjs/cmdopt.c new file mode 100644 index 0000000..bcfc20c --- /dev/null +++ b/quickjs/cmdopt.c @@ -0,0 +1,363 @@ +/* + * Copied from https://bellard.org/nncp/nncp-2023-10-21.tar.gz + */ + +/* + * Yet another command line option parser + * + * Copyright (c) 2021 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include "my_stdlib.h" +#include +#include "my_stdio.h" +#include +#include +#include "my_string.h" +#include "my_assert.h" +#include "cmdopt.h" + +typedef struct { + const CMDOptDesc *desc; + const char *optarg; /* NULL if no argument */ +} CMDOpt; + +struct CMDOption { + int desc_count; + int opt_count; + int opt_size; + CMDOpt *opt_tab; + + const CMDOptDesc *desc_tab[16]; +}; + +static const char *cmd_prog_name; + +int ckb_vprintf(const char *format, va_list va); +void cmd_error(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + printf("%s:", cmd_prog_name); + ckb_vprintf(fmt, ap); + exit(1); +} + +static const CMDOptDesc *find_opt(CMDOption *s, const char *opt) +{ + const CMDOptDesc *d; + int i; + size_t opt_len, len; + const char *p, *r; + + opt_len = strlen(opt); + for(i = 0; i < s->desc_count; i++) { + for(d = s->desc_tab[i]; d->opt != NULL; d++) { + p = d->opt; + for(;;) { + r = strchr(p, ','); + if (r) + len = r - p; + else + len = strlen(p); + if (len == opt_len && !memcmp(p, opt, opt_len)) + return d; + if (!r) + break; + p = r + 1; + } + } + } + return NULL; +} + +static void add_opt(CMDOption *s, const CMDOptDesc *d, + const char *optarg) +{ + int new_size; + CMDOpt *cs; + + if ((s->opt_count + 1) > s->opt_size) { + new_size = max_int(4, max_int(s->opt_count + 1, + s->opt_size + s->opt_size / 2)); + s->opt_tab = realloc(s->opt_tab, new_size * sizeof(s->opt_tab[0])); + s->opt_size = new_size; + } + cs = &s->opt_tab[s->opt_count++]; + cs->desc = d; + cs->optarg = optarg; +} + +/* See the reason: https://reviews.llvm.org/D103009 */ +#pragma clang optimize off +CMDOption *cmdopt_init(const char *prog_name) +{ + CMDOption *s; + + cmd_prog_name = prog_name; + s = malloc(sizeof(*s)); + memset(s, 0, sizeof(*s)); + return s; +} +#pragma clang optimize on + +void cmdopt_add_desc(CMDOption *s, const CMDOptDesc *desc) +{ + if (s->desc_count >= countof(s->desc_tab)) + cmd_error("too many cmd desc"); + s->desc_tab[s->desc_count++] = desc; +} + +int cmdopt_parse(CMDOption *s, int argc, const char **argv) +{ + const char *arg, *optarg; + const char **param; + char optbuf[2]; + const CMDOptDesc *d; + int optind, c, param_len, i, arg_pos; + + param = malloc(sizeof(param[0]) * argc); + param_len = 0; + arg_pos = 0; + for(optind = 0; optind < argc; ) { + arg = argv[optind]; + if (*arg != '-') { + param[param_len++] = arg; + optind++; + } else { + argv[arg_pos++] = arg; + optind++; + arg++; + if (*arg == '-') { + arg++; + if (*arg == '\0') { + /* '--' stops option parsing */ + while (optind < argc) { + param[param_len++] = argv[optind++]; + } + break; + } + if (strlen(arg) > 1) + d = find_opt(s, arg); + else + d = NULL; + if (!d) + cmd_error("unknown option: '--%s'", arg); + if (d->flags & CMD_HAS_ARG) { + if (optind >= argc) + cmd_error("option '--%s' must have an argument", arg); + optarg = argv[optind++]; + argv[arg_pos++] = optarg; + } else { + optarg = NULL; + } + add_opt(s, d, optarg); + } else { + while (*arg != '\0') { + c = *arg++; + optbuf[0] = c; + optbuf[1] = '\0'; + d = find_opt(s, optbuf); + if (!d) + cmd_error("unknown option: '-%c'", c); + if (d->flags & CMD_HAS_ARG) { + /* arguments expected */ + if (*arg != '\0') { + optarg = arg; + } else { + if (optind >= argc) + cmd_error("option '-%c' must have an argument", c); + optarg = argv[optind++]; + argv[arg_pos++] = optarg; + } + } else { + optarg = NULL; + } + add_opt(s, d, optarg); + } + } + } + } + + /* put the parameters after the options */ + optind = arg_pos; + for(i = 0; i < param_len; i++) + argv[arg_pos++] = param[i]; + assert(arg_pos == argc); + free(param); +#if 0 + { + printf("reorderd argv: "); + for(i = 0; i < argc; i++) { + if (i == optind) + printf(" |"); + printf(" %s", argv[i]); + } + printf("\n"); + } +#endif + return optind; +} + +void cmdopt_show_desc(const CMDOptDesc *desc) +{ + const CMDOptDesc *d; + int col, pos, opt_width; + size_t len; + const char *p, *r; + + opt_width = 24; + for(d = desc; d->opt != NULL; d++) { + char l[256] = {0}; + col = 0; + p = d->opt; + for(;;) { + r = strchr(p, ','); + if (r) + len = r - p; + else + len = strlen(p); + if (p != d->opt) { + strcat(l, " "); + col++; + } + strcat(l, "-"); + if (len > 1) { + strcat(l, "-"); + col++; + } + memcpy(&l[col+1], p, len); + col += len + 1; + + if (!r) + break; + p = r + 1; + } + + if (d->flags & CMD_HAS_ARG) { + if (d->arg_desc) { + strcat(l, " "); + strcat(l, d->arg_desc); + col += 1 + strlen(d->arg_desc); + } else { + strcat(l, " arg"); + col += 4; + } + } + if (col < opt_width) { + pos = opt_width; + } else { + pos = ((col - opt_width + 8) & ~7) + opt_width; + } + while (col < pos) { + strcat(l, " "); + col++; + } + strcat(l, d->desc); + printf("%s", l); + } +} + +static const char *cmdopt_get_internal(CMDOption *s, const char *opt, + BOOL has_arg) +{ + const CMDOptDesc *d; + int i; + + d = find_opt(s, opt); + if (!d) { + cmd_error("option '-%s%s' does not exist", + strlen(opt) > 1 ? "-" : "", opt); + } + + /* check the argument consistency */ + if (((d->flags & CMD_HAS_ARG) != 0) != has_arg) { + if (d->flags & CMD_HAS_ARG) { + cmd_error("option '-%s%s' has an argument", + strlen(opt) > 1 ? "-" : "", opt); + } else { + cmd_error("option '-%s%s' does not have an argument", + strlen(opt) > 1 ? "-" : "", opt); + } + } + + /* the last option is used */ + for(i = s->opt_count - 1; i >= 0; i--) { + CMDOpt *cs = &s->opt_tab[i]; + if (cs->desc == d) { + if (has_arg) { + return cs->optarg; + } else { + return ""; + } + } + } + return NULL; +} + +const char *cmdopt_get(CMDOption *s, const char *opt) +{ + return cmdopt_get_internal(s, opt, TRUE); +} + +BOOL cmdopt_has(CMDOption *s, const char *opt) +{ + return (cmdopt_get_internal(s, opt, FALSE) != NULL); +} + +int cmdopt_get_int(CMDOption *s, const char *opt, int def_val) +{ + const char *str, *p; + double d; + int val; + + str = cmdopt_get(s, opt); + if (!str) + return def_val; + d = strtod(str, (char **)&p); + val = (int)d; + if (*p != '\0' || d != (double)val) + cmd_error("option -%s%s expects an integer", + strlen(opt) > 1 ? "-" : "", opt); + return val; +} + +float cmdopt_get_float(CMDOption *s, const char *opt, float def_val) +{ + const char *str, *p; + float val; + + str = cmdopt_get(s, opt); + if (!str) + return def_val; + val = strtod(str, (char **)&p); + if (*p != '\0') + cmd_error("option -%s%s expects a floating point value", + strlen(opt) > 1 ? "-" : "", opt); + return val; +} + +void cmdopt_free(CMDOption *s) +{ + free(s->opt_tab); + free(s); +} diff --git a/quickjs/cmdopt.h b/quickjs/cmdopt.h new file mode 100644 index 0000000..ed443fc --- /dev/null +++ b/quickjs/cmdopt.h @@ -0,0 +1,52 @@ +/* + * Yet another command line option parser + * + * Copyright (c) 2021 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef CMDOPT_H +#define CMDOPT_H + +#include "cutils.h" + +typedef struct CMDOption CMDOption; + +#define CMD_HAS_ARG (1 << 0) + +typedef struct { + const char *opt; + int flags; + const char *desc; + const char *arg_desc; +} CMDOptDesc; + +void __attribute__((noreturn, format(printf, 1, 2))) cmd_error(const char *fmt, ...); +CMDOption *cmdopt_init(const char *prog_name); +void cmdopt_add_desc(CMDOption *s, const CMDOptDesc *desc); +int cmdopt_parse(CMDOption *s, int argc, const char **argv); +void cmdopt_free(CMDOption *s); + +void cmdopt_show_desc(const CMDOptDesc *desc); +const char *cmdopt_get(CMDOption *s, const char *opt); +BOOL cmdopt_has(CMDOption *s, const char *opt); +int cmdopt_get_int(CMDOption *s, const char *opt, int def_val); +float cmdopt_get_float(CMDOption *s, const char *opt, float def_val); + +#endif /* CMDOPT_H */ diff --git a/quickjs/qjs.c b/quickjs/qjs.c index 775eea5..d2c5188 100644 --- a/quickjs/qjs.c +++ b/quickjs/qjs.c @@ -35,60 +35,11 @@ #include "std_module.h" #include "ckb_module.h" #include "ckb_exec.h" +#include "cmdopt.h" #define MAIN_FILE_NAME "main.js" #define MAIN_FILE_NAME_BC "main.bc" -typedef enum { - RunJsError = 0, - RunJsWithCode, - RunJsWithFile, - RunJsWithFileSystem, - - RunJsWithDbgFile, - RunJsWithDbgFileSystem, - - CompileWithFile, -} RunJSType; - -static RunJSType parse_args(int argc, const char **argv) { - bool has_r = false; - bool has_f = false; - bool has_e = false; - bool has_c = false; - - for (int i = 0; i < argc; i++) { - if (strcmp(argv[i], "-r") == 0) { - has_r = true; - } else if (strcmp(argv[i], "-f") == 0) { - has_f = true; - } else if (strcmp(argv[i], "-e") == 0) { - has_e = true; - } else if (strcmp(argv[i], "-c") == 0) { - has_c = true; - } - } - - if (has_c) { - return CompileWithFile; - } else if (has_e) { - if (argc < 2 || argv[1] == NULL) return RunJsError; - if (has_r || has_f) - return RunJsError; - else - return RunJsWithCode; - } else if (has_r) { - if (has_f) - return RunJsWithDbgFileSystem; - else - return RunJsWithDbgFile; - } else if (has_f) { - return RunJsWithFileSystem; - } else { - return RunJsWithFile; - } -} - static void js_dump_obj(JSContext *ctx, JSValueConst val) { const char *str; @@ -260,17 +211,59 @@ static int run_from_cell_data(JSContext *ctx, bool enable_fs) { return err; } - char buf[buf_size + 1]; + char *buf = malloc(buf_size + 1); err = load_cell_code(buf_size, index, (uint8_t *)buf); if (err) { return err; } if (enable_fs) { - return run_from_file_system_buf(ctx, buf, buf_size); + err = run_from_file_system_buf(ctx, buf, buf_size); + free(buf); + return err; } else { buf[buf_size] = 0; - return eval_buf(ctx, buf, buf_size, "", 0); + err = eval_buf(ctx, buf, buf_size, "", 0); + free(buf); + return err; + } +} + +static int run_from_target(JSContext *ctx, const char *target, bool enable_fs) { + if (strlen(target) < 66) { + return -1; + } + + uint8_t target_byte[33] = {}; + uint32_t length = 0; + _exec_hex2bin(target, target_byte, 33, &length); + uint8_t *code_hash = target_byte; + uint8_t hash_type = target_byte[32]; + + int err = 0; + size_t buf_size = 0; + size_t index = 0; + + err = load_cell_code_info_explicit(&buf_size, &index, code_hash, hash_type); + if (err) { + return err; + } + + char *buf = malloc(buf_size + 1); + err = load_cell_code(buf_size, index, (uint8_t *)buf); + if (err) { + return err; + } + + if (enable_fs) { + err = run_from_file_system_buf(ctx, buf, buf_size); + free(buf); + return err; + } else { + buf[buf_size] = 0; + err = eval_buf(ctx, buf, buf_size, "", 0); + free(buf); + return err; } } @@ -286,18 +279,36 @@ static JSContext *JS_NewCustomContext(JSRuntime *rt) { return ctx; } +static const CMDOptDesc js_vm_options[] = { + { "h,help", 0, "show the help" }, + { "c", 0, "compile javascript to bytecode" }, + { "e", CMD_HAS_ARG, "run javascript from argument value" }, + { "r", 0, "read from file" }, + { "t", CMD_HAS_ARG, "specify target code_hash and hash_type in hex" }, + { "f", 0, "use file system" }, + { NULL }, +}; + int main(int argc, const char **argv) { + int optind; + CMDOption *co; + co = cmdopt_init("ckb-js-vm"); + cmdopt_add_desc(co, js_vm_options); + optind = cmdopt_parse(co, argc, argv); + if (optind > argc) { + cmdopt_show_desc(js_vm_options); + exit(1); + } + if (cmdopt_has(co, "help")) { + cmdopt_show_desc(js_vm_options); + exit(1); + } + int err = 0; JSRuntime *rt = NULL; JSContext *ctx = NULL; size_t memory_limit = 0; size_t stack_size = 1024 * 1020; - size_t optind = 1; - RunJSType type = parse_args(argc, argv); - if (type == RunJsError) { - printf("ckb-js: args failed"); - return -1; - } rt = JS_NewRuntime(); if (!rt) { printf("qjs: cannot allocate JS runtime\n"); @@ -312,48 +323,36 @@ int main(int argc, const char **argv) { CHECK2(ctx != NULL, -1); /* loader for ES6 modules */ JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL); - js_std_add_helpers(ctx, argc - optind, argv + optind); + js_std_add_helpers(ctx, argc - optind, &argv[optind]); err = js_init_module_ckb(ctx); CHECK(err); - switch (type) { - case RunJsWithCode: - err = eval_buf(ctx, argv[1], strlen(argv[1]), "", 0); - if (err == 0) { - js_std_loop(ctx); - } - break; - case RunJsWithFile: - err = run_from_cell_data(ctx, false); - if (err == 0) { - js_std_loop(ctx); - } - break; - case RunJsWithFileSystem: - err = run_from_cell_data(ctx, true); - if (err == 0) { - js_std_loop(ctx); - } - break; - case RunJsWithDbgFile: - err = run_from_local_file(ctx, false); - if (err == 0) { - js_std_loop(ctx); - } - break; - case RunJsWithDbgFileSystem: - err = run_from_local_file(ctx, true); - if (err == 0) { - js_std_loop(ctx); - } - break; - case CompileWithFile: - JS_SetModuleLoaderFunc(rt, NULL, js_module_dummy_loader, NULL); - err = compile_from_file(ctx); - break; - default: - printf("unknow type: %d", type); - return -1; + bool c_bool = cmdopt_has(co, "c"); + const char *e_data = cmdopt_get(co, "e"); + bool r_bool = cmdopt_has(co, "r"); + bool f_bool = cmdopt_has(co, "f"); + const char *t_data = cmdopt_get(co, "t"); + + if (c_bool) { + JS_SetModuleLoaderFunc(rt, NULL, js_module_dummy_loader, NULL); + err = compile_from_file(ctx); + } else if (e_data) { + err = eval_buf(ctx, e_data, strlen(e_data), "", 0); + } else if (r_bool && f_bool) { + err = run_from_local_file(ctx, true); + } else if (r_bool) { + err = run_from_local_file(ctx, false); + } else if (t_data && f_bool) { + err = run_from_target(ctx, t_data, true); + } else if (t_data) { + err = run_from_target(ctx, t_data, false); + } else if (f_bool) { + err = run_from_cell_data(ctx, true); + } else { + err = run_from_cell_data(ctx, false); + } + if (err == 0) { + js_std_loop(ctx); } CHECK(err); diff --git a/tests/ckb_js_tests/Makefile b/tests/ckb_js_tests/Makefile index 232f457..3a498d8 100644 --- a/tests/ckb_js_tests/Makefile +++ b/tests/ckb_js_tests/Makefile @@ -44,7 +44,11 @@ fs_mount: simple_udt: cargo run --bin simple_udt | $(CKB_DEBUGGER) --tx-file=- --script-group-type type --cell-type output --cell-index 0 - + +cell_target: + echo "blake2b(../../basic/test_loop.js) == 91bc1d1fc8c19289e72d15004dd506b76246bfead13391a5b35179cf7c8f37ef" + cargo run --bin default_by_cell | $(CKB_DEBUGGER) -s lock --tx-file=- -- -t 91bc1d1fc8c19289e72d15004dd506b76246bfead13391a5b35179cf7c8f37ef01 + install-lua: sudo apt install lua5.4