mirror of
https://github.com/radareorg/radare2.git
synced 2024-12-13 07:57:35 +00:00
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
This commit is contained in:
parent
688c411afe
commit
305cc00766
@ -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
|
||||
|
262
libr/core/cannotated_code.c
Normal file
262
libr/core/cannotated_code.c
Normal file
@ -0,0 +1,262 @@
|
||||
#include <r_util/r_annotated_code.h>
|
||||
|
||||
#include <r_util.h>
|
||||
#include <r_core.h>
|
||||
#include <r_types.h>
|
||||
#include <r_vector.h>
|
||||
|
||||
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);
|
||||
}
|
@ -65,6 +65,7 @@ r_core_sources = [
|
||||
'yank.c',
|
||||
'p/core_a2f.c',
|
||||
'p/core_java.c',
|
||||
'cannotated_code.c'
|
||||
]
|
||||
|
||||
r_core_inc = []
|
||||
|
@ -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
|
||||
|
133
libr/include/r_util/r_annotated_code.h
Normal file
133
libr/include/r_util/r_annotated_code.h
Normal file
@ -0,0 +1,133 @@
|
||||
|
||||
#ifndef R_ANNOTATEDCODE_H
|
||||
#define R_ANNOTATEDCODE_H
|
||||
|
||||
// #include <r_core.h>
|
||||
#include <r_types.h>
|
||||
#include <r_vector.h>
|
||||
|
||||
#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 /*<RCodeAnnotation>*/ 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<RCodeAnnotation> 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<RCodeAnnotation> 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
|
@ -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')
|
||||
|
||||
|
@ -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
|
||||
|
84
libr/util/annotated_code.c
Normal file
84
libr/util/annotated_code.c
Normal file
@ -0,0 +1,84 @@
|
||||
|
||||
#include <r_util/r_annotated_code.h>
|
||||
#include <r_core.h>
|
||||
#include <r_util.h>
|
||||
|
||||
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;
|
||||
}
|
@ -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]
|
||||
|
@ -9,6 +9,7 @@ if get_option('enable_tests')
|
||||
'anal_meta',
|
||||
'anal_var',
|
||||
'anal_xrefs',
|
||||
'annotated_code',
|
||||
'base64',
|
||||
'bin',
|
||||
'bitmap',
|
||||
|
343
test/unit/test_annotated_code.c
Normal file
343
test/unit/test_annotated_code.c
Normal file
@ -0,0 +1,343 @@
|
||||
#include <r_util.h>
|
||||
#include <r_vector.h>
|
||||
#include <r_core.h>
|
||||
#include <r_cons.h>
|
||||
#include <r_util/r_annotated_code.h>
|
||||
|
||||
#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 /*<RCodeAnnotation>*/ *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 /*<RCodeAnnotation>*/ *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 /*<RCodeAnnotation>*/ *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 /*<RCodeAnnotation>*/ *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 ();
|
||||
}
|
Loading…
Reference in New Issue
Block a user