diff --git a/.ci-scripts/ci-build-linux.sh b/.ci-scripts/ci-build-linux.sh index 2b9e0fc..59001ba 100644 --- a/.ci-scripts/ci-build-linux.sh +++ b/.ci-scripts/ci-build-linux.sh @@ -28,7 +28,7 @@ rm -rf "rizin-*" cd "$CI_JSDEC" # build jsdec and install in the rizin dir. -meson setup --buildtype=release -Dstandalone=false build +meson setup --buildtype=release -Dbuild_type=rizin build sudo ninja -C build install # check if it was installed correctly and try to run it. diff --git a/.reuse/dep5 b/.reuse/dep5 index 84b9d09..63c5abd 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -46,3 +46,7 @@ License: BSD-3-Clause Files: .clang-format Copyright: 2021 Giovanni Dante Grazioli License: BSD-3-Clause + +Files: cutter-plugin/CMakeLists.txt +Copyright: 2024 Giovanni Dante Grazioli +License: BSD-3-Clause diff --git a/c/jsdec-cutter.c b/c/jsdec-cutter.c new file mode 100644 index 0000000..ac6e5a4 --- /dev/null +++ b/c/jsdec-cutter.c @@ -0,0 +1,359 @@ +// SPDX-FileCopyrightText: 2024 Giovanni Dante Grazioli +// SPDX-License-Identifier: BSD-3-Clause + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "jsdec.h" +#include "jsdec-cutter.h" + +typedef struct rz_core_t RzCore; + +typedef struct exec_context_t { + RzCore *core; + void *bed; + RzStrBuf *anno; + JSValue shared; +} ExecContext; + +static JSValue js_analysis_bytes(JSContext *ctx, RzCore *core, RzAnalysisBytes *ab) { + RzAnalysisOp *aop = ab->op; + JSValue op = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, op, "offset", JS_NewBigUint64(ctx, aop->addr)); + if (aop->ptr != UT64_MAX) { + JS_SetPropertyStr(ctx, op, "ptr", JS_NewBigUint64(ctx, aop->ptr)); + } + if (aop->val != UT64_MAX) { + JS_SetPropertyStr(ctx, op, "val", JS_NewBigUint64(ctx, aop->val)); + } + JS_SetPropertyStr(ctx, op, "opcode", JS_NewString(ctx, rz_str_get_null(ab->opcode))); + JS_SetPropertyStr(ctx, op, "disasm", JS_NewString(ctx, rz_str_get_null(ab->disasm))); + JS_SetPropertyStr(ctx, op, "type", JS_NewString(ctx, rz_analysis_optype_to_string(aop->type))); + if (aop->jump != UT64_MAX) { + JS_SetPropertyStr(ctx, op, "jump", JS_NewBigInt64(ctx, aop->jump)); + } + if (aop->fail != UT64_MAX) { + JS_SetPropertyStr(ctx, op, "fail", JS_NewBigUint64(ctx, aop->fail)); + } + const char *comment = rz_meta_get_string(core->analysis, RZ_META_TYPE_COMMENT, aop->addr); + if (RZ_STR_ISNOTEMPTY(comment)) { + JS_SetPropertyStr(ctx, op, "comment", JS_NewString(ctx, comment)); + } + return op; +} + +static JSValue js_analysis_opcodes(JSContext *ctx, RzCore *core) { + RzAnalysisBytes *ab; + JSValue ops = JS_NewArray(ctx); + st64 op_idx = 0; + + RzIterator *iter = rz_core_analysis_bytes(core, core->offset, core->block, core->blocksize, 0); + if (!iter) { + return ops; + } + rz_iterator_foreach(iter, ab) { + if (!ab || !ab->op || !strcmp(ab->opcode, "nop")) { + continue; + } + JSValue op = js_analysis_bytes(ctx, core, ab); + JS_SetPropertyInt64(ctx, ops, op_idx, op); + op_idx++; + } + rz_iterator_free(iter); + return ops; +} + +static JSValue js_function_bbs(JSContext *ctx, RzCore *core, RzAnalysisFunction *fcn) { + JSValue bbs = JS_NewArray(ctx); + RzAnalysisBlock *bbi; + RzListIter *iter; + st64 bbs_idx = 0; + ut64 old_offset = core->offset; + ut64 old_bsize = core->blocksize; + rz_list_foreach (fcn->bbs, iter, bbi) { + rz_core_block_size(core, bbi->size); + rz_core_seek(core, bbi->addr, true); + + JSValue bb = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, bb, "address", JS_NewBigUint64(ctx, bbi->addr)); + if (bbi->jump != UT64_MAX) { + JS_SetPropertyStr(ctx, bb, "jump", JS_NewBigUint64(ctx, bbi->jump)); + } + if (bbi->fail != UT64_MAX) { + JS_SetPropertyStr(ctx, bb, "fail", JS_NewBigUint64(ctx, bbi->fail)); + } + JSValue ops = js_analysis_opcodes(ctx, core); + JS_SetPropertyStr(ctx, bb, "ops", ops); + JS_SetPropertyInt64(ctx, bbs, bbs_idx, bb); + bbs_idx++; + } + rz_core_block_size(core, old_bsize); + rz_core_seek(core, old_offset, true); + return bbs; +} + +static JSValue js_command(JSContext *ctx, JSValueConst jsThis, int argc, JSValueConst *argv) { + if (argc != 1) { + return JS_EXCEPTION; + } + + const char *command = JS_ToCString(ctx, argv[0]); + if (!command) { + return JS_EXCEPTION; + } + + ExecContext *ectx = (ExecContext *)JS_GetContextOpaque(ctx); + RzCore *core = ectx->core; + rz_cons_sleep_end(ectx->bed); + + char *output = rz_core_cmd_str(core, command); + JS_FreeCString(ctx, command); + JSValue result = JS_NewString(ctx, output ? output : ""); + free(output); + + ectx->bed = rz_cons_sleep_begin(); + return result; +} + +static JSValue js_graph(JSContext *ctx, JSValueConst jsThis, int argc, JSValueConst *argv) { + if (argc != 0) { + return JS_EXCEPTION; + } + ExecContext *ectx = (ExecContext *)JS_GetContextOpaque(ctx); + RzCore *core = ectx->core; + rz_cons_sleep_end(ectx->bed); + + RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, core->offset, -1); + if (!fcn) { + ectx->bed = rz_cons_sleep_begin(); + return JS_ThrowInternalError(ctx, "Cannot find function at 0x%08" PFMT64x, core->offset); + } + + JSValue graph = JS_NewArray(ctx); + JSValue object = JS_NewObject(ctx); + JS_SetPropertyInt64(ctx, graph, 0, object); + JS_SetPropertyStr(ctx, object, "name", JS_NewString(ctx, rz_str_get_null(fcn->name))); + JSValue blocks = js_function_bbs(ctx, core, fcn); + JS_SetPropertyStr(ctx, object, "blocks", blocks); + ectx->bed = rz_cons_sleep_begin(); + return graph; +} + +static JSValue js_console_log(JSContext *ctx, JSValueConst jsThis, int argc, JSValueConst *argv) { + ExecContext *ectx = (ExecContext *)JS_GetContextOpaque(ctx); + for (int i = 0; i < argc; ++i) { + if (i != 0) { + rz_strbuf_append_n(ectx->anno, " ", 1); + } + const char *str = JS_ToCString(ctx, argv[i]); + if (!str) { + return JS_EXCEPTION; + } + rz_strbuf_append(ectx->anno, str); + JS_FreeCString(ctx, str); + } + rz_strbuf_append_n(ectx->anno, "\n", 1); + return JS_UNDEFINED; +} + +static JSValue js_get_global(JSContext *ctx, JSValueConst jsThis, int argc, JSValueConst *argv) { + ExecContext *ectx = (ExecContext *)JS_GetContextOpaque(ctx); + return JS_GetPropertyStr(ctx, ectx->shared, "Shared"); +} + +static jsdec_t *jsdec_create(ExecContext *ec) { + jsdec_t *dec = jsdec_new(); + if (!dec) { + return NULL; + } + + JSContext *ctx = jsdec_context(dec); + JS_SetContextOpaque(ctx, ec); + ec->shared = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, ec->shared, "Shared", JS_NewObject(ctx)); + + JSValue global = JS_GetGlobalObject(ctx); + JS_SetPropertyStr(ctx, global, "Global", JS_NewCFunction(ctx, js_get_global, "Global", 1)); + + JSValue console = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, global, "console", console); + JS_SetPropertyStr(ctx, console, "log", JS_NewCFunction(ctx, js_console_log, "log", 1)); + + JSValue rizin = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, global, "rizin", rizin); + JS_SetPropertyStr(ctx, rizin, "command", JS_NewCFunction(ctx, js_command, "command", 1)); + JS_SetPropertyStr(ctx, rizin, "graph", JS_NewCFunction(ctx, js_graph, "graph", 1)); + + JSValue process = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, global, "process", process); + JSValue args = JS_NewArray(ctx); + JS_SetPropertyInt64(ctx, args, 0, JS_NewString(ctx, "--annotation")); + JS_SetPropertyStr(ctx, process, "args", args); + + JS_FreeValue(ctx, global); + return dec; +} + +static void jsdec_destroy(jsdec_t *dec, ExecContext *ec) { + JSContext *ctx = jsdec_context(dec); + JS_FreeValue(ctx, ec->shared); + jsdec_free(dec); +} + +static char *json_to_strdup(const RzJson *object, const char *key) { + const RzJson *j = rz_json_get(object, key); + if (!j || j->type != RZ_JSON_STRING) { + return NULL; + } + return rz_str_dup(j->str_value); +} + +static const char *json_as_string(const RzJson *object, const char *key) { + const RzJson *j = rz_json_get(object, key); + if (!j || j->type != RZ_JSON_STRING) { + return NULL; + } + return j->str_value; +} + +static ut64 json_as_ut64(const RzJson *object, const char *key) { + const RzJson *j = rz_json_get(object, key); + if (!j || (j->type != RZ_JSON_STRING && j->type != RZ_JSON_INTEGER)) { + return 0; + } + if (j->type == RZ_JSON_INTEGER) { + return j->num.u_value; + } else if (rz_num_is_valid_input(NULL, j->str_value)) { + return rz_num_get_input_value(NULL, j->str_value); + } + return 0; +} + +static RzAnnotatedCode *parse_json(RzStrBuf *sb) { + char *text = rz_strbuf_drain(sb); + if (RZ_STR_ISEMPTY(text)) { + free(text); + return NULL; + } + + // text is modified by rz_json_parse and + // is freed by rz_json_free + RzJson *json = rz_json_parse(text); + if (!json) { + RZ_LOG_ERROR("failed to parse from json string\n"); + return NULL; + } + + char *raw_code = json_to_strdup(json, "code"); + if (!raw_code) { + rz_json_free(json); + RZ_LOG_ERROR("failed to dup code\n"); + return NULL; + } + + RzAnnotatedCode *code = rz_annotated_code_new(raw_code); + if (!code) { + rz_json_free(json); + RZ_LOG_ERROR("failed to create RzAnnotatedCode\n"); + return NULL; + } + + const RzJson *annotations = rz_json_get(json, "annotations"); + const RzJson *element = NULL; + for (size_t idx = 0; annotations && (element = rz_json_item(annotations, idx)); idx++) { + RzCodeAnnotation annotation = { 0 }; + annotation.start = json_as_ut64(element, "start"); + annotation.end = json_as_ut64(element, "end"); + const char *type = json_as_string(element, "type"); + if (!strcmp(type, "offset")) { + annotation.type = RZ_CODE_ANNOTATION_TYPE_OFFSET; + annotation.offset.offset = json_as_ut64(element, "offset"); + } else if (!strcmp(type, "function_name")) { + annotation.type = RZ_CODE_ANNOTATION_TYPE_FUNCTION_NAME; + annotation.reference.name = json_to_strdup(element, "name"); + annotation.reference.offset = json_as_ut64(element, "offset"); + } else if (!strcmp(type, "global_variable")) { + annotation.type = RZ_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE; + annotation.reference.offset = json_as_ut64(element, "offset"); + } else if (!strcmp(type, "constant_variable")) { + annotation.type = RZ_CODE_ANNOTATION_TYPE_CONSTANT_VARIABLE; + annotation.reference.offset = json_as_ut64(element, "offset"); + } else if (!strcmp(type, "local_variable")) { + annotation.type = RZ_CODE_ANNOTATION_TYPE_LOCAL_VARIABLE; + annotation.variable.name = json_to_strdup(element, "name"); + } else if (!strcmp(type, "function_parameter")) { + annotation.type = RZ_CODE_ANNOTATION_TYPE_FUNCTION_PARAMETER; + annotation.variable.name = json_to_strdup(element, "name"); + } else if (!strcmp(type, "syntax_highlight")) { + annotation.type = RZ_CODE_ANNOTATION_TYPE_SYNTAX_HIGHLIGHT; + const char *highlightType = json_as_string(element, "syntax_highlight"); + if (!strcmp(highlightType, "keyword")) { + annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_KEYWORD; + } else if (!strcmp(highlightType, "comment")) { + annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_COMMENT; + } else if (!strcmp(highlightType, "datatype")) { + annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_DATATYPE; + } else if (!strcmp(highlightType, "function_name")) { + annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_FUNCTION_NAME; + } else if (!strcmp(highlightType, "function_parameter")) { + annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_FUNCTION_PARAMETER; + } else if (!strcmp(highlightType, "local_variable")) { + annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_LOCAL_VARIABLE; + } else if (!strcmp(highlightType, "constant_variable")) { + annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_CONSTANT_VARIABLE; + } else if (!strcmp(highlightType, "global_variable")) { + annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_GLOBAL_VARIABLE; + } + } + rz_annotated_code_add_annotation(code, &annotation); + } + + rz_json_free(json); + return code; +} + +RzAnnotatedCode *jsdec_as_annotation(RzCore *core, ut64 addr) { + ExecContext ectx; + ectx.core = core; + ectx.anno = rz_strbuf_new(""); + if (!ectx.anno) { + rz_strbuf_free(ectx.anno); + RZ_LOG_ERROR("failed to create RzStrBuf\n"); + return NULL; + } + + jsdec_t *dec = jsdec_create(&ectx); + if (!dec) { + rz_strbuf_free(ectx.anno); + RZ_LOG_ERROR("failed to create jsdec_t\n"); + return NULL; + } + + ut64 offset = core->offset; + if (offset != addr) { + rz_core_seek(core, addr, true); + } + + ectx.bed = rz_cons_sleep_begin(); + bool ret = jsdec_run(dec); + rz_cons_sleep_end(ectx.bed); + + if (offset != addr) { + rz_core_seek(core, offset, true); + } + + jsdec_destroy(dec, &ectx); + if (!ret) { + rz_strbuf_free(ectx.anno); + RZ_LOG_ERROR("jsdec_run returned false\n"); + return NULL; + } + + return parse_json(ectx.anno); +} diff --git a/c/jsdec-cutter.h b/c/jsdec-cutter.h new file mode 100644 index 0000000..dc61b5b --- /dev/null +++ b/c/jsdec-cutter.h @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2024 Giovanni Dante Grazioli +// SPDX-License-Identifier: BSD-3-Clause + +#ifndef JSDEC_CUTTER_H +#define JSDEC_CUTTER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +RzAnnotatedCode *jsdec_as_annotation(RzCore *core, ut64 addr); + +#ifdef __cplusplus +} +#endif + +#endif /* JSDEC_CUTTER_H */ diff --git a/cutter-plugin/CMakeLists.txt b/cutter-plugin/CMakeLists.txt new file mode 100644 index 0000000..dea72c5 --- /dev/null +++ b/cutter-plugin/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.12) +project(cutter-jsdec-plugin) + +set(CUTTER_INSTALL_PLUGDIR "share/rizin/cutter/plugins/native" CACHE STRING "Directory to install Cutter plugin into") +set(JSDEC_BUILD_DIR "../build" CACHE STRING "Directory where to find libjsdec.a") + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_INSTALL_PREFIX}/lib/cmake/Cutter") + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +add_library(jsdec_cutter MODULE + JSDecPlugin.h + JSDecPlugin.cpp + JSDecDecompiler.h + JSDecDecompiler.cpp +) + +find_library(libjsdec NAMES jsdec PATHS "${JSDEC_BUILD_DIR}") +include_directories("../c") + +find_package(Cutter REQUIRED) + +target_link_libraries(jsdec_cutter PRIVATE Cutter::Cutter "${libjsdec}") +install(TARGETS jsdec_cutter DESTINATION "${CUTTER_INSTALL_PLUGDIR}") diff --git a/cutter-plugin/JSDecDecompiler.cpp b/cutter-plugin/JSDecDecompiler.cpp new file mode 100644 index 0000000..7dcb235 --- /dev/null +++ b/cutter-plugin/JSDecDecompiler.cpp @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: 2024 Giovanni Dante Grazioli +// SPDX-License-Identifier: BSD-3-Clause + +#include "JSDecDecompiler.h" +#include + +#include +#include +#include + +#include "../c/jsdec-cutter.h" + +JSDecDecompiler::JSDecDecompiler(QObject *parent) + : Decompiler("jsdec", "jsdec", parent) { + task = nullptr; +} + +void JSDecDecompiler::decompileAt(RVA addr) { + if (task) { + return; + } + + task = new RizinFunctionTask([addr](RzCore *core) { + return jsdec_as_annotation(core, addr); + }); + + connect(task, &RizinFunctionTask::finished, this, [this]() { + auto res = reinterpret_cast(task->getResult()); + delete task; + task = nullptr; + if (!res) { + res = Decompiler::makeWarning(tr("Failed to parse JSON from jsdec")); + } + emit finished(res); + }); + + task->startTask(); +} diff --git a/cutter-plugin/JSDecDecompiler.h b/cutter-plugin/JSDecDecompiler.h new file mode 100644 index 0000000..99b0bf8 --- /dev/null +++ b/cutter-plugin/JSDecDecompiler.h @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2024 Giovanni Dante Grazioli +// SPDX-License-Identifier: BSD-3-Clause + +#ifndef JSDEC_DECOMPILER_H +#define JSDEC_DECOMPILER_H + +#include "Decompiler.h" +#include "RizinTask.h" + +class JSDecDecompiler: public Decompiler +{ + private: + RizinFunctionTask *task; + + public: + JSDecDecompiler(QObject *parent = nullptr); + virtual void decompileAt(RVA addr) override; + virtual bool isRunning() override { return task != nullptr; } +}; + +#endif // JSDEC_DECOMPILER_H diff --git a/cutter-plugin/JSDecPlugin.cpp b/cutter-plugin/JSDecPlugin.cpp new file mode 100644 index 0000000..617889c --- /dev/null +++ b/cutter-plugin/JSDecPlugin.cpp @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2024 Giovanni Dante Grazioli +// SPDX-License-Identifier: BSD-3-Clause + +#include "JSDecDecompiler.h" +#include "JSDecPlugin.h" + +void JSDecPlugin::setupPlugin() +{ +} + +void JSDecPlugin::setupInterface(MainWindow *) +{ +} + +void JSDecPlugin::registerDecompilers() +{ + Core()->registerDecompiler(new JSDecDecompiler(Core())); +} diff --git a/cutter-plugin/JSDecPlugin.h b/cutter-plugin/JSDecPlugin.h new file mode 100644 index 0000000..c68d1fe --- /dev/null +++ b/cutter-plugin/JSDecPlugin.h @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2024 Giovanni Dante Grazioli +// SPDX-License-Identifier: BSD-3-Clause + +#ifndef JSDEC_CUTTER_PLUGIN_H +#define JSDEC_CUTTER_PLUGIN_H + +#include +#include +#include + +class JSDecPlugin : public QObject, CutterPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "re.rizin.cutter.plugins.jsdec") + Q_INTERFACES(CutterPlugin) + +public: + void setupPlugin() override; + void setupInterface(MainWindow *main) override; + void registerDecompilers() override; + + QString getName() const override { return "JsDec Decompiler (jsdec)"; } + QString getAuthor() const override { return "deroad"; } + QString getDescription() const override { return "GUI Integration of jsdec."; } + QString getVersion() const override { return "1.0"; } +}; + + +#endif // JSDEC_CUTTER_PLUGIN_H diff --git a/js/meson.build b/js/meson.build index f2c19b2..1b548cd 100644 --- a/js/meson.build +++ b/js/meson.build @@ -4,7 +4,7 @@ jsdec_plugin = 'jsdec-plugin.js' jsdec_testsuite = 'jsdec-testsuite.js' -if get_option('standalone') +if get_option('build_type') == 'standalone' bytecode_h = custom_target( 'bytecode.h', build_always_stale: true, diff --git a/meson.build b/meson.build index 2d24b27..ff2b7f9 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ -# SPDX-FileCopyrightText: 2023 Giovanni Dante Grazioli +# SPDX-FileCopyrightText: 2023-2024 Giovanni Dante Grazioli # SPDX-License-Identifier: BSD-3-Clause -project('jsdec', 'c', meson_version: '>=0.55.0') +project('jsdec', 'c', meson_version: '>=0.56.0') cc = meson.get_compiler('c') @@ -16,7 +16,7 @@ jsdec_src = [ 'c' / 'base64.c', ] jsdec_deps = [ - libquickjs_dep, + libquickjs_dep.as_link_whole(), ] modjs_gen = executable('modjs_gen', 'tools' / 'modjs_gen.c', @@ -28,8 +28,8 @@ modjs_gen = executable('modjs_gen', 'tools' / 'modjs_gen.c', subdir('js') -if get_option('standalone') - +build_type = get_option('build_type') +if build_type == 'standalone' jsdec_src += [ bytecode_h, bytecode_mod_h, @@ -43,7 +43,31 @@ if get_option('standalone') implicit_include_directories: false, install: false, ) -else +elif build_type == 'cutter' + jsdec_deps += dependency('rz_core') + jsdec_deps += dependency('rz_util') + jsdec_deps += dependency('rz_cons') + jsdec_deps += dependency('rz_config') + jsdec_deps += dependency('rz_io') + + jsdec_src += [ + bytecode_h, + bytecode_mod_h, + 'c' / 'jsdec-cutter.c' + ] + + libjsdec = static_library('jsdec', jsdec_src, + c_args : jsdec_c_args, + dependencies: jsdec_deps, + include_directories: include_directories(jsdec_incs), + implicit_include_directories: false, + install: false, + ) + libjsdec_dep = declare_dependency( + link_with: libjsdec, + include_directories: include_directories(jsdec_incs), + ) +elif build_type == 'rizin' # build plugin for Rizin rz_core_dep = dependency('rz_core') jsdec_deps += rz_core_dep @@ -54,9 +78,6 @@ else rizin_plugdir = get_option('rizin_plugdir') if rizin_plugdir == '' rizin_plugdir = rz_core_dep.get_variable(pkgconfig: 'plugindir', cmake: 'rz_core_PLUGINDIR') - plugin_jsdec_dir = join_paths(get_option('prefix'), rizin_plugdir, 'jsdec') - else - plugin_jsdec_dir = join_paths(rizin_plugdir, 'jsdec') endif jsdec_src += [ @@ -73,4 +94,6 @@ else install: true, install_dir: rizin_plugdir ) +else + error('Invalid `build_type`.') endif diff --git a/meson_options.txt b/meson_options.txt index 1a86985..ab60e9e 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,2 +1,2 @@ option('rizin_plugdir', type: 'string', value: '', description: 'rizin install directory') -option('standalone', type: 'boolean', value: false, description: 'enables/disables building only the standalone executable for testing purposes') +option('build_type', type: 'combo', choices: ['rizin', 'cutter', 'standalone'], value: 'rizin', description: 'build type; `standalone` for tests, `rizin` as rizin native plugin and `cutter` as cutter native plugin')