From 305cc00766516eb563fd4a6a9e74700aa0907629 Mon Sep 17 00:00:00 2001 From: NIRMAL MANOJ C Date: Wed, 27 May 2020 09:24:31 +0530 Subject: [PATCH] Migrating RAnnotatedCode to radare2 (#16939) * Added comments for functions in RAnnotatedCode * Modified code to follow coding style * Added more documentation and changed the name of core_annotated_code.c * Fixed memory leaks --- libr/core/Makefile | 1 + libr/core/cannotated_code.c | 262 +++++++++++++++++++ libr/core/meson.build | 1 + libr/include/r_core.h | 38 +++ libr/include/r_util/r_annotated_code.h | 133 ++++++++++ libr/meson.build | 3 +- libr/util/Makefile | 1 + libr/util/annotated_code.c | 84 ++++++ libr/util/meson.build | 3 +- test/unit/meson.build | 1 + test/unit/test_annotated_code.c | 343 +++++++++++++++++++++++++ 11 files changed, 868 insertions(+), 2 deletions(-) create mode 100644 libr/core/cannotated_code.c create mode 100644 libr/include/r_util/r_annotated_code.h create mode 100644 libr/util/annotated_code.c create mode 100644 test/unit/test_annotated_code.c diff --git a/libr/core/Makefile b/libr/core/Makefile index a7ad9c8c80..c073d7416b 100644 --- a/libr/core/Makefile +++ b/libr/core/Makefile @@ -11,6 +11,7 @@ OBJS+=fortune.o hack.o vasm.o patch.o cbin.o corelog.o rtr.o cmd_api.o OBJS+=carg.o canal.o project.o gdiff.o casm.o disasm.o cplugin.o OBJS+=vmenus.o vmenus_graph.o vmenus_zigns.o zdiff.o citem.o OBJS+=task.o panels.o pseudo.o vmarks.o anal_tp.o anal_objc.o blaze.o cundo.o +OBJS+=cannotated_code.o CFLAGS+=-I../../shlr/heap/include CFLAGS+=-I../../shlr/tree-sitter/lib/include -I../../shlr/radare2-shell-parser/src/tree_parser diff --git a/libr/core/cannotated_code.c b/libr/core/cannotated_code.c new file mode 100644 index 0000000000..bba249411b --- /dev/null +++ b/libr/core/cannotated_code.c @@ -0,0 +1,262 @@ +#include + +#include +#include +#include +#include + +R_API void r_core_annotated_code_print_json(RAnnotatedCode *code) { + PJ *pj = pj_new (); + if (!pj) { + return; + } + + pj_o (pj); + pj_ks (pj, "code", code->code); + + pj_k (pj, "annotations"); + pj_a (pj); + + char *type_str; + RCodeAnnotation *annotation; + r_vector_foreach (&code->annotations, annotation) { + pj_o (pj); + pj_kn (pj, "start", (ut64)annotation->start); + pj_kn (pj, "end", (ut64)annotation->end); + switch (annotation->type) { + case R_CODE_ANNOTATION_TYPE_OFFSET: + pj_ks (pj, "type", "offset"); + pj_kn (pj, "offset", annotation->offset.offset); + break; + case R_CODE_ANNOTATION_TYPE_SYNTAX_HIGHLIGHT: + pj_ks (pj, "type", "syntax_highlight"); + switch (annotation->syntax_highlight.type) { + case R_SYNTAX_HIGHLIGHT_TYPE_KEYWORD: + type_str = "keyword"; + break; + case R_SYNTAX_HIGHLIGHT_TYPE_COMMENT: + type_str = "comment"; + break; + case R_SYNTAX_HIGHLIGHT_TYPE_DATATYPE: + type_str = "datatype"; + break; + case R_SYNTAX_HIGHLIGHT_TYPE_FUNCTION_NAME: + type_str = "function_name"; + break; + case R_SYNTAX_HIGHLIGHT_TYPE_FUNCTION_PARAMETER: + type_str = "function_parameter"; + break; + case R_SYNTAX_HIGHLIGHT_TYPE_LOCAL_VARIABLE: + type_str = "local_variable"; + break; + case R_SYNTAX_HIGHLIGHT_TYPE_CONSTANT_VARIABLE: + type_str = "constant_variable"; + break; + case R_SYNTAX_HIGHLIGHT_TYPE_GLOBAL_VARIABLE: + type_str = "global_variable"; + break; + } + pj_ks (pj, "syntax_highlight", type_str); + break; + } + pj_end (pj); + } + pj_end (pj); + + pj_end (pj); + r_cons_printf ("%s\n", pj_string (pj)); + pj_free (pj); +} + +#define PALETTE(x) (cons && cons->context->pal.x) ? cons->context->pal.x +#define PRINT_COLOR(x) \ + do { \ + if (cons->context->color_mode) { \ + r_cons_printf ("%s", (x)); \ + } \ + } while (0) + +/** + * @param width maximum nibbles per address + */ +static void print_offset_in_binary_line_bar(RAnnotatedCode *code, ut64 offset, size_t width) { + static const char *fmt[9] = { + "0x%08" PFMT64x, + "0x%09" PFMT64x, + "0x%010" PFMT64x, + "0x%011" PFMT64x, + "0x%012" PFMT64x, + "0x%013" PFMT64x, + "0x%014" PFMT64x, + "0x%015" PFMT64x, + "0x%016" PFMT64x + }; + if (width < 8) { + width = 8; + } + if (width > 16) { + width = 16; + } + width -= 8; + + RCons *cons = r_cons_singleton (); + r_cons_printf (" "); + if (offset == UT64_MAX) { + r_cons_print (" "); + while (width > 0) { + r_cons_print (" "); + width--; + } + } else { + PRINT_COLOR (PALETTE (offset) + : Color_GREEN); + r_cons_printf (fmt[width], offset); + PRINT_COLOR (Color_RESET); + } + r_cons_printf (" |"); +} + +R_API void r_core_annotated_code_print(RAnnotatedCode *code, RVector *line_offsets) { + if (code->annotations.len == 0) { + r_cons_printf ("%s\n", code->code); + return; + } + + size_t cur = 0; + size_t line_idx = 0; + size_t len = strlen (code->code); + + size_t offset_width = 0; + if (line_offsets) { + ut64 *offset; + ut64 offset_max = 0; + r_vector_foreach (line_offsets, offset) { + if (*offset != UT64_MAX && *offset > offset_max) { + offset_max = *offset; + } + } + while (offset_max) { + offset_width += 1; + offset_max >>= 4; + } + if (offset_width < 4) { + offset_width = 4; + } + } + + RCons *cons = r_cons_singleton (); + RCodeAnnotation *annotation; + r_vector_foreach (&code->annotations, annotation) { + if (annotation->type != R_CODE_ANNOTATION_TYPE_SYNTAX_HIGHLIGHT) { + continue; + } + + // (1/3) + // now we have a syntax highlighting annotation. + // pick a suitable color for it. + const char *color = Color_RESET; + switch (annotation->syntax_highlight.type) { + case R_SYNTAX_HIGHLIGHT_TYPE_COMMENT: + color = PALETTE (comment) + : Color_WHITE; + break; + case R_SYNTAX_HIGHLIGHT_TYPE_KEYWORD: + color = PALETTE (pop) + : Color_MAGENTA; + break; + case R_SYNTAX_HIGHLIGHT_TYPE_DATATYPE: + color = PALETTE (func_var_type) + : Color_BLUE; + break; + case R_SYNTAX_HIGHLIGHT_TYPE_FUNCTION_NAME: + color = PALETTE (fname) + : Color_RED; + break; + case R_SYNTAX_HIGHLIGHT_TYPE_CONSTANT_VARIABLE: + color = PALETTE (num) + : Color_YELLOW; + default: + break; + } + + // (2/3) + // the chunk before the syntax highlighting annotation should not be colored + for (; cur < annotation->start && cur < len; cur++) { + // if we are starting a new line and we are printing with offsets + // we need to prepare the bar with offsets on the left handside before that + if (line_offsets && (cur == 0 || code->code[cur - 1] == '\n')) { + ut64 offset = 0; + if (line_idx < line_offsets->len) { + offset = *(ut64 *)r_vector_index_ptr (line_offsets, line_idx); + } + print_offset_in_binary_line_bar (code, offset, offset_width); + line_idx++; + } + r_cons_printf ("%c", code->code[cur]); + } + + // (3/3) + // everything in between the "start" and the "end" inclusive should be highlighted + PRINT_COLOR (color); + for (; cur < annotation->end && cur < len; cur++) { + // if we are starting a new line and we are printing with offsets + // we need to prepare the bar with offsets on the left handside before that + if (line_offsets && (cur == 0 || code->code[cur - 1] == '\n')) { + ut64 offset = 0; + if (line_idx < line_offsets->len) { + offset = *(ut64 *)r_vector_index_ptr (line_offsets, line_idx); + } + PRINT_COLOR (Color_RESET); + print_offset_in_binary_line_bar (code, offset, offset_width); + PRINT_COLOR (color); + line_idx++; + } + r_cons_printf ("%c", code->code[cur]); + } + PRINT_COLOR (Color_RESET); + } + // the rest of the decompiled code should be printed + // without any highlighting since we don't have any annotations left + for (; cur < len; cur++) { + // if we are starting a new line and we are printing with offsets + // we need to prepare the bar with offsets on the left handside before that + if (line_offsets && (cur == 0 || code->code[cur - 1] == '\n')) { + ut64 offset = 0; + if (line_idx < line_offsets->len) { + offset = *(ut64 *)r_vector_index_ptr (line_offsets, line_idx); + } + print_offset_in_binary_line_bar (code, offset, offset_width); + line_idx++; + } + r_cons_printf ("%c", code->code[cur]); + } +} + +static bool foreach_offset_annotation(void *user, const ut64 offset, const void *val) { + RAnnotatedCode *code = user; + const RCodeAnnotation *annotation = val; + char *b64statement = r_base64_encode_dyn (code->code + annotation->start, annotation->end - annotation->start); + r_cons_printf ("CCu base64:%s @ 0x%" PFMT64x "\n", b64statement, annotation->offset.offset); + free (b64statement); + return true; +} + +R_API void r_core_annotated_code_print_comment_cmds(RAnnotatedCode *code) { + RCodeAnnotation *annotation; + HtUP *ht = ht_up_new0 (); + r_vector_foreach (&code->annotations, annotation) { + if (annotation->type != R_CODE_ANNOTATION_TYPE_OFFSET) { + continue; + } + // choose the "best" annotation at a single offset + RCodeAnnotation *prev_annot = ht_up_find (ht, annotation->offset.offset, NULL); + if (prev_annot) { + if (annotation->end - annotation->start < prev_annot->end - prev_annot->start) { + continue; + } + } + ht_up_update (ht, annotation->offset.offset, annotation); + } + ht_up_foreach (ht, foreach_offset_annotation, code); + ht_up_free (ht); +} diff --git a/libr/core/meson.build b/libr/core/meson.build index 3c422e3747..574596a75d 100644 --- a/libr/core/meson.build +++ b/libr/core/meson.build @@ -65,6 +65,7 @@ r_core_sources = [ 'yank.c', 'p/core_a2f.c', 'p/core_java.c', + 'cannotated_code.c' ] r_core_inc = [] diff --git a/libr/include/r_core.h b/libr/include/r_core.h index 761049a016..77ec61f12e 100644 --- a/libr/include/r_core.h +++ b/libr/include/r_core.h @@ -30,6 +30,7 @@ #include "r_util/r_print.h" #include "r_crypto.h" #include "r_bind.h" +#include "r_util/r_annotated_code.h" #ifdef __cplusplus extern "C" { @@ -924,6 +925,43 @@ R_API void r_core_anal_propagate_noreturn(RCore *core, ut64 addr); extern RCorePlugin r_core_plugin_java; extern RCorePlugin r_core_plugin_a2f; +/* DECOMPILER PRINTING FUNCTIONS */ +/** +* r_core_annotated_code_print_json() - Prints the data contained in RAnnotatedCode *code in JSON format. +* @code: Pointer to a RAnnotatedCode +* +* Prints the data contained in RAnnotatedCode represented by the pointer 'code' in JSON format. +* The function will print the output in console using the function r_cons_printf(); +* +* Return: Nothing +*/ +R_API void r_core_annotated_code_print_json(RAnnotatedCode *code); +/** +* r_core_annotated_code_print() - Prints the decompiled code in the passed argument 'code'. +* @code: Pointer to a RAnnotatedCode +* @line_offsets: Pointer to a RVector that containes offsets for the decompiled code +* +* This function is used for printing the output of commands pdg and pdgo. +* It can print the decompiled code with or without offsets. If line_offsets is a null pointer, +* the output will be printed without offsets (pdg), otherwise, the output will be +* printed with offsets. +* This function will print the output in console using the function r_cons_printf(); +* +* Return: Nothing +*/ +R_API void r_core_annotated_code_print(RAnnotatedCode *code, RVector *line_offsets); +/** +* r_core_annotated_code_print_comment_cmds() - Prints the decompiled code as comments +* @code: Pointer to a RAnnotatedCode +* +* This functions prints the decompiled code as comment. +* This function is used for the output of command pdg* +* Output will be printed in console using the function r_cons_printf(); +* +* Return: Nothing +*/ +R_API void r_core_annotated_code_print_comment_cmds(RAnnotatedCode *code); + #endif #ifdef __cplusplus diff --git a/libr/include/r_util/r_annotated_code.h b/libr/include/r_util/r_annotated_code.h new file mode 100644 index 0000000000..56ead07e7b --- /dev/null +++ b/libr/include/r_util/r_annotated_code.h @@ -0,0 +1,133 @@ + +#ifndef R_ANNOTATEDCODE_H +#define R_ANNOTATEDCODE_H + +// #include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum r_syntax_highlight_type_t { + R_SYNTAX_HIGHLIGHT_TYPE_KEYWORD, + R_SYNTAX_HIGHLIGHT_TYPE_COMMENT, + R_SYNTAX_HIGHLIGHT_TYPE_DATATYPE, + R_SYNTAX_HIGHLIGHT_TYPE_FUNCTION_NAME, + R_SYNTAX_HIGHLIGHT_TYPE_FUNCTION_PARAMETER, + R_SYNTAX_HIGHLIGHT_TYPE_LOCAL_VARIABLE, + R_SYNTAX_HIGHLIGHT_TYPE_CONSTANT_VARIABLE, + R_SYNTAX_HIGHLIGHT_TYPE_GLOBAL_VARIABLE, +} RSyntaxHighlightType; + +/** + * enum r_code_annotation_type_t - typedefed as RCodeAnnotationType and this gives types of annotation + * + * There are two kinds of RCodeAnnotation. One for offset, which of the type + * R_CODE_ANNOTATION_TYPE_OFFSET and other one is for syntax highlight, which is + * of the type R_CODE_ANNOTATION_TYPE_SYNTAX_HIGHLIGHT. + * R_CODE_ANNOTATION_TYPE_OFFSET is for representing annotations that gives an offset for + * a range while R_CODE_ANNOTATION_TYPE_SYNTAX_HIGHLIGHT is for representing the + * kind of data the range represents. Here, range refers to the range of annotation. + */ + +typedef enum r_code_annotation_type_t { + R_CODE_ANNOTATION_TYPE_OFFSET, + R_CODE_ANNOTATION_TYPE_SYNTAX_HIGHLIGHT, + // ... +} RCodeAnnotationType; + +typedef struct r_code_annotation_t { + size_t start; + size_t end; + RCodeAnnotationType type; + union { + struct { + ut64 offset; + } offset; + + struct { + RSyntaxHighlightType type; + } syntax_highlight; + }; +} RCodeAnnotation; + +typedef struct r_annotated_code_t { + char *code; // owned + RVector /**/ annotations; +} RAnnotatedCode; + +/** + * r_annotated_code_new() - Creates a new RAnnotatedCode structure and returns its pointer. + * @code: Literal code for which the RAnnotatedCode structure will be created . + * + * This functions creates a new RAnnotatedCode structure. + * RAnnotatedCode.code will be initialized as the character array passed. + * Here, code must be a string that can deallocated. + * This will initialize RVector annotations as well. + * + * Return: Pointer to the new RAnnotatedCode structure created. + */ +R_API RAnnotatedCode *r_annotated_code_new(char *code); +/** + * r_annotated_code_free() - Deallocates *code. + * @code: Pointer to a RAnnotatedCode. + * + * This functions deallocates memory allocated for *code. + * + * Return: Nothing. + */ +R_API void r_annotated_code_free(RAnnotatedCode *code); +/** + * r_annotated_code_add_annotation() - Inserts *annotation in *code. + * @code: Pointer to a RAnnotatedCode. + * @annotation: Pointer to a annotation. + * + * This functions inserts the annotation represented by the pointer 'annotation' to the vector + * of annotations in the RAnnotatedCode represented by 'code'. To be more precise, + * annotation will be added to code->annotations, which is a RVector annotations. + * + * Return: Nothing. + */ +R_API void r_annotated_code_add_annotation(RAnnotatedCode *code, RCodeAnnotation *annotation); +/** + * r_annotated_code_annotations_in() - Returns all annotations with range that contains the given offset. + * @code: Pointer to a RAnnotatedCode. + * @offset: Offset. + * + * Creates an RPVector and inserts the pointers to all annotations in which + * annotation->start <= offset < annotation->end. + * + * Return: Pointer to the RPVecrtor created. + */ +R_API RPVector *r_annotated_code_annotations_in(RAnnotatedCode *code, size_t offset); +/** + * r_annotated_code_annotations_range() - Returns all annotations with range that overlap with the given range. + * @code: Pointer to a RAnnotatedCode. + * @start: Start of the range(inclusive). + * @end: End of the range(exclusive). + * + * Creates an RPVector and inserts the pointers to all annotations whose + * range overlap with range [start, end-1] (both inclusive). + * + * Return: Pointer to the RPVecrtor created. + */ +R_API RPVector *r_annotated_code_annotations_range(RAnnotatedCode *code, size_t start, size_t end); +/** + * r_annotated_code_line_offsets() - Returns the offset for every line of decompiled code in RAnnotatedCode *code. + * @code: Pointer to a RAnnotatedCode. + * + * Creates an RVector and inserts the offsets for every seperate line of decompiled code in + * code->code (code->code is a character array). + * If a line of decompiled code doesn't have a unique offset, UT64_MAX is inserted as its offset. + * + * Return: Pointer to the RVector created. + */ +R_API RVector *r_annotated_code_line_offsets(RAnnotatedCode *code); + +#ifdef __cplusplus +} +#endif + +#endif //R_ANNOTATEDCODE_H diff --git a/libr/meson.build b/libr/meson.build index a2e831a06f..e21a0a63c5 100644 --- a/libr/meson.build +++ b/libr/meson.build @@ -431,7 +431,8 @@ r_util_files = [ 'include/r_util/r_utf16.h', 'include/r_util/r_utf32.h', 'include/r_util/r_utf8.h', - 'include/r_util/r_x509.h' + 'include/r_util/r_x509.h', + 'include/r_util/r_annotated_code.h' ] install_headers(r_util_files, subdir: 'libr/r_util') diff --git a/libr/util/Makefile b/libr/util/Makefile index a03289b0df..bd30127e80 100644 --- a/libr/util/Makefile +++ b/libr/util/Makefile @@ -23,6 +23,7 @@ OBJS+=udiff.o bdiff.o stack.o queue.o tree.o idpool.o assert.o OBJS+=punycode.o pkcs7.o x509.o asn1.o astr.o json_indent.o skiplist.o pj.o OBJS+=rbtree.o intervaltree.o qrcode.o vector.o str_constpool.o str_trim.o OBJS+=ascii_table.o protobuf.o +OBJS+=annotated_code.o ifeq (${HAVE_LIB_GMP},1) OBJS+=big-gmp.o diff --git a/libr/util/annotated_code.c b/libr/util/annotated_code.c new file mode 100644 index 0000000000..afe821187c --- /dev/null +++ b/libr/util/annotated_code.c @@ -0,0 +1,84 @@ + +#include +#include +#include + +R_API RAnnotatedCode *r_annotated_code_new(char *code) { + RAnnotatedCode *r = R_NEW0 (RAnnotatedCode); + if (!r) { + return NULL; + } + r->code = code; + r_vector_init (&r->annotations, sizeof (RCodeAnnotation), NULL, NULL); + return r; +} + +R_API void r_annotated_code_free(RAnnotatedCode *code) { + if (!code) { + return; + } + r_vector_clear (&code->annotations); + r_free (code->code); + r_free (code); +} + +R_API void r_annotated_code_add_annotation(RAnnotatedCode *code, RCodeAnnotation *annotation) { + r_vector_push (&code->annotations, annotation); +} + +R_API RPVector *r_annotated_code_annotations_in(RAnnotatedCode *code, size_t offset) { + RPVector *r = r_pvector_new (NULL); + if (!r) { + return NULL; + } + RCodeAnnotation *annotation; + r_vector_foreach (&code->annotations, annotation) { + if (offset >= annotation->start && offset < annotation->end) { + r_pvector_push (r, annotation); + } + } + return r; +} + +R_API RPVector *r_annotated_code_annotations_range(RAnnotatedCode *code, size_t start, size_t end) { + RPVector *r = r_pvector_new (NULL); + if (!r) { + return NULL; + } + RCodeAnnotation *annotation; + r_vector_foreach (&code->annotations, annotation) { + if (start >= annotation->end || end < annotation->start) { + continue; + } + r_pvector_push (r, annotation); + } + return r; +} + +R_API RVector *r_annotated_code_line_offsets(RAnnotatedCode *code) { + RVector *r = r_vector_new (sizeof (ut64), NULL, NULL); + if (!r) { + return NULL; + } + size_t cur = 0; + size_t len = strlen (code->code); + do { + char *next = strchr (code->code + cur, '\n'); + size_t next_i = next? (next - code->code) + 1: len; + RPVector *annotations = r_annotated_code_annotations_range (code, cur, next_i); + ut64 offset = UT64_MAX; + void **it; + r_pvector_foreach (annotations, it) { + RCodeAnnotation *annotation = *it; + if (annotation->type != R_CODE_ANNOTATION_TYPE_OFFSET) { + continue; + } + offset = annotation->offset.offset; + break; + } + r_vector_push (r, &offset); + cur = next_i; + r_pvector_free (annotations); + } while (cur < len); + return r; +} diff --git a/libr/util/meson.build b/libr/util/meson.build index b1a87c6cb4..3ab39affd1 100644 --- a/libr/util/meson.build +++ b/libr/util/meson.build @@ -81,7 +81,8 @@ r_util_sources = [ 'protobuf.c', 'regex/regcomp.c', 'regex/regexec.c', - 'regex/regerror.c' + 'regex/regerror.c', + 'annotated_code.c' ] r_util_deps = [ldl, mth, pth, utl, sdb_dep, zlib_dep, platform_deps] diff --git a/test/unit/meson.build b/test/unit/meson.build index 954c2e2470..efecfdf6f6 100644 --- a/test/unit/meson.build +++ b/test/unit/meson.build @@ -9,6 +9,7 @@ if get_option('enable_tests') 'anal_meta', 'anal_var', 'anal_xrefs', + 'annotated_code', 'base64', 'bin', 'bitmap', diff --git a/test/unit/test_annotated_code.c b/test/unit/test_annotated_code.c new file mode 100644 index 0000000000..1be5a49a46 --- /dev/null +++ b/test/unit/test_annotated_code.c @@ -0,0 +1,343 @@ +#include +#include +#include +#include +#include + +#include "minunit.h" + +static RCodeAnnotation make_code_annotation(int st, int en, RCodeAnnotationType typec, + ut64 offset, RSyntaxHighlightType types) { + RCodeAnnotation annotation = {}; + annotation.start = st; + annotation.end = en; + annotation.type = typec; + if (annotation.type == R_CODE_ANNOTATION_TYPE_OFFSET) { + annotation.offset.offset = offset; + } + if (annotation.type == R_CODE_ANNOTATION_TYPE_SYNTAX_HIGHLIGHT) { + annotation.syntax_highlight.type = types; + } + return annotation; +} + +static RVector *get_some_code_annotation_for_add() { + RVector *test_annotations = r_vector_new (sizeof (RCodeAnnotation), NULL, NULL); + RCodeAnnotation annotation; + r_vector_init (test_annotations, sizeof (RCodeAnnotation), NULL, NULL); + annotation = make_code_annotation (1, 2, R_CODE_ANNOTATION_TYPE_OFFSET, 123, R_SYNTAX_HIGHLIGHT_TYPE_KEYWORD); + r_vector_push (test_annotations, &annotation); + annotation = make_code_annotation (1, 5, R_CODE_ANNOTATION_TYPE_SYNTAX_HIGHLIGHT, 123, R_SYNTAX_HIGHLIGHT_TYPE_KEYWORD); + r_vector_push (test_annotations, &annotation); + return test_annotations; +} + +static RVector *get_some_annotations_for_in() { + RVector *test_annotations = r_vector_new (sizeof (RCodeAnnotation), NULL, NULL); + RCodeAnnotation annotation; + annotation = make_code_annotation (1, 2, R_CODE_ANNOTATION_TYPE_OFFSET, 123, R_SYNTAX_HIGHLIGHT_TYPE_KEYWORD); + r_vector_push (test_annotations, &annotation); + annotation = make_code_annotation (1, 7, R_CODE_ANNOTATION_TYPE_SYNTAX_HIGHLIGHT, 123, R_SYNTAX_HIGHLIGHT_TYPE_KEYWORD); + r_vector_push (test_annotations, &annotation); + annotation = make_code_annotation (9, 11, R_CODE_ANNOTATION_TYPE_OFFSET, 123, R_SYNTAX_HIGHLIGHT_TYPE_KEYWORD); + r_vector_push (test_annotations, &annotation); + + // For offset = 11, indices expected = 3, 4, 5 + annotation = make_code_annotation (7, 13, R_CODE_ANNOTATION_TYPE_OFFSET, 123, R_SYNTAX_HIGHLIGHT_TYPE_KEYWORD); + r_vector_push (test_annotations, &annotation); + annotation = make_code_annotation (11, 15, R_CODE_ANNOTATION_TYPE_OFFSET, 123, R_SYNTAX_HIGHLIGHT_TYPE_KEYWORD); + r_vector_push (test_annotations, &annotation); + annotation = make_code_annotation (10, 16, R_CODE_ANNOTATION_TYPE_OFFSET, 123, R_SYNTAX_HIGHLIGHT_TYPE_KEYWORD); + r_vector_push (test_annotations, &annotation); + annotation = make_code_annotation (17, 20, R_CODE_ANNOTATION_TYPE_OFFSET, 32, R_SYNTAX_HIGHLIGHT_TYPE_KEYWORD); + r_vector_push (test_annotations, &annotation); + + return test_annotations; +} + +static RVector *get_annotations_for_hello_world() { + RVector *test_annotations = r_vector_new (sizeof (RCodeAnnotation), NULL, NULL); + RCodeAnnotation annotation; + // r_vector_init (&test_annotations, sizeof (RCodeAnnotation), NULL, NULL); + //Code Annotations for a hello world program + annotation = make_code_annotation (1, 5, R_CODE_ANNOTATION_TYPE_SYNTAX_HIGHLIGHT, 123, R_SYNTAX_HIGHLIGHT_TYPE_DATATYPE); + r_vector_push (test_annotations, &annotation); //1 + annotation = make_code_annotation (6, 10, R_CODE_ANNOTATION_TYPE_SYNTAX_HIGHLIGHT, 123, R_SYNTAX_HIGHLIGHT_TYPE_FUNCTION_NAME); + r_vector_push (test_annotations, &annotation); //2 + annotation = make_code_annotation (11, 15, R_CODE_ANNOTATION_TYPE_SYNTAX_HIGHLIGHT, 123, R_SYNTAX_HIGHLIGHT_TYPE_KEYWORD); + r_vector_push (test_annotations, &annotation); //3 + annotation = make_code_annotation (23, 35, R_CODE_ANNOTATION_TYPE_SYNTAX_HIGHLIGHT, 123, R_SYNTAX_HIGHLIGHT_TYPE_FUNCTION_NAME); + r_vector_push (test_annotations, &annotation); //4 + annotation = make_code_annotation (36, 51, R_CODE_ANNOTATION_TYPE_SYNTAX_HIGHLIGHT, 123, R_SYNTAX_HIGHLIGHT_TYPE_CONSTANT_VARIABLE); + r_vector_push (test_annotations, &annotation); //5 + annotation = make_code_annotation (23, 52, R_CODE_ANNOTATION_TYPE_OFFSET, 4440, R_SYNTAX_HIGHLIGHT_TYPE_KEYWORD); + r_vector_push (test_annotations, &annotation); //6 + annotation = make_code_annotation (58, 64, R_CODE_ANNOTATION_TYPE_OFFSET, 4447, R_SYNTAX_HIGHLIGHT_TYPE_KEYWORD); + r_vector_push (test_annotations, &annotation); //7 + annotation = make_code_annotation (58, 64, R_CODE_ANNOTATION_TYPE_SYNTAX_HIGHLIGHT, 123, R_SYNTAX_HIGHLIGHT_TYPE_KEYWORD); + r_vector_push (test_annotations, &annotation); //8 + annotation = make_code_annotation (58, 64, R_CODE_ANNOTATION_TYPE_OFFSET, 4447, R_SYNTAX_HIGHLIGHT_TYPE_KEYWORD); + r_vector_push (test_annotations, &annotation); //9 + + return test_annotations; +} + +static RAnnotatedCode *get_hello_world() { + char *test_string = strdup ("\nvoid main(void)\n{\n sym.imp.puts(\"Hello, World!\");\n return;\n}\n"); + RAnnotatedCode *code = r_annotated_code_new (test_string); + + RVector /**/ *test_annotations; + test_annotations = get_annotations_for_hello_world (); + RCodeAnnotation *annotation; + r_vector_foreach (test_annotations, annotation) { + r_annotated_code_add_annotation (code, annotation); + } + + r_vector_free (test_annotations); + return code; +} + +static bool test_r_annotated_code_new() { + //Testing RAnnoatedCode->code + char *test_string = strdup ("How are you?"); + RAnnotatedCode *code = r_annotated_code_new (test_string); + mu_assert_streq (code->code, test_string, "Code in RAnnotatedCode is not set as expected"); + + // Testing RAnnoatedCode->annotations + mu_assert_eq (code->annotations.elem_size, sizeof (RCodeAnnotation), "Code Annotations are initialized is not properly"); + + r_annotated_code_free (code); + mu_end; +} + +static bool test_r_annotated_code_free() { + char *test_string = strdup ("How are you?"); + RAnnotatedCode *code = r_annotated_code_new (test_string); + + RCodeAnnotation test_annotation1, test_annotation2; + test_annotation1 = make_code_annotation (1, 2, R_CODE_ANNOTATION_TYPE_OFFSET, 123, R_SYNTAX_HIGHLIGHT_TYPE_KEYWORD); + r_vector_push (&code->annotations, &test_annotation1); + test_annotation2 = make_code_annotation (1, 5, R_CODE_ANNOTATION_TYPE_SYNTAX_HIGHLIGHT, 123, R_SYNTAX_HIGHLIGHT_TYPE_KEYWORD); + r_vector_push (&code->annotations, &test_annotation2); + + // This test is only for run errors + + r_annotated_code_free (code); + mu_end; +} + +static bool test_equal(RCodeAnnotation *first, RCodeAnnotation *second) { // First - Got, Second - Expected + mu_assert_eq (first->start, second->start, "start of annotations doesn't match"); + mu_assert_eq (first->end, second->end, "end of annotations doesn't match"); + mu_assert_eq (first->type, second->type, "type of annotation doesn't match"); + if (first->type == R_CODE_ANNOTATION_TYPE_OFFSET) { + mu_assert_eq (first->offset.offset, second->offset.offset, "offset of annotations doesn't match"); + return true; + } + if (first->type == R_CODE_ANNOTATION_TYPE_SYNTAX_HIGHLIGHT) { + mu_assert_eq (first->syntax_highlight.type, second->syntax_highlight.type, "syntax highlight type of offset doesn't match"); + return true; + } + return false; +} + +static bool test_r_annotated_code_add_annotation() { + char *test_string = strdup ("abcdefghijklmnopqrtstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + RAnnotatedCode *code = r_annotated_code_new (test_string); + RVector /**/ *test_annotations; + test_annotations = get_some_code_annotation_for_add (); + RCodeAnnotation *annotation; + r_vector_foreach (test_annotations, annotation) { + r_annotated_code_add_annotation (code, annotation); + } + + //Comparing + if (!test_equal (r_vector_index_ptr (&code->annotations, 0), r_vector_index_ptr (test_annotations, 0))) { + return false; + } + if (!test_equal (r_vector_index_ptr (&code->annotations, 1), r_vector_index_ptr (test_annotations, 1))) { + return false; + } + + r_vector_free (test_annotations); + r_annotated_code_free (code); + mu_end; +} + +static bool test_r_annotated_code_annotations_in() { + char *test_string = strdup ("abcdefghijklmnopqrtstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + RAnnotatedCode *code = r_annotated_code_new (test_string); + RVector /**/ *test_annotations; + test_annotations = get_some_annotations_for_in (); + + RCodeAnnotation *annotation; + r_vector_foreach (test_annotations, annotation) { + r_annotated_code_add_annotation (code, annotation); + } + + RPVector *out = r_annotated_code_annotations_in (code, 11); + //Expecting indices = 3, 4, 5 + mu_assert_eq (out->v.len, 3, "Additional annotations found. Bad output."); + if (!test_equal (*r_pvector_index_ptr (out, 0), r_vector_index_ptr (test_annotations, 3))) { + return false; + } + if (!test_equal (*r_pvector_index_ptr (out, 1), r_vector_index_ptr (test_annotations, 4))) { + return false; + } + if (!test_equal (*r_pvector_index_ptr (out, 2), r_vector_index_ptr (test_annotations, 5))) { + return false; + } + + r_vector_free (test_annotations); + r_pvector_free (out); + r_annotated_code_free (code); + mu_end; +} + +static bool test_r_annotated_code_annotations_range() { + char *test_string = strdup ("abcdefghijklmnopqrtstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + RAnnotatedCode *code = r_annotated_code_new (test_string); + RVector /**/ *test_annotations; + test_annotations = get_some_annotations_for_in (); + RCodeAnnotation *annotation; + r_vector_foreach (test_annotations, annotation) { + r_annotated_code_add_annotation (code, annotation); + } + + RPVector *out = r_annotated_code_annotations_range (code, 7, 16); + // Expecting indices = 2, 3, 4, 5 + mu_assert_eq (out->v.len, 4, "Additional annotations found. Bad output."); + if (!test_equal (*r_pvector_index_ptr (out, 0), r_vector_index_ptr (test_annotations, 2))) { + return false; + } + if (!test_equal (*r_pvector_index_ptr (out, 1), r_vector_index_ptr (test_annotations, 3))) { + return false; + } + if (!test_equal (*r_pvector_index_ptr (out, 2), r_vector_index_ptr (test_annotations, 4))) { + return false; + } + if (!test_equal (*r_pvector_index_ptr (out, 3), r_vector_index_ptr (test_annotations, 5))) { + return false; + } + + r_vector_free (test_annotations); + r_pvector_free (out); + r_annotated_code_free (code); + mu_end; +} + +static bool test_r_annotated_code_line_offsets() { + + RAnnotatedCode *code = get_hello_world (); + RVector *offsets = r_annotated_code_line_offsets (code); + mu_assert_eq (offsets->len, 6, "Number of offsets not expected"); + + ut64 *off = r_vector_index_ptr (offsets, 0); + mu_assert_eq_fmt (*off, UT64_MAX, "Unexpected offset", "%llu"); + off = r_vector_index_ptr (offsets, 1); + mu_assert_eq_fmt (*off, UT64_MAX, "Unexpected offset", "%llu"); + off = r_vector_index_ptr (offsets, 2); + mu_assert_eq_fmt (*off, UT64_MAX, "Unexpected offset", "%llu"); + off = r_vector_index_ptr (offsets, 3); + mu_assert_eq_fmt (*off, (ut64)4440, "Unexpected offset", "%llu"); + off = r_vector_index_ptr (offsets, 4); + mu_assert_eq_fmt (*off, (ut64)4447, "Unexpected offset", "%llu"); + off = r_vector_index_ptr (offsets, 5); + mu_assert_eq_fmt (*off, UT64_MAX, "Unexpected offset", "%llu"); + + r_vector_free (offsets); + r_annotated_code_free (code); + mu_end; +} + +static bool test_r_core_annotated_code_print_json() { + RAnnotatedCode *code = get_hello_world (); + char *actual; + char *expected = "{\"code\":\"\\nvoid main(void)\\n{\\n sym.imp.puts(\\\"Hello, World!\\\");\\n return;\\n}\\n\",\"annotations\":[{\"start\":1,\"end\":5,\"type\":\"syntax_highlight\",\"syntax_highlight\":\"datatype\"},{\"start\":6,\"end\":10,\"type\":\"syntax_highlight\",\"syntax_highlight\":\"function_name\"},{\"start\":11,\"end\":15,\"type\":\"syntax_highlight\",\"syntax_highlight\":\"keyword\"},{\"start\":23,\"end\":35,\"type\":\"syntax_highlight\",\"syntax_highlight\":\"function_name\"},{\"start\":36,\"end\":51,\"type\":\"syntax_highlight\",\"syntax_highlight\":\"constant_variable\"},{\"start\":23,\"end\":52,\"type\":\"offset\",\"offset\":4440},{\"start\":58,\"end\":64,\"type\":\"offset\",\"offset\":4447},{\"start\":58,\"end\":64,\"type\":\"syntax_highlight\",\"syntax_highlight\":\"keyword\"},{\"start\":58,\"end\":64,\"type\":\"offset\",\"offset\":4447}]}\n"; + r_cons_new (); + r_cons_push (); + r_core_annotated_code_print_json (code); + actual = strdup (r_cons_get_buffer ()); + r_cons_pop (); + mu_assert_streq (actual, expected, "pdgj OUTPUT DOES NOT MATCH"); + + r_cons_free (); + free (actual); + r_annotated_code_free (code); + mu_end; +} + +static bool test_r_core_annotated_code_print() { + RAnnotatedCode *code = get_hello_world (); + char *actual; + //Checking without line offset + char *expected_first = "\n" + "void main(void)\n" + "{\n" + " sym.imp.puts(\"Hello, World!\");\n" + " return;\n" + "}\n"; + r_cons_new (); + r_cons_push (); + r_core_annotated_code_print (code, NULL); + actual = strdup (r_cons_get_buffer ()); + r_cons_pop (); + mu_assert_streq (actual, expected_first, "pdg OUTPUT DOES NOT MATCH"); + r_cons_pop (); + + //Checking with offset - pdgo + RVector *offsets = r_annotated_code_line_offsets (code); + char *expected_second = " |\n" + " |void main(void)\n" + " |{\n" + " 0x00001158 | sym.imp.puts(\"Hello, World!\");\n" + " 0x0000115f | return;\n" + " |}\n"; + r_core_annotated_code_print (code, offsets); + free (actual); + actual = strdup (r_cons_get_buffer ()); + r_cons_pop (); + mu_assert_streq (actual, expected_second, "pdgo OUTPUT DOES NOT MATCH"); + r_cons_pop (); + + r_cons_free (); + free (actual); + r_vector_free (offsets); + r_annotated_code_free (code); + mu_end; +} + +static bool test_r_core_annotated_code_print_comment_cmds() { + RAnnotatedCode *code = get_hello_world (); + char *actual; + char *expected = "CCu base64:c3ltLmltcC5wdXRzKCJIZWxsbywgV29ybGQhIik= @ 0x1158\n" + "CCu base64:cmV0dXJu @ 0x115f\n"; + r_cons_new (); + r_cons_push (); + r_core_annotated_code_print_comment_cmds (code); + actual = strdup (r_cons_get_buffer ()); + r_cons_pop (); + mu_assert_streq (actual, expected, "pdg* OUTPUT DOES NOT MATCH"); + + r_cons_free (); + free (actual); + r_annotated_code_free (code); + mu_end; +} + +static int all_tests() { + mu_run_test (test_r_annotated_code_new); + mu_run_test (test_r_annotated_code_free); + mu_run_test (test_r_annotated_code_add_annotation); + mu_run_test (test_r_annotated_code_annotations_in); + mu_run_test (test_r_annotated_code_annotations_range); + mu_run_test (test_r_annotated_code_line_offsets); + mu_run_test (test_r_core_annotated_code_print_json); + mu_run_test (test_r_core_annotated_code_print); + mu_run_test (test_r_core_annotated_code_print_comment_cmds); + return tests_passed != tests_run; +} + +int main (int argc, char **argv) { + return all_tests (); +} \ No newline at end of file