Add Function Name, Constants, Globals and Local Variables to RAnnotatedCode (#17429)

* Annotation for function name (#17204)
* Annotations for Constant Variables and Global Variables for the decompiler (#17281)
* Annotation For Function Variables (#17375)
* function variable annotation added (includes local variable and function parameter)
* API for checking if an annotation is a reference or function variable. (#17386)
* Update docs in annotate code API  (#17397)
* Unit tests for annotated code API (#17403)
This commit is contained in:
NIRMAL MANOJ C 2020-08-10 16:10:16 +05:30 committed by GitHub
parent 18c052c266
commit b4677b4dfe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 272 additions and 91 deletions

View File

@ -28,6 +28,27 @@ R_API void r_core_annotated_code_print_json(RAnnotatedCode *code) {
pj_ks (pj, "type", "offset");
pj_kn (pj, "offset", annotation->offset.offset);
break;
case R_CODE_ANNOTATION_TYPE_FUNCTION_NAME:
pj_ks (pj, "type", "function_name");
pj_ks (pj, "name", annotation->reference.name);
pj_kn (pj, "offset", annotation->reference.offset);
break;
case R_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE:
pj_ks (pj, "type", "global_variable");
pj_kn (pj, "offset", annotation->reference.offset);
break;
case R_CODE_ANNOTATION_TYPE_CONSTANT_VARIABLE:
pj_ks (pj, "type", "constant_variable");
pj_kn (pj, "offset", annotation->reference.offset);
break;
case R_CODE_ANNOTATION_TYPE_LOCAL_VARIABLE:
pj_ks (pj, "type", "local_variable");
pj_ks (pj, "name", annotation->variable.name);
break;
case R_CODE_ANNOTATION_TYPE_FUNCTION_PARAMETER:
pj_ks (pj, "type", "function_parameter");
pj_ks (pj, "name", annotation->variable.name);
break;
case R_CODE_ANNOTATION_TYPE_SYNTAX_HIGHLIGHT:
pj_ks (pj, "type", "syntax_highlight");
switch (annotation->syntax_highlight.type) {

View File

@ -930,39 +930,34 @@ 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
*/
* @brief Prints the data contained in the specified RAnnotatedCode in JSON format.
*
* The function will print the output in console using the function r_cons_printf();
*
* @param code Pointer to a RAnnotatedCode.
*/
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
*/
* @brief Prints the decompiled code from the specified RAnnotatedCode.
*
* 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();
*
* @param code Pointer to a RAnnotatedCode.
* @param line_offsets Pointer to a @ref RVector that contains offsets for the decompiled code.
*/
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
*/
* @brief Prints the decompiled code as comments
*
* This function is used for the output of command pdg*
* Output will be printed in console using the function r_cons_printf();
*
* @param code Pointer to a RAnnotatedCode.
*/
R_API void r_core_annotated_code_print_comment_cmds(RAnnotatedCode *code);
#endif

View File

@ -21,108 +21,155 @@ typedef enum r_syntax_highlight_type_t {
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.
*/
/** Represents the type of annnotation. */
typedef enum r_code_annotation_type_t {
R_CODE_ANNOTATION_TYPE_OFFSET,
R_CODE_ANNOTATION_TYPE_SYNTAX_HIGHLIGHT,
R_CODE_ANNOTATION_TYPE_OFFSET, /*!< Gives the offset of the specified range in annotation. */
R_CODE_ANNOTATION_TYPE_SYNTAX_HIGHLIGHT, /*!< Represents the kind of data the specified range represents for highlighting purposes. */
R_CODE_ANNOTATION_TYPE_FUNCTION_NAME, /*!< Specified range in annotation represents a function name. */
R_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE, /*!< Specified range in annotation represents a global variable. */
R_CODE_ANNOTATION_TYPE_CONSTANT_VARIABLE, /*!< Specified range in annotation represents a constant variable with an address. */
R_CODE_ANNOTATION_TYPE_LOCAL_VARIABLE, /*!< Specified range in annotation represents a local variable. */
R_CODE_ANNOTATION_TYPE_FUNCTION_PARAMETER, /*!< Specified range in annotation represents a function parameter. */
// ...
} RCodeAnnotationType;
/**
* \brief Annotations for the decompiled code are represented using this structure.
*/
typedef struct r_code_annotation_t {
size_t start;
size_t end;
size_t start; /**< Start of the range in the annotation(inclusive). */
size_t end; /**< End of the range in the annotation(exclusive). */
RCodeAnnotationType type;
union {
/** If the annotation is of type R_CODE_ANNOTATION_TYPE_OFFSET,
* offset should be stored in the struct named offset in this union.
*/
struct {
ut64 offset;
} offset;
/** If the annotation is of type R_CODE_ANNOTATION_TYPE_SYNTAX_HIGHLIGHT,
* type of the syntax highlight will be stored in the struct named syntax_highlight
* in this union.
*/
struct {
RSyntaxHighlightType type;
} syntax_highlight;
/** Information in annotations of type R_CODE_ANNOTATION_TYPE_FUNCTION_NAME,
* R_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE, and R_CODE_ANNOTATION_TYPE_CONSTANT_VARIABLE
* will be stored in the struct named reference in this union.
*/
struct {
char *name;
ut64 offset;
} reference;
/** Information in annotations of type R_CODE_ANNOTATION_TYPE_LOCAL_VARIABLE
* and R_CODE_ANNOTATION_TYPE_FUNCTION_PARAMETER will be stored in the
* struct named variable in this union.
*/
struct {
char *name;
} variable;
};
} RCodeAnnotation;
/**
* \brief This structure contains the decompiled code and all the annotations for the decompiled code.
*/
typedef struct r_annotated_code_t {
char *code; // owned
RVector /*<RCodeAnnotation>*/ annotations;
char *code; /**< Decompiled code. RAnnotatedCode owns this string and it must free it. */
RVector annotations; /**< @ref RVector <RCodeAnnotation> contains the list of annotations for the decompiled code. */
} 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.
* @brief Create and initialize a RAnnotatedCode structure and returns its pointer.
*
* Return: Pointer to the new RAnnotatedCode structure created.
* This function creates and initializes a new RAnnotatedCode
* structure with the specified decompiled code that's passed
* as an argument. Here, the argument code must be a string that can be deallocated.
* This will initialize @ref RVector <RCodeAnnotation> annotations as well.
*
* @param code A deallocatable character array.
* @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.
* @brief Deallocates the dynamically allocated memory for the specified RAnnotatedCode.
*
* Return: Nothing.
* @param code Pointer to a RAnnotatedCode.
*/
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.
* @brief Deallocates dynamically allocated memory for the specified annotation.
*
* Return: Nothing.
* This function recognizes the type of the specified annotation and
* frees memory that is dynamically allocated for it.
*
* @param e Pointer to the annotation.
* @param user Always NULL for this function. Present here for this function to be of the type @ref RVectorFree.
*/
R_API void r_annotation_free(void *e, void *user);
/**
* @brief Checks if the specified annotation is a reference.
*
* This function recognizes the type of the specified annotation and returns true if its
* type is any of the following three: R_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE,
* R_CODE_ANNOTATION_TYPE_CONSTANT_VARIABLE, R_CODE_ANNOTATION_TYPE_FUNCTION_NAME
*
* @param annotation Pointer to an annotation.
* @return Returns true if the specified annotation is a reference.
*/
R_API bool r_annotation_is_reference(RCodeAnnotation *annotation);
/**
* @brief Checks if the specified annotation is a function variable.
*
* This function recognizes the type of the specified annotation and returns true if its
* type is any of the following two: R_CODE_ANNOTATION_TYPE_LOCAL_VARIABLE,
* R_CODE_ANNOTATION_TYPE_FUNCTION_PARAMETER
*
* @param annotation Pointer to an annotation.
* @return Returns true if the specified annotation is a function variable.
*/
R_API bool r_annotation_is_variable(RCodeAnnotation *annotation);
/**
* @brief Inserts the specified annotation into the list of annotations in the specified RAnnotatedCode.
*
* @param code Pointer to a RAnnotatedCode.
* @param annotation Pointer to an annotation.
*/
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
* @brief Returns all annotations with range that contains the given offset.
*
* Creates a @ref RPVector <RCodeAnnotation> and inserts the pointers to all annotations in which
* annotation->start <= offset < annotation->end.
*
* Return: Pointer to the RPVecrtor created.
* @param code Pointer to a RAnnotatedCode.
* @param offset Offset.
* @return Pointer to the @ref RPVector 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).
* @brief Returns all annotations with range that overlap with the specified range.
*
* Return: Pointer to the RPVecrtor created.
* Creates an @ref RPVector <RCodeAnnotation> and inserts the pointers to all annotations whose
* range overlap with range specified.
*
* @param code Pointer to a RAnnotatedCode.
* @param start Start of the range(inclusive).
* @param end End of the range(exclusive).
* @return Pointer to the @ref RPVector 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).
* @brief Returns the offset for every line of decompiled code in the specified RAnnotatedCode.
*
* Creates an @ref RVector <ut64> and inserts the offsets for every seperate line of decompiled code in
* the specified RAnnotatedCode.
* 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.
*
* @param code Pointer to a RAnnotatedCode.
* @return Pointer to the @ref RVector created.
*/
R_API RVector *r_annotated_code_line_offsets(RAnnotatedCode *code);

View File

@ -9,10 +9,28 @@ R_API RAnnotatedCode *r_annotated_code_new(char *code) {
return NULL;
}
r->code = code;
r_vector_init (&r->annotations, sizeof (RCodeAnnotation), NULL, NULL);
r_vector_init (&r->annotations, sizeof (RCodeAnnotation), r_annotation_free, NULL);
return r;
}
R_API void r_annotation_free(void *e, void *user) {
(void)user;
RCodeAnnotation *annotation = e;
if (annotation->type == R_CODE_ANNOTATION_TYPE_FUNCTION_NAME) {
free (annotation->reference.name);
} else if (annotation->type == R_CODE_ANNOTATION_TYPE_LOCAL_VARIABLE || annotation->type == R_CODE_ANNOTATION_TYPE_FUNCTION_PARAMETER) {
free (annotation->variable.name);
}
}
R_API bool r_annotation_is_reference(RCodeAnnotation *annotation) {
return (annotation->type == R_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE || annotation->type == R_CODE_ANNOTATION_TYPE_CONSTANT_VARIABLE || annotation->type == R_CODE_ANNOTATION_TYPE_FUNCTION_NAME);
}
R_API bool r_annotation_is_variable(RCodeAnnotation *annotation) {
return (annotation->type == R_CODE_ANNOTATION_TYPE_LOCAL_VARIABLE || annotation->type == R_CODE_ANNOTATION_TYPE_FUNCTION_PARAMETER);
}
R_API void r_annotated_code_free(RAnnotatedCode *code) {
if (!code) {
return;

View File

@ -21,6 +21,31 @@ static RCodeAnnotation make_code_annotation(int st, int en, RCodeAnnotationType
return annotation;
}
static RCodeAnnotation make_variable_annotation(int st, int en, RCodeAnnotationType typec,
const char *name) {
RCodeAnnotation annotation = { 0 };
annotation.start = st;
annotation.end = en;
annotation.type = typec;
annotation.variable.name = strdup (name);
return annotation;
}
static RCodeAnnotation make_reference_annotation(int st, int en, RCodeAnnotationType typec,
ut64 offset, const char *name) {
RCodeAnnotation annotation = { 0 };
annotation.start = st;
annotation.end = en;
annotation.type = typec;
annotation.reference.offset = offset;
if (annotation.type == R_CODE_ANNOTATION_TYPE_FUNCTION_NAME) {
annotation.reference.name = strdup (name);
} else {
annotation.reference.name = NULL;
}
return annotation;
}
static RVector *get_some_code_annotation_for_add(void) {
RVector *test_annotations = r_vector_new (sizeof (RCodeAnnotation), NULL, NULL);
RCodeAnnotation annotation;
@ -97,6 +122,22 @@ static RAnnotatedCode *get_hello_world(void) {
return code;
}
static RAnnotatedCode *get_all_context_annotated_code(void) {
char *test_string = strdup ("\nfunc-name\nconst-var\n global-var(\"Hello, local-var\");\n function-param\n}\n");
RAnnotatedCode *code = r_annotated_code_new (test_string);
RCodeAnnotation function_name = make_reference_annotation (1, 10, R_CODE_ANNOTATION_TYPE_FUNCTION_NAME, 1234, "func-name");
RCodeAnnotation constant_variable = make_reference_annotation (10, 19, R_CODE_ANNOTATION_TYPE_CONSTANT_VARIABLE, 12345, NULL);
RCodeAnnotation global_variable = make_reference_annotation (23, 33, R_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE, 123456, NULL);
RCodeAnnotation local_variable = make_variable_annotation (42, 51, R_CODE_ANNOTATION_TYPE_LOCAL_VARIABLE, "local-var");
RCodeAnnotation function_parameter = make_variable_annotation (59, 73, R_CODE_ANNOTATION_TYPE_FUNCTION_PARAMETER, "function-param");
r_annotated_code_add_annotation (code, &function_name);
r_annotated_code_add_annotation (code, &constant_variable);
r_annotated_code_add_annotation (code, &global_variable);
r_annotated_code_add_annotation (code, &local_variable);
r_annotated_code_add_annotation (code, &function_parameter);
return code;
}
static bool test_r_annotated_code_new(void) {
//Testing RAnnoatedCode->code
char *test_string = strdup ("How are you?");
@ -267,6 +308,23 @@ static bool test_r_core_annotated_code_print_json(void) {
mu_end;
}
/**
* @brief Tests JSON output for all context related annotations
*/
static bool test_r_core_annotated_code_print_json_context_annotations(void) {
RAnnotatedCode *code = get_all_context_annotated_code ();
char *expected = "{\"code\":\"\\nfunc-name\\nconst-var\\n global-var(\\\"Hello, local-var\\\");\\n function-param\\n}\\n\",\"annotations\":[{\"start\":1,\"end\":10,\"type\":\"function_name\",\"name\":\"func-name\",\"offset\":1234},{\"start\":10,\"end\":19,\"type\":\"constant_variable\",\"offset\":12345},{\"start\":23,\"end\":33,\"type\":\"global_variable\",\"offset\":123456},{\"start\":42,\"end\":51,\"type\":\"local_variable\",\"name\":\"local-var\"},{\"start\":59,\"end\":73,\"type\":\"function_parameter\",\"name\":\"function-param\"}]}\n";
r_cons_new ();
r_cons_push ();
r_core_annotated_code_print_json (code);
char *actual = strdup (r_cons_get_buffer ());
r_cons_pop ();
mu_assert_streq (actual, expected, "r_core_annotated_code_print_json() output doesn't match with the expected output");
free (actual);
r_annotated_code_free (code);
mu_end;
}
static bool test_r_core_annotated_code_print(void) {
RAnnotatedCode *code = get_hello_world ();
char *actual;
@ -325,6 +383,46 @@ static bool test_r_core_annotated_code_print_comment_cmds(void) {
mu_end;
}
/**
* @brief Tests functions r_annotation_is_variable(), r_annotation_is_reference(), and r_annotation_free()
*/
static bool test_r_annotation_free_and_is_annotation_type_functions(void) {
// Making all types of annotations
RCodeAnnotation offset = make_code_annotation (58, 64, R_CODE_ANNOTATION_TYPE_OFFSET, 4447, R_SYNTAX_HIGHLIGHT_TYPE_KEYWORD);
RCodeAnnotation syntax_highlight = make_code_annotation (1, 5, R_CODE_ANNOTATION_TYPE_SYNTAX_HIGHLIGHT, 123, R_SYNTAX_HIGHLIGHT_TYPE_DATATYPE);
RCodeAnnotation local_variable = make_variable_annotation (1, 2, R_CODE_ANNOTATION_TYPE_LOCAL_VARIABLE, "RADARE2");
RCodeAnnotation function_parameter = make_variable_annotation (4, 10, R_CODE_ANNOTATION_TYPE_LOCAL_VARIABLE, "Cutter");
RCodeAnnotation function_name = make_reference_annotation (10, 12, R_CODE_ANNOTATION_TYPE_FUNCTION_NAME, 123513, "test_function");
RCodeAnnotation global_variable = make_reference_annotation (10, 12, R_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE, 1234234, NULL);
RCodeAnnotation constant_variable = make_reference_annotation (21, 200, R_CODE_ANNOTATION_TYPE_CONSTANT_VARIABLE, 12342314, NULL);
// Test r_annotation_is_variable()
char *error_message = "r_annotation_is_variable() result doesn't match with the expected output";
mu_assert_true (r_annotation_is_variable (&local_variable), error_message);
mu_assert_true (r_annotation_is_variable (&function_parameter), error_message);
mu_assert_false (r_annotation_is_variable (&function_name), error_message);
mu_assert_false (r_annotation_is_variable (&global_variable), error_message);
mu_assert_false (r_annotation_is_variable (&constant_variable), error_message);
mu_assert_false (r_annotation_is_variable (&offset), error_message);
mu_assert_false (r_annotation_is_variable (&syntax_highlight), error_message);
// Test r_annotation_is_reference()
error_message = "r_annotation_is_reference() result doesn't match with the expected output";
mu_assert_true (r_annotation_is_reference (&function_name), error_message);
mu_assert_true (r_annotation_is_reference (&global_variable), error_message);
mu_assert_true (r_annotation_is_reference (&constant_variable), error_message);
mu_assert_false (r_annotation_is_reference (&local_variable), error_message);
mu_assert_false (r_annotation_is_reference (&function_parameter), error_message);
mu_assert_false (r_annotation_is_reference (&offset), error_message);
mu_assert_false (r_annotation_is_reference (&syntax_highlight), error_message);
// Free dynamically allocated memory for annotations.
// This is also supposed to be a test of r_annotation_free() for run errors.
r_annotation_free (&local_variable, NULL);
r_annotation_free (&function_parameter, NULL);
r_annotation_free (&function_name, NULL);
r_annotation_free (&global_variable, NULL);
r_annotation_free (&constant_variable, NULL);
mu_end;
}
static int all_tests(void) {
mu_run_test (test_r_annotated_code_new);
mu_run_test (test_r_annotated_code_free);
@ -333,8 +431,10 @@ static int all_tests(void) {
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_json_context_annotations);
mu_run_test (test_r_core_annotated_code_print);
mu_run_test (test_r_core_annotated_code_print_comment_cmds);
mu_run_test (test_r_annotation_free_and_is_annotation_type_functions);
return tests_passed != tests_run;
}