mirror of
https://github.com/zlgopen/awtk.git
synced 2025-05-08 19:44:45 +08:00
improve fscript to support debugger
This commit is contained in:
parent
c2553a5a28
commit
0ba781528c
@ -1,4 +1,8 @@
|
||||
# 最新动态
|
||||
|
||||
2022/01/16
|
||||
* 完善fscript支持调试器。
|
||||
|
||||
2022/01/13
|
||||
* 优化tk\_object\_get\_child\_object(感谢雨欣提供补丁)
|
||||
|
||||
|
@ -13,6 +13,7 @@ TOOLS_NAME = os.environ['TOOLS_NAME']
|
||||
|
||||
BASE_SOURCES = Glob('layouters/*.c') + \
|
||||
Glob('base/*.c') + \
|
||||
Glob('debugger/*.c') + \
|
||||
Glob('ui_loader/*.c') + \
|
||||
Glob('svg/*.c') + \
|
||||
Glob('clip_board/*.c') + \
|
||||
|
6
src/debugger/README.md
Normal file
6
src/debugger/README.md
Normal file
@ -0,0 +1,6 @@
|
||||
# fscript 调试器
|
||||
|
||||
## TODO
|
||||
* step in/step out
|
||||
* hot reload(update code)
|
||||
|
12
src/debugger/SConscript
Normal file
12
src/debugger/SConscript
Normal file
@ -0,0 +1,12 @@
|
||||
import os
|
||||
import copy
|
||||
import awtk_config as awtk
|
||||
|
||||
BIN_DIR=os.environ['BIN_DIR'];
|
||||
LIB_DIR=os.environ['LIB_DIR'];
|
||||
|
||||
sources = Glob('*.c')
|
||||
|
||||
env=DefaultEnvironment().Clone()
|
||||
env.Library(os.path.join(LIB_DIR, 'debugger'), sources, LIBS=['tkc'])
|
||||
|
180
src/debugger/debugger.c
Normal file
180
src/debugger/debugger.c
Normal file
@ -0,0 +1,180 @@
|
||||
/**
|
||||
* File: debugger.c
|
||||
* Author: AWTK Develop Team
|
||||
* Brief: debugger
|
||||
*
|
||||
* Copyright (c) 2022 - 2022 Guangzhou ZHIYUAN Electronics Co.,Ltd.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* License file for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* History:
|
||||
* ================================================================
|
||||
* 2022-01-11 Li XianJing <xianjimli@hotmail.com> created
|
||||
*
|
||||
*/
|
||||
|
||||
#include "debugger/debugger.h"
|
||||
|
||||
ret_t debugger_lock(debugger_t* debugger) {
|
||||
return_value_if_fail(debugger != NULL && debugger->vt != NULL, RET_BAD_PARAMS);
|
||||
return_value_if_fail(debugger->vt->lock != NULL, RET_BAD_PARAMS);
|
||||
|
||||
return debugger->vt->lock(debugger);
|
||||
}
|
||||
|
||||
ret_t debugger_unlock(debugger_t* debugger) {
|
||||
return_value_if_fail(debugger != NULL && debugger->vt != NULL, RET_BAD_PARAMS);
|
||||
return_value_if_fail(debugger->vt->unlock != NULL, RET_BAD_PARAMS);
|
||||
|
||||
return debugger->vt->unlock(debugger);
|
||||
}
|
||||
|
||||
ret_t debugger_stop(debugger_t* debugger) {
|
||||
return_value_if_fail(debugger != NULL && debugger->vt != NULL, RET_BAD_PARAMS);
|
||||
return_value_if_fail(debugger->vt->stop != NULL, RET_BAD_PARAMS);
|
||||
|
||||
return debugger->vt->stop(debugger);
|
||||
}
|
||||
|
||||
ret_t debugger_pause(debugger_t* debugger) {
|
||||
return_value_if_fail(debugger != NULL && debugger->vt != NULL, RET_BAD_PARAMS);
|
||||
return_value_if_fail(debugger->vt->pause != NULL, RET_BAD_PARAMS);
|
||||
|
||||
return debugger->vt->pause(debugger);
|
||||
}
|
||||
|
||||
bool_t debugger_is_paused(debugger_t* debugger) {
|
||||
return_value_if_fail(debugger != NULL && debugger->vt != NULL, FALSE);
|
||||
return_value_if_fail(debugger->vt->is_paused != NULL, FALSE);
|
||||
|
||||
return debugger->vt->is_paused(debugger);
|
||||
}
|
||||
|
||||
bool_t debugger_match(debugger_t* debugger, const char* code_id) {
|
||||
return_value_if_fail(debugger != NULL && debugger->vt != NULL, FALSE);
|
||||
return_value_if_fail(debugger->vt->match != NULL && code_id != NULL, FALSE);
|
||||
|
||||
return debugger->vt->match(debugger, code_id);
|
||||
}
|
||||
|
||||
ret_t debugger_next(debugger_t* debugger) {
|
||||
return_value_if_fail(debugger != NULL && debugger->vt != NULL, RET_BAD_PARAMS);
|
||||
return_value_if_fail(debugger->vt->next != NULL, RET_BAD_PARAMS);
|
||||
|
||||
return debugger->vt->next(debugger);
|
||||
}
|
||||
|
||||
ret_t debugger_step_in(debugger_t* debugger) {
|
||||
return_value_if_fail(debugger != NULL && debugger->vt != NULL, RET_BAD_PARAMS);
|
||||
return_value_if_fail(debugger->vt->step_in != NULL, RET_BAD_PARAMS);
|
||||
|
||||
return debugger->vt->step_in(debugger);
|
||||
}
|
||||
|
||||
ret_t debugger_step_out(debugger_t* debugger) {
|
||||
return_value_if_fail(debugger != NULL && debugger->vt != NULL, RET_BAD_PARAMS);
|
||||
return_value_if_fail(debugger->vt->step_out != NULL, RET_BAD_PARAMS);
|
||||
|
||||
return debugger->vt->step_out(debugger);
|
||||
}
|
||||
|
||||
ret_t debugger_step_over(debugger_t* debugger) {
|
||||
return_value_if_fail(debugger != NULL && debugger->vt != NULL, RET_BAD_PARAMS);
|
||||
return_value_if_fail(debugger->vt->step_over != NULL, RET_BAD_PARAMS);
|
||||
|
||||
return debugger->vt->step_over(debugger);
|
||||
}
|
||||
|
||||
ret_t debugger_continue(debugger_t* debugger) {
|
||||
return_value_if_fail(debugger != NULL && debugger->vt != NULL, RET_BAD_PARAMS);
|
||||
return_value_if_fail(debugger->vt->continve != NULL, RET_BAD_PARAMS);
|
||||
|
||||
return debugger->vt->continve(debugger);
|
||||
}
|
||||
|
||||
tk_object_t* debugger_get_local(debugger_t* debugger, uint32_t frame_index) {
|
||||
return_value_if_fail(debugger != NULL && debugger->vt != NULL, NULL);
|
||||
return_value_if_fail(debugger->vt->get_local != NULL, NULL);
|
||||
|
||||
return debugger->vt->get_local(debugger, frame_index);
|
||||
}
|
||||
|
||||
tk_object_t* debugger_get_self(debugger_t* debugger) {
|
||||
return_value_if_fail(debugger != NULL && debugger->vt != NULL, NULL);
|
||||
return_value_if_fail(debugger->vt->get_self != NULL, NULL);
|
||||
|
||||
return debugger->vt->get_self(debugger);
|
||||
}
|
||||
|
||||
tk_object_t* debugger_get_global(debugger_t* debugger) {
|
||||
return_value_if_fail(debugger != NULL && debugger->vt != NULL, NULL);
|
||||
return_value_if_fail(debugger->vt->get_global != NULL, NULL);
|
||||
|
||||
return debugger->vt->get_global(debugger);
|
||||
}
|
||||
|
||||
ret_t debugger_get_callstack(debugger_t* debugger, binary_data_t* callstack) {
|
||||
return_value_if_fail(debugger != NULL && debugger->vt != NULL, RET_BAD_PARAMS);
|
||||
return_value_if_fail(debugger->vt->get_callstack != NULL, RET_BAD_PARAMS);
|
||||
return_value_if_fail(callstack != NULL, RET_BAD_PARAMS);
|
||||
|
||||
return debugger->vt->get_callstack(debugger, callstack);
|
||||
}
|
||||
|
||||
ret_t debugger_clear_break_points(debugger_t* debugger) {
|
||||
return_value_if_fail(debugger != NULL && debugger->vt != NULL, RET_BAD_PARAMS);
|
||||
return_value_if_fail(debugger->vt->clear_break_points != NULL, RET_BAD_PARAMS);
|
||||
|
||||
return debugger->vt->clear_break_points(debugger);
|
||||
}
|
||||
|
||||
ret_t debugger_set_break_point(debugger_t* debugger, uint32_t line) {
|
||||
return_value_if_fail(debugger != NULL && debugger->vt != NULL, RET_BAD_PARAMS);
|
||||
return_value_if_fail(debugger->vt->set_break_point != NULL, RET_BAD_PARAMS);
|
||||
|
||||
return debugger->vt->set_break_point(debugger, line);
|
||||
}
|
||||
|
||||
ret_t debugger_remove_break_point(debugger_t* debugger, uint32_t line) {
|
||||
return_value_if_fail(debugger != NULL && debugger->vt != NULL, RET_BAD_PARAMS);
|
||||
return_value_if_fail(debugger->vt->remove_break_point != NULL, RET_BAD_PARAMS);
|
||||
|
||||
return debugger->vt->remove_break_point(debugger, line);
|
||||
}
|
||||
|
||||
ret_t debugger_init(debugger_t* debugger, const char* lang, const char* code_id) {
|
||||
return_value_if_fail(debugger != NULL && debugger->vt != NULL, RET_BAD_PARAMS);
|
||||
return_value_if_fail(debugger->vt->init != NULL, RET_BAD_PARAMS);
|
||||
return_value_if_fail(lang != NULL && code_id != NULL, RET_BAD_PARAMS);
|
||||
|
||||
return debugger->vt->init(debugger, lang, code_id);
|
||||
}
|
||||
|
||||
ret_t debugger_get_code(debugger_t* debugger, binary_data_t* code) {
|
||||
return_value_if_fail(debugger != NULL && debugger->vt != NULL, RET_BAD_PARAMS);
|
||||
return_value_if_fail(debugger->vt->get_code != NULL, RET_BAD_PARAMS);
|
||||
return_value_if_fail(code != NULL, RET_BAD_PARAMS);
|
||||
|
||||
return debugger->vt->get_code(debugger, code);
|
||||
}
|
||||
|
||||
ret_t debugger_update_code(debugger_t* debugger, const binary_data_t* code) {
|
||||
return_value_if_fail(debugger != NULL && debugger->vt != NULL, RET_BAD_PARAMS);
|
||||
return_value_if_fail(debugger->vt->update_code != NULL, RET_BAD_PARAMS);
|
||||
return_value_if_fail(code != NULL && code->data != NULL, RET_BAD_PARAMS);
|
||||
|
||||
return debugger->vt->update_code(debugger, code);
|
||||
}
|
||||
|
||||
ret_t debugger_deinit(debugger_t* debugger) {
|
||||
return_value_if_fail(debugger != NULL && debugger->vt != NULL, RET_BAD_PARAMS);
|
||||
return_value_if_fail(debugger->vt->deinit != NULL, RET_BAD_PARAMS);
|
||||
|
||||
return debugger->vt->deinit(debugger);
|
||||
}
|
322
src/debugger/debugger.h
Normal file
322
src/debugger/debugger.h
Normal file
@ -0,0 +1,322 @@
|
||||
/**
|
||||
* File: debugger.h
|
||||
* Author: AWTK Develop Team
|
||||
* Brief: debugger
|
||||
*
|
||||
* Copyright (c) 2022 - 2022 Guangzhou ZHIYUAN Electronics Co.,Ltd.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* License file for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* History:
|
||||
* ================================================================
|
||||
* 2022-01-11 Li XianJing <xianjimli@hotmail.com> created
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TK_DEBUGGER_H
|
||||
#define TK_DEBUGGER_H
|
||||
|
||||
#include "tkc/event.h"
|
||||
#include "tkc/object.h"
|
||||
#include "tkc/types_def.h"
|
||||
#include "debugger/debugger_const.h"
|
||||
|
||||
BEGIN_C_DECLS
|
||||
|
||||
struct _debugger_t;
|
||||
typedef struct _debugger_t debugger_t;
|
||||
|
||||
typedef ret_t (*debugger_lock_t)(debugger_t* debugger);
|
||||
typedef ret_t (*debugger_unlock_t)(debugger_t* debugger);
|
||||
typedef ret_t (*debugger_stop_t)(debugger_t* debugger);
|
||||
typedef ret_t (*debugger_pause_t)(debugger_t* debugger);
|
||||
typedef bool_t (*debugger_match_t)(debugger_t* debugger, const char* code_id);
|
||||
typedef bool_t (*debugger_is_paused_t)(debugger_t* debugger);
|
||||
|
||||
typedef ret_t (*debugger_next_t)(debugger_t* debugger);
|
||||
typedef ret_t (*debugger_step_in_t)(debugger_t* debugger);
|
||||
typedef ret_t (*debugger_step_out_t)(debugger_t* debugger);
|
||||
typedef ret_t (*debugger_step_over_t)(debugger_t* debugger);
|
||||
typedef ret_t (*debugger_continue_t)(debugger_t* debugger);
|
||||
typedef tk_object_t* (*debugger_get_local_t)(debugger_t* debugger, uint32_t frame_index);
|
||||
typedef tk_object_t* (*debugger_get_self_t)(debugger_t* debugger);
|
||||
typedef tk_object_t* (*debugger_get_global_t)(debugger_t* debugger);
|
||||
typedef ret_t (*debugger_get_callstack_t)(debugger_t* debugger, binary_data_t* callstack);
|
||||
typedef ret_t (*debugger_clear_break_points_t)(debugger_t* debugger);
|
||||
typedef ret_t (*debugger_set_break_point_t)(debugger_t* debugger, uint32_t line);
|
||||
typedef ret_t (*debugger_remove_break_point_t)(debugger_t* debugger, uint32_t line);
|
||||
typedef ret_t (*debugger_get_code_t)(debugger_t* debugger, binary_data_t* code);
|
||||
typedef ret_t (*debugger_update_code_t)(debugger_t* debugger, const binary_data_t* code);
|
||||
typedef ret_t (*debugger_init_t)(debugger_t* debugger, const char* lang, const char* code_id);
|
||||
typedef ret_t (*debugger_deinit_t)(debugger_t* debugger);
|
||||
|
||||
typedef debugger_t* (*debugger_fscript_create_t)(void);
|
||||
|
||||
typedef struct _debugger_vtable_t {
|
||||
const char* lang;
|
||||
|
||||
debugger_init_t init;
|
||||
debugger_lock_t lock;
|
||||
debugger_unlock_t unlock;
|
||||
debugger_stop_t stop;
|
||||
debugger_pause_t pause;
|
||||
debugger_match_t match;
|
||||
debugger_is_paused_t is_paused;
|
||||
|
||||
debugger_next_t next;
|
||||
debugger_step_in_t step_in;
|
||||
debugger_step_out_t step_out;
|
||||
debugger_step_over_t step_over;
|
||||
debugger_continue_t continve;
|
||||
debugger_get_local_t get_local;
|
||||
debugger_get_self_t get_self;
|
||||
debugger_get_global_t get_global;
|
||||
debugger_get_callstack_t get_callstack;
|
||||
debugger_get_code_t get_code;
|
||||
debugger_update_code_t update_code;
|
||||
debugger_set_break_point_t set_break_point;
|
||||
debugger_remove_break_point_t remove_break_point;
|
||||
debugger_clear_break_points_t clear_break_points;
|
||||
debugger_deinit_t deinit;
|
||||
} debugger_vtable_t;
|
||||
|
||||
/**
|
||||
* @class debugger_t
|
||||
* 调试器接口。
|
||||
*
|
||||
*/
|
||||
struct _debugger_t {
|
||||
tk_object_t object;
|
||||
const debugger_vtable_t* vt;
|
||||
};
|
||||
|
||||
/**
|
||||
* @method debugger_lock
|
||||
* 锁定debugger对象。
|
||||
* @param {debugger_t*} debugger debugger对象。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t debugger_lock(debugger_t* debugger);
|
||||
|
||||
/**
|
||||
* @method debugger_unlock
|
||||
* 解锁debugger对象。
|
||||
* @param {debugger_t*} debugger debugger对象。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t debugger_unlock(debugger_t* debugger);
|
||||
|
||||
/**
|
||||
* @method debugger_stop
|
||||
* 停止运行。
|
||||
* @param {debugger_t*} debugger debugger对象。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t debugger_stop(debugger_t* debugger);
|
||||
|
||||
/**
|
||||
* @method debugger_pause
|
||||
* 暂停运行。
|
||||
* > 暂停后才能执行next/step_xxx等函数。
|
||||
*
|
||||
* @param {debugger_t*} debugger debugger对象。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t debugger_pause(debugger_t* debugger);
|
||||
|
||||
/**
|
||||
* @method debugger_is_paused
|
||||
* 查看当前是否处于暂停运行状态。
|
||||
*
|
||||
* @param {debugger_t*} debugger debugger对象。
|
||||
*
|
||||
* @return {bool_t} 返回TRUE表示处于暂停运行状态。
|
||||
*/
|
||||
bool_t debugger_is_paused(debugger_t* debugger);
|
||||
|
||||
/**
|
||||
* @method debugger_match
|
||||
* 检查code_id是否与当前debugger匹配。
|
||||
*
|
||||
* @param {debugger_t*} debugger debugger对象。
|
||||
* @param {const char*} code_id 代码ID。
|
||||
*
|
||||
* @return {bool_t} 返回TRUE表示匹配到。
|
||||
*/
|
||||
bool_t debugger_match(debugger_t* debugger, const char* code_id);
|
||||
|
||||
/**
|
||||
* @method debugger_next
|
||||
* 执行到下一行代码。
|
||||
* > 处于暂停状态才能执行本命令。
|
||||
* @param {debugger_t*} debugger debugger对象。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t debugger_next(debugger_t* debugger);
|
||||
|
||||
/**
|
||||
* @method debugger_step_in
|
||||
* 进入函数。
|
||||
* > 处于暂停状态才能执行本命令。
|
||||
* @param {debugger_t*} debugger debugger对象。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t debugger_step_in(debugger_t* debugger);
|
||||
|
||||
/**
|
||||
* @method debugger_step_out
|
||||
* 执行到函数结束。
|
||||
* > 处于暂停状态才能执行本命令。
|
||||
* @param {debugger_t*} debugger debugger对象。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t debugger_step_out(debugger_t* debugger);
|
||||
|
||||
/**
|
||||
* @method debugger_step_over
|
||||
* 执行下一条语句(跳过循环)
|
||||
* > 处于暂停状态才能执行本命令。
|
||||
* @param {debugger_t*} debugger debugger对象。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t debugger_step_over(debugger_t* debugger);
|
||||
|
||||
/**
|
||||
* @method debugger_continue
|
||||
* 执行到下一个断点。
|
||||
* > 处于暂停状态才能执行本命令。
|
||||
* @param {debugger_t*} debugger debugger对象。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t debugger_continue(debugger_t* debugger);
|
||||
|
||||
/**
|
||||
* @method debugger_get_local
|
||||
* 获取局部变量对象。
|
||||
* > 处于暂停状态才能执行本命令。
|
||||
* @param {debugger_t*} debugger debugger对象。
|
||||
* @param {uint32_t} frame_index frame序数(0表示当前)
|
||||
*
|
||||
* @return {tk_object_t*} 返回局部变量对象。
|
||||
*/
|
||||
tk_object_t* debugger_get_local(debugger_t* debugger, uint32_t frame_index);
|
||||
|
||||
/**
|
||||
* @method debugger_get_self
|
||||
* 获取self对象。
|
||||
* > 处于暂停状态才能执行本命令。
|
||||
* @param {debugger_t*} debugger debugger对象。
|
||||
*
|
||||
* @return {tk_object_t*} 返回self对象。
|
||||
*/
|
||||
tk_object_t* debugger_get_self(debugger_t* debugger);
|
||||
|
||||
/**
|
||||
* @method debugger_get_global
|
||||
* 获取全局对象。
|
||||
* > 处于暂停状态才能执行本命令。
|
||||
* @param {debugger_t*} debugger debugger对象。
|
||||
*
|
||||
* @return {tk_object_t*} 返回全局对象。
|
||||
*/
|
||||
tk_object_t* debugger_get_global(debugger_t* debugger);
|
||||
|
||||
/**
|
||||
* @method debugger_get_callstack
|
||||
* 获取callstack。
|
||||
* @param {debugger_t*} debugger debugger对象。
|
||||
* @param {binary_data_t*} callstack callstack。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t debugger_get_callstack(debugger_t* debugger, binary_data_t* callstack);
|
||||
|
||||
/**
|
||||
* @method debugger_clear_break_points
|
||||
* 清除全部断点。
|
||||
* @param {debugger_t*} debugger debugger对象。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t debugger_clear_break_points(debugger_t* debugger);
|
||||
|
||||
/**
|
||||
* @method debugger_set_break_point
|
||||
* 设置断点。
|
||||
* @param {debugger_t*} debugger debugger对象。
|
||||
* @param {uint32_t} line 代码行号。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t debugger_set_break_point(debugger_t* debugger, uint32_t line);
|
||||
|
||||
/**
|
||||
* @method debugger_remove_break_point
|
||||
* 清除断点。
|
||||
* @param {debugger_t*} debugger debugger对象。
|
||||
* @param {uint32_t} line 代码行号。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t debugger_remove_break_point(debugger_t* debugger, uint32_t line);
|
||||
|
||||
/**
|
||||
* @method debugger_init
|
||||
* 初始化
|
||||
* @param {debugger_t*} debugger debugger对象。
|
||||
* @param {const char*} lang 代码的语言。
|
||||
* @param {const char*} code_id 代码的ID。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t debugger_init(debugger_t* debugger, const char* lang, const char* code_id);
|
||||
|
||||
/**
|
||||
* @method debugger_deinit
|
||||
* 释放资源。
|
||||
* @param {debugger_t*} debugger debugger对象。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t debugger_deinit(debugger_t* debugger);
|
||||
|
||||
/**
|
||||
* @method debugger_update_code
|
||||
* 更新代码。
|
||||
* @param {debugger_t*} debugger debugger对象。
|
||||
* @param {const binary_data_t*} code 代码。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t debugger_update_code(debugger_t* debugger, const binary_data_t* code);
|
||||
|
||||
/**
|
||||
* @method debugger_get_code
|
||||
* 获取代码。
|
||||
* @param {debugger_t*} debugger debugger对象。
|
||||
* @param {binary_data_t*} code 代码。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t debugger_get_code(debugger_t* debugger, binary_data_t* code);
|
||||
|
||||
#define DEBUGGER(debugger) ((debugger_t*)(debugger))
|
||||
|
||||
END_C_DECLS
|
||||
|
||||
#endif /*TK_DEBUGGER_H*/
|
407
src/debugger/debugger_client.c
Normal file
407
src/debugger/debugger_client.c
Normal file
@ -0,0 +1,407 @@
|
||||
/**
|
||||
* File: debugger.c
|
||||
* Author: AWTK Develop Team
|
||||
* Brief: debugger
|
||||
*
|
||||
* Copyright (c) 2022 - 2022 Guangzhou ZHIYUAN Electronics Co.,Ltd.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* License file for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* History:
|
||||
* ================================================================
|
||||
* 2022-01-11 Li XianJing <xianjimli@hotmail.com> created
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ubjson/ubjson_parser.h"
|
||||
#include "debugger/debugger_message.h"
|
||||
#include "debugger/debugger_client.h"
|
||||
|
||||
static ret_t debugger_client_lock(debugger_t* debugger) {
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static ret_t debugger_client_unlock(debugger_t* debugger) {
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static ret_t debugger_client_write_data(tk_ostream_t* out, const void* data, uint32_t size) {
|
||||
return tk_ostream_write_len(out, data, size, DEBUGGER_IO_WRITE_TIMEOUT) == size ? RET_OK : RET_IO;
|
||||
}
|
||||
|
||||
static ret_t debugger_client_read_data(tk_istream_t* in, void* data, uint32_t size) {
|
||||
return tk_istream_read_len(in, data, size, DEBUGGER_IO_READ_TIMEOUT) == size ? RET_OK : RET_IO;
|
||||
}
|
||||
|
||||
static ret_t debugger_client_extend_buff(debugger_t* debugger, uint32_t size) {
|
||||
ret_t ret = RET_OK;
|
||||
debugger_client_t* client = DEBUGGER_CLIENT(debugger);
|
||||
|
||||
if (client->capacity < size) {
|
||||
void* buff = TKMEM_REALLOC(client->buff, size);
|
||||
if (buff != NULL) {
|
||||
client->buff = buff;
|
||||
client->capacity = size;
|
||||
} else {
|
||||
ret = RET_OOM;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ret_t debugger_client_dispatch_message(debugger_t* debugger, debugger_resp_t* resp) {
|
||||
debugger_client_t* client = DEBUGGER_CLIENT(debugger);
|
||||
|
||||
switch (resp->code) {
|
||||
case DEBUGGER_RESP_MSG_BREAKED: {
|
||||
uint32_t line = 0;
|
||||
debugger_breaked_event_t event;
|
||||
tk_object_t* obj = ubjson_to_object(client->buff, resp->size);
|
||||
return_value_if_fail(obj != NULL, RET_BAD_PARAMS);
|
||||
line = tk_object_get_prop_int(obj, STR_DEBUGGER_EVENT_PROP_LINE, 0);
|
||||
emitter_dispatch(EMITTER(debugger), debugger_breaked_event_init(&event, line));
|
||||
TK_OBJECT_UNREF(obj);
|
||||
break;
|
||||
}
|
||||
case DEBUGGER_RESP_MSG_LOG: {
|
||||
uint32_t line = 0;
|
||||
const char* message = NULL;
|
||||
debugger_log_event_t event;
|
||||
tk_object_t* obj = ubjson_to_object(client->buff, resp->size);
|
||||
return_value_if_fail(obj != NULL, RET_BAD_PARAMS);
|
||||
line = tk_object_get_prop_int(obj, STR_DEBUGGER_EVENT_PROP_LINE, 0);
|
||||
message = tk_object_get_prop_str(obj, STR_DEBUGGER_EVENT_PROP_MESSAGE);
|
||||
emitter_dispatch(EMITTER(debugger), debugger_log_event_init(&event, line, message));
|
||||
TK_OBJECT_UNREF(obj);
|
||||
break;
|
||||
}
|
||||
case DEBUGGER_RESP_MSG_ERROR: {
|
||||
uint32_t line = 0;
|
||||
const char* message = NULL;
|
||||
debugger_error_event_t event;
|
||||
tk_object_t* obj = ubjson_to_object(client->buff, resp->size);
|
||||
return_value_if_fail(obj != NULL, RET_BAD_PARAMS);
|
||||
line = tk_object_get_prop_int(obj, STR_DEBUGGER_EVENT_PROP_LINE, 0);
|
||||
message = tk_object_get_prop_str(obj, STR_DEBUGGER_EVENT_PROP_MESSAGE);
|
||||
emitter_dispatch(EMITTER(debugger), debugger_error_event_init(&event, line, message));
|
||||
TK_OBJECT_UNREF(obj);
|
||||
break;
|
||||
}
|
||||
case DEBUGGER_RESP_MSG_COMPLETED: {
|
||||
client->program_completed = TRUE;
|
||||
emitter_dispatch_simple_event(EMITTER(debugger), DEBUGGER_RESP_MSG_COMPLETED);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static ret_t debugger_client_dispatch_one(debugger_t* debugger, debugger_resp_t* resp) {
|
||||
debugger_client_t* client = DEBUGGER_CLIENT(debugger);
|
||||
tk_istream_t* in = tk_iostream_get_istream(client->io);
|
||||
|
||||
return_value_if_fail(in != NULL, RET_BAD_PARAMS);
|
||||
return_value_if_fail(debugger_client_read_data(in, resp, sizeof(*resp)) == RET_OK, RET_IO);
|
||||
assert(DEBUGGER_VERSION == resp->version);
|
||||
return_value_if_fail(debugger_client_extend_buff(debugger, resp->size) == RET_OK, RET_OOM);
|
||||
|
||||
if (resp->size > 0) {
|
||||
return_value_if_fail(debugger_client_read_data(in, client->buff, resp->size) == RET_OK, RET_IO);
|
||||
}
|
||||
|
||||
return debugger_client_dispatch_message(debugger, resp);
|
||||
}
|
||||
|
||||
ret_t debugger_client_dispatch(debugger_t* debugger) {
|
||||
debugger_resp_t resp;
|
||||
return_value_if_fail(debugger != NULL, RET_BAD_PARAMS);
|
||||
|
||||
memset(&resp, 0x00, sizeof(resp));
|
||||
|
||||
return debugger_client_dispatch_one(debugger, &resp);
|
||||
}
|
||||
|
||||
ret_t debugger_client_wait_for_completed(debugger_t* debugger) {
|
||||
debugger_resp_t resp;
|
||||
debugger_client_t* client = DEBUGGER_CLIENT(debugger);
|
||||
|
||||
memset(&resp, 0x00, sizeof(resp));
|
||||
while (!(client->program_completed)) {
|
||||
break_if_fail(debugger_client_dispatch_one(debugger, &resp) == RET_OK);
|
||||
if (resp.code == DEBUGGER_RESP_MSG_COMPLETED) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static ret_t debugger_client_read_packet(debugger_t* debugger, uint32_t resp_code, void** data,
|
||||
uint32_t* size, ret_t* ret) {
|
||||
debugger_resp_t resp;
|
||||
debugger_client_t* client = DEBUGGER_CLIENT(debugger);
|
||||
|
||||
memset(&resp, 0x00, sizeof(resp));
|
||||
while (TRUE) {
|
||||
break_if_fail(debugger_client_dispatch_one(debugger, &resp) == RET_OK);
|
||||
if (resp.code == resp_code) {
|
||||
*data = client->buff;
|
||||
*size = resp.size;
|
||||
*ret = resp.error;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static ret_t debugger_client_read_simple(debugger_t* debugger, uint32_t resp_code) {
|
||||
void* data = NULL;
|
||||
uint32_t size = 0;
|
||||
ret_t ret = RET_FAIL;
|
||||
|
||||
debugger_client_read_packet(debugger, resp_code, &data, &size, &ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static tk_object_t* debugger_client_read_object(debugger_t* debugger, uint32_t resp_code) {
|
||||
void* data = NULL;
|
||||
uint32_t size = 0;
|
||||
ret_t ret = RET_FAIL;
|
||||
|
||||
if (debugger_client_read_packet(debugger, resp_code, &data, &size, &ret) == RET_OK &&
|
||||
ret == RET_OK) {
|
||||
assert(data != NULL && size > 0);
|
||||
return ubjson_to_object(data, size);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ret_t debugger_client_read_binary(debugger_t* debugger, uint32_t resp_code,
|
||||
binary_data_t* data) {
|
||||
ret_t ret = RET_FAIL;
|
||||
|
||||
debugger_client_read_packet(debugger, resp_code, &(data->data), &(data->size), &ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ret_t debugger_client_write_binary(debugger_t* debugger, uint32_t code, const void* data,
|
||||
uint32_t size) {
|
||||
debugger_req_t req;
|
||||
debugger_client_t* client = DEBUGGER_CLIENT(debugger);
|
||||
tk_ostream_t* out = tk_iostream_get_ostream(client->io);
|
||||
memset(&req, 0x00, sizeof(req));
|
||||
req.code = code;
|
||||
req.size = size;
|
||||
req.version = DEBUGGER_VERSION;
|
||||
debugger_client_write_data(out, &req, sizeof(req));
|
||||
|
||||
if (req.size > 0) {
|
||||
return debugger_client_write_data(out, data, size);
|
||||
} else {
|
||||
return RET_OK;
|
||||
}
|
||||
}
|
||||
|
||||
static ret_t debugger_client_write_simple(debugger_t* debugger, uint32_t code, uint32_t data) {
|
||||
debugger_req_t req;
|
||||
debugger_client_t* client = DEBUGGER_CLIENT(debugger);
|
||||
tk_ostream_t* out = tk_iostream_get_ostream(client->io);
|
||||
|
||||
memset(&req, 0x00, sizeof(req));
|
||||
|
||||
req.code = code;
|
||||
req.data = data;
|
||||
req.size = 0;
|
||||
req.version = DEBUGGER_VERSION;
|
||||
|
||||
return debugger_client_write_data(out, &req, sizeof(req));
|
||||
}
|
||||
|
||||
static ret_t debugger_client_request_simple(debugger_t* debugger, uint32_t code, uint32_t data) {
|
||||
return_value_if_fail(debugger_client_write_simple(debugger, code, data) == RET_OK, RET_FAIL);
|
||||
|
||||
return debugger_client_read_simple(debugger, code);
|
||||
}
|
||||
|
||||
static ret_t debugger_client_request_binary(debugger_t* debugger, uint32_t code, const void* data,
|
||||
uint32_t size) {
|
||||
return_value_if_fail(debugger_client_write_binary(debugger, code, data, size) == RET_OK,
|
||||
RET_FAIL);
|
||||
|
||||
return debugger_client_read_simple(debugger, code);
|
||||
}
|
||||
|
||||
static ret_t debugger_client_stop(debugger_t* debugger) {
|
||||
return debugger_client_request_simple(debugger, DEBUGGER_REQ_STOP, 0);
|
||||
}
|
||||
|
||||
static ret_t debugger_client_pause(debugger_t* debugger) {
|
||||
return debugger_client_request_simple(debugger, DEBUGGER_REQ_PAUSE, 0);
|
||||
}
|
||||
|
||||
static bool_t debugger_client_is_paused(debugger_t* debugger) {
|
||||
return debugger_client_request_simple(debugger, DEBUGGER_REQ_IS_PAUSED, 0) == RET_OK;
|
||||
}
|
||||
|
||||
static ret_t debugger_client_next(debugger_t* debugger) {
|
||||
return debugger_client_request_simple(debugger, DEBUGGER_REQ_NEXT, 0);
|
||||
}
|
||||
|
||||
static ret_t debugger_client_step_in(debugger_t* debugger) {
|
||||
return debugger_client_request_simple(debugger, DEBUGGER_REQ_STEP_IN, 0);
|
||||
}
|
||||
|
||||
static ret_t debugger_client_step_out(debugger_t* debugger) {
|
||||
return debugger_client_request_simple(debugger, DEBUGGER_REQ_STEP_OUT, 0);
|
||||
}
|
||||
|
||||
static ret_t debugger_client_step_over(debugger_t* debugger) {
|
||||
return debugger_client_request_simple(debugger, DEBUGGER_REQ_STEP_OVER, 0);
|
||||
}
|
||||
|
||||
static ret_t debugger_client_continue(debugger_t* debugger) {
|
||||
return debugger_client_request_simple(debugger, DEBUGGER_REQ_CONTINUE, 0);
|
||||
}
|
||||
|
||||
static tk_object_t* debugger_client_get_local(debugger_t* debugger, uint32_t frame_index) {
|
||||
if (debugger_client_write_simple(debugger, DEBUGGER_REQ_GET_LOCAL, frame_index) == RET_OK) {
|
||||
return debugger_client_read_object(debugger, DEBUGGER_RESP_GET_LOCAL);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static tk_object_t* debugger_client_get_self(debugger_t* debugger) {
|
||||
if (debugger_client_write_simple(debugger, DEBUGGER_REQ_GET_SELF, 0) == RET_OK) {
|
||||
return debugger_client_read_object(debugger, DEBUGGER_RESP_GET_SELF);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static tk_object_t* debugger_client_get_global(debugger_t* debugger) {
|
||||
if (debugger_client_write_simple(debugger, DEBUGGER_REQ_GET_GLOBAL, 0) == RET_OK) {
|
||||
return debugger_client_read_object(debugger, DEBUGGER_RESP_GET_GLOBAL);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static ret_t debugger_client_get_callstack(debugger_t* debugger, binary_data_t* callstack) {
|
||||
if (debugger_client_write_simple(debugger, DEBUGGER_REQ_GET_CALLSTACK, 0) == RET_OK) {
|
||||
return debugger_client_read_binary(debugger, DEBUGGER_RESP_GET_CALLSTACK, callstack);
|
||||
} else {
|
||||
return RET_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
static ret_t debugger_client_clear_break_points(debugger_t* debugger) {
|
||||
return debugger_client_request_simple(debugger, DEBUGGER_REQ_CLEAR_BREAK_POINTS, 0);
|
||||
}
|
||||
|
||||
static ret_t debugger_client_set_break_point(debugger_t* debugger, uint32_t line) {
|
||||
return debugger_client_request_simple(debugger, DEBUGGER_REQ_SET_BREAK_POINT, line);
|
||||
}
|
||||
|
||||
static ret_t debugger_client_remove_break_point(debugger_t* debugger, uint32_t line) {
|
||||
return debugger_client_request_simple(debugger, DEBUGGER_REQ_REMOVE_BREAK_POINT, line);
|
||||
}
|
||||
|
||||
static ret_t debugger_client_init(debugger_t* debugger, const char* lang, const char* code_id) {
|
||||
char data[256];
|
||||
tk_snprintf(data, sizeof(data) - 1, "%s:%s", lang, code_id);
|
||||
return debugger_client_request_binary(debugger, DEBUGGER_REQ_INIT, data, strlen(data) + 1);
|
||||
}
|
||||
|
||||
static ret_t debugger_client_deinit(debugger_t* debugger) {
|
||||
return debugger_client_request_simple(debugger, DEBUGGER_REQ_DEINIT, 0);
|
||||
}
|
||||
|
||||
static ret_t debugger_client_update_code(debugger_t* debugger, const binary_data_t* code) {
|
||||
return debugger_client_request_binary(debugger, DEBUGGER_REQ_UPDATE_CODE, code->data, code->size);
|
||||
}
|
||||
|
||||
static ret_t debugger_client_get_code(debugger_t* debugger, binary_data_t* code) {
|
||||
if (debugger_client_write_simple(debugger, DEBUGGER_REQ_GET_CODE, 0) == RET_OK) {
|
||||
return debugger_client_read_binary(debugger, DEBUGGER_RESP_GET_CODE, code);
|
||||
} else {
|
||||
return RET_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
static const debugger_vtable_t s_debugger_client_vtable = {
|
||||
.init = debugger_client_init,
|
||||
.lang = "client",
|
||||
.lock = debugger_client_lock,
|
||||
.unlock = debugger_client_unlock,
|
||||
.stop = debugger_client_stop,
|
||||
.pause = debugger_client_pause,
|
||||
.is_paused = debugger_client_is_paused,
|
||||
.next = debugger_client_next,
|
||||
.step_in = debugger_client_step_in,
|
||||
.step_out = debugger_client_step_out,
|
||||
.step_over = debugger_client_step_over,
|
||||
.continve = debugger_client_continue,
|
||||
.get_local = debugger_client_get_local,
|
||||
.get_self = debugger_client_get_self,
|
||||
.get_global = debugger_client_get_global,
|
||||
.get_code = debugger_client_get_code,
|
||||
.get_callstack = debugger_client_get_callstack,
|
||||
.update_code = debugger_client_update_code,
|
||||
.set_break_point = debugger_client_set_break_point,
|
||||
.remove_break_point = debugger_client_remove_break_point,
|
||||
.clear_break_points = debugger_client_clear_break_points,
|
||||
.deinit = debugger_client_deinit,
|
||||
};
|
||||
|
||||
debugger_client_t* debugger_client_cast(debugger_t* debugger) {
|
||||
return_value_if_fail(debugger != NULL && debugger->vt == &s_debugger_client_vtable, NULL);
|
||||
|
||||
return (debugger_client_t*)debugger;
|
||||
}
|
||||
|
||||
static ret_t debugger_client_on_destroy(tk_object_t* obj) {
|
||||
debugger_client_t* debugger = DEBUGGER_CLIENT(obj);
|
||||
return_value_if_fail(debugger != NULL, RET_BAD_PARAMS);
|
||||
|
||||
TKMEM_FREE(debugger->buff);
|
||||
TK_OBJECT_UNREF(debugger->io);
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static const object_vtable_t s_object_debugger_client_vtable = {
|
||||
.type = "object_debugger_client",
|
||||
.desc = "object_debugger_client",
|
||||
.size = sizeof(debugger_client_t),
|
||||
.is_collection = FALSE,
|
||||
.on_destroy = debugger_client_on_destroy};
|
||||
|
||||
debugger_t* debugger_client_create(tk_iostream_t* io) {
|
||||
debugger_client_t* debugger = NULL;
|
||||
return_value_if_fail(io != NULL, NULL);
|
||||
debugger = (debugger_client_t*)tk_object_create(&s_object_debugger_client_vtable);
|
||||
return_value_if_fail(debugger != NULL, NULL);
|
||||
|
||||
debugger->io = io;
|
||||
TK_OBJECT_REF(debugger->io);
|
||||
debugger->debugger.vt = &s_debugger_client_vtable;
|
||||
debugger->capacity = 10 * 1024;
|
||||
debugger->buff = TKMEM_ALLOC(debugger->capacity);
|
||||
|
||||
return (debugger_t*)debugger;
|
||||
}
|
94
src/debugger/debugger_client.h
Normal file
94
src/debugger/debugger_client.h
Normal file
@ -0,0 +1,94 @@
|
||||
/**
|
||||
* File: debugger_client.h
|
||||
* Author: AWTK Develop Team
|
||||
* Brief: debugger_client
|
||||
*
|
||||
* Copyright (c) 2022 - 2022 Guangzhou ZHIYUAN Electronics Co.,Ltd.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* License file for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* History:
|
||||
* ================================================================
|
||||
* 2022-01-12 Li XianJing <xianjimli@hotmail.com> created
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TK_DEBUGGER_CLIENT_H
|
||||
#define TK_DEBUGGER_CLIENT_H
|
||||
|
||||
#include "tkc/mem.h"
|
||||
#include "tkc/iostream.h"
|
||||
#include "debugger/debugger.h"
|
||||
|
||||
BEGIN_C_DECLS
|
||||
|
||||
/**
|
||||
* @class debugger_client_t
|
||||
* 调试器客户端。
|
||||
*
|
||||
*/
|
||||
typedef struct _debugger_client_t {
|
||||
debugger_t debugger;
|
||||
|
||||
/**
|
||||
* @property {tk_iostream_t*} io
|
||||
* @annotation ["readable"]
|
||||
* 与服务器通信的stream对象。
|
||||
*/
|
||||
tk_iostream_t* io;
|
||||
|
||||
/*private*/
|
||||
/*读取包的缓冲区*/
|
||||
void* buff;
|
||||
uint32_t capacity;
|
||||
/*程序执行完成*/
|
||||
bool_t program_completed;
|
||||
} debugger_client_t;
|
||||
|
||||
/**
|
||||
* @method debugger_client_create
|
||||
* 创建调试器对象。
|
||||
* @param {tk_iostream_t*} io io对象。
|
||||
*
|
||||
* @return {debugger_t*} 返回debugger对象。
|
||||
*/
|
||||
debugger_t* debugger_client_create(tk_iostream_t* io);
|
||||
|
||||
/**
|
||||
* @method debugger_client_cast
|
||||
* 类型转换。
|
||||
* @param {debugger_t*} debugger debugger对象。
|
||||
*
|
||||
* @return {debugger_client_t*} 返回debugger对象。
|
||||
*/
|
||||
debugger_client_t* debugger_client_cast(debugger_t* debugger);
|
||||
|
||||
/**
|
||||
* @method debugger_client_dispatch
|
||||
* 分发一个服务端推送的事件。
|
||||
* @param {debugger_t*} debugger debugger对象。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t debugger_client_dispatch(debugger_t* debugger);
|
||||
|
||||
/**
|
||||
* @method debugger_client_wait_for_completed
|
||||
* 等待完成事件。
|
||||
* @param {debugger_t*} debugger debugger对象。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t debugger_client_wait_for_completed(debugger_t* debugger);
|
||||
|
||||
#define DEBUGGER_CLIENT(debugger) debugger_client_cast((debugger_t*)debugger);
|
||||
|
||||
END_C_DECLS
|
||||
|
||||
#endif /*TK_DEBUGGER_CLIENT_H*/
|
44
src/debugger/debugger_client_tcp.c
Normal file
44
src/debugger/debugger_client_tcp.c
Normal file
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* File: debugger_server.h
|
||||
* Author: AWTK Develop Team
|
||||
* Brief: debugger server
|
||||
*
|
||||
* Copyright (c) 2022 - 2022 Guangzhou ZHIYUAN Electronics Co.,Ltd.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* License file for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* History:
|
||||
* ================================================================
|
||||
* 2022-01-14 Li XianJing <xianjimli@hotmail.com> created
|
||||
*
|
||||
*/
|
||||
|
||||
#include "tkc/socket_helper.h"
|
||||
#include "streams/inet/iostream_tcp.h"
|
||||
#include "debugger/debugger_client_tcp.h"
|
||||
|
||||
debugger_t* debugger_client_tcp_create(const char* host, uint32_t port) {
|
||||
int32_t sock = 0;
|
||||
tk_iostream_t* io = NULL;
|
||||
debugger_t* debugger = NULL;
|
||||
|
||||
return_value_if_fail(host != NULL, NULL);
|
||||
sock = tk_tcp_connect(host, port);
|
||||
return_value_if_fail(sock >= 0, NULL);
|
||||
|
||||
io = tk_iostream_tcp_create(sock);
|
||||
if (io != NULL) {
|
||||
debugger = debugger_client_create(io);
|
||||
TK_OBJECT_UNREF(io);
|
||||
} else {
|
||||
socket_close(sock);
|
||||
}
|
||||
|
||||
return debugger;
|
||||
}
|
50
src/debugger/debugger_client_tcp.h
Normal file
50
src/debugger/debugger_client_tcp.h
Normal file
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* File: debugger_client_tcp.h
|
||||
* Author: AWTK Develop Team
|
||||
* Brief: debugger client_tcp
|
||||
*
|
||||
* Copyright (c) 2022 - 2022 Guangzhou ZHIYUAN Electronics Co.,Ltd.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* License file for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* History:
|
||||
* ================================================================
|
||||
* 2022-01-14 Li XianJing <xianjimli@hotmail.com> created
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TK_DEBUGGER_CLIENT_TCP_H
|
||||
#define TK_DEBUGGER_CLIENT_TCP_H
|
||||
|
||||
#include "tkc/iostream.h"
|
||||
#include "debugger/debugger.h"
|
||||
#include "debugger/debugger_client.h"
|
||||
|
||||
BEGIN_C_DECLS
|
||||
|
||||
/**
|
||||
* @class debugger_client_tcp_t
|
||||
* @annotation ["fake"]
|
||||
* 调试器TCP客户端。
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @method debugger_client_tcp_create
|
||||
* 创建调试器TCP客户端对象。
|
||||
* @param {const char*} host 目标主机。
|
||||
* @param {uint32_t} port 目标端口。
|
||||
*
|
||||
* @return {debugger_t*} 返回debugger对象。
|
||||
*/
|
||||
debugger_t* debugger_client_tcp_create(const char* host, uint32_t port);
|
||||
|
||||
END_C_DECLS
|
||||
|
||||
#endif /*TK_DEBUGGER_CLIENT_TCP_H*/
|
33
src/debugger/debugger_const.h
Normal file
33
src/debugger/debugger_const.h
Normal file
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* File: debugger_const.h
|
||||
* Author: AWTK Develop Team
|
||||
* Brief: debugger constant
|
||||
*
|
||||
* Copyright (c) 2022 - 2022 Guangzhou ZHIYUAN Electronics Co.,Ltd.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* License file for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* History:
|
||||
* ================================================================
|
||||
* 2022-01-16 Li XianJing <xianjimli@hotmail.com> created
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TK_DEBUGGER_CONST_H
|
||||
#define TK_DEBUGGER_CONST_H
|
||||
|
||||
#define DEBUGGER_VERSION 0x0100
|
||||
|
||||
#define STR_DEBUGGER_EVENT_PROP_LINE "line"
|
||||
#define STR_DEBUGGER_EVENT_PROP_MESSAGE "message"
|
||||
|
||||
#define DEBUGGER_IO_READ_TIMEOUT 10000
|
||||
#define DEBUGGER_IO_WRITE_TIMEOUT 5000
|
||||
|
||||
#endif /*TK_DEBUGGER_CONST_H*/
|
63
src/debugger/debugger_factory.c
Normal file
63
src/debugger/debugger_factory.c
Normal file
@ -0,0 +1,63 @@
|
||||
/**
|
||||
* File: debugger_factory.c
|
||||
* Author: AWTK Develop Team
|
||||
* Brief: debugger factory
|
||||
*
|
||||
* Copyright (c) 2022 - 2022 Guangzhou ZHIYUAN Electronics Co.,Ltd.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* License file for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* History:
|
||||
* ================================================================
|
||||
* 2022-01-14 Li XianJing <xianjimli@hotmail.com> created
|
||||
*
|
||||
*/
|
||||
|
||||
#include "tkc/general_factory.h"
|
||||
#include "debugger/debugger_factory.h"
|
||||
|
||||
static general_factory_t* s_debugger_factory = NULL;
|
||||
|
||||
ret_t debugger_factory_init(void) {
|
||||
return_value_if_fail(s_debugger_factory == NULL, RET_BAD_PARAMS);
|
||||
s_debugger_factory = general_factory_create();
|
||||
|
||||
return s_debugger_factory != NULL ? RET_OK : RET_FAIL;
|
||||
}
|
||||
|
||||
ret_t debugger_factory_reg(const char* lang, debugger_fscript_create_t create) {
|
||||
return_value_if_fail(s_debugger_factory != NULL, RET_BAD_PARAMS);
|
||||
return_value_if_fail(lang != NULL && create != NULL, RET_BAD_PARAMS);
|
||||
|
||||
return general_factory_register(s_debugger_factory, lang, (tk_create_t)create);
|
||||
}
|
||||
|
||||
debugger_t* debugger_factory_create_debugger(const char* lang, const char* code_id) {
|
||||
debugger_t* debugger = NULL;
|
||||
debugger_fscript_create_t create = NULL;
|
||||
return_value_if_fail(s_debugger_factory != NULL, NULL);
|
||||
return_value_if_fail(lang != NULL && code_id != NULL, NULL);
|
||||
|
||||
create = (debugger_fscript_create_t)general_factory_find(s_debugger_factory, lang);
|
||||
return_value_if_fail(create != NULL, NULL);
|
||||
|
||||
debugger = create();
|
||||
return_value_if_fail(debugger != NULL, NULL);
|
||||
debugger_init(debugger, lang, code_id);
|
||||
|
||||
return debugger;
|
||||
}
|
||||
|
||||
ret_t debugger_factory_deinit(void) {
|
||||
return_value_if_fail(s_debugger_factory != NULL, RET_BAD_PARAMS);
|
||||
general_factory_destroy(s_debugger_factory);
|
||||
s_debugger_factory = NULL;
|
||||
|
||||
return RET_OK;
|
||||
}
|
73
src/debugger/debugger_factory.h
Normal file
73
src/debugger/debugger_factory.h
Normal file
@ -0,0 +1,73 @@
|
||||
/**
|
||||
* File: debugger_factory.h
|
||||
* Author: AWTK Develop Team
|
||||
* Brief: debugger factory
|
||||
*
|
||||
* Copyright (c) 2022 - 2022 Guangzhou ZHIYUAN Electronics Co.,Ltd.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* License file for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* History:
|
||||
* ================================================================
|
||||
* 2022-01-14 Li XianJing <xianjimli@hotmail.com> created
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TK_DEBUGGER_FACTORY_H
|
||||
#define TK_DEBUGGER_FACTORY_H
|
||||
|
||||
#include "debugger/debugger.h"
|
||||
|
||||
BEGIN_C_DECLS
|
||||
|
||||
/**
|
||||
* @class debugger_factory_t
|
||||
* @annotaion ["fake"]
|
||||
* 调试器工厂
|
||||
*/
|
||||
|
||||
/**
|
||||
* @method debugger_factory_init
|
||||
* 初始化工厂。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t debugger_factory_init(void);
|
||||
|
||||
/**
|
||||
* @method debugger_factory_reg
|
||||
* 注册创建函数。
|
||||
* @param {const char*} lang 语言类型。
|
||||
* @param {debugger_fscript_create_t} create 创建函数。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t debugger_factory_reg(const char* lang, debugger_fscript_create_t create);
|
||||
|
||||
/**
|
||||
* @method debugger_factory_create_debugger
|
||||
* 创建调试器。
|
||||
* @param {const char*} lang 语言类型。
|
||||
* @param {const char*} code_id 代码的ID。
|
||||
*
|
||||
* @return {debugger_t*} 返回调试器对象。
|
||||
*/
|
||||
debugger_t* debugger_factory_create_debugger(const char* lang, const char* code_id);
|
||||
|
||||
/**
|
||||
* @method debugger_factory_deinit
|
||||
* 释放资源。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t debugger_factory_deinit(void);
|
||||
|
||||
END_C_DECLS
|
||||
|
||||
#endif /*TK_DEBUGGER_FACTORY_H*/
|
486
src/debugger/debugger_fscript.c
Normal file
486
src/debugger/debugger_fscript.c
Normal file
@ -0,0 +1,486 @@
|
||||
/**
|
||||
* File: debugger.c
|
||||
* Author: AWTK Develop Team
|
||||
* Brief: debugger
|
||||
*
|
||||
* Copyright (c) 2022 - 2022 Guangzhou ZHIYUAN Electronics Co.,Ltd.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* License file for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* History:
|
||||
* ================================================================
|
||||
* 2022-01-11 Li XianJing <xianjimli@hotmail.com> created
|
||||
*
|
||||
*/
|
||||
|
||||
#include "debugger/debugger_server.h"
|
||||
#include "debugger/debugger_message.h"
|
||||
#include "debugger/debugger_fscript.h"
|
||||
|
||||
static ret_t debugger_fscript_lock(debugger_t* debugger) {
|
||||
debugger_fscript_t* d = DEBUGGER_FSCRIPT(debugger);
|
||||
return_value_if_fail(d != NULL, RET_BAD_PARAMS);
|
||||
|
||||
log_debug("debugger_fscript_lock\n");
|
||||
return tk_mutex_nest_lock(d->mutex);
|
||||
}
|
||||
|
||||
static ret_t debugger_fscript_unlock(debugger_t* debugger) {
|
||||
debugger_fscript_t* d = DEBUGGER_FSCRIPT(debugger);
|
||||
return_value_if_fail(d != NULL, RET_BAD_PARAMS);
|
||||
|
||||
log_debug("debugger_fscript_unlock\n");
|
||||
return tk_mutex_nest_unlock(d->mutex);
|
||||
}
|
||||
|
||||
static ret_t debugger_fscript_stop(debugger_t* debugger) {
|
||||
ret_t ret = RET_FAIL;
|
||||
debugger_fscript_t* d = DEBUGGER_FSCRIPT(debugger);
|
||||
return_value_if_fail(d != NULL, RET_BAD_PARAMS);
|
||||
|
||||
if (debugger_fscript_lock(debugger) == RET_OK) {
|
||||
if (d->fscript != NULL) {
|
||||
/*TODO*/
|
||||
}
|
||||
debugger_fscript_unlock(debugger);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ret_t debugger_fscript_pause(debugger_t* debugger) {
|
||||
ret_t ret = RET_FAIL;
|
||||
debugger_fscript_t* d = DEBUGGER_FSCRIPT(debugger);
|
||||
return_value_if_fail(d != NULL, RET_BAD_PARAMS);
|
||||
|
||||
if (debugger_fscript_lock(debugger) == RET_OK) {
|
||||
if (d->fscript != NULL && d->paused == FALSE) {
|
||||
ret = RET_OK;
|
||||
/*停止到下一行要执行的代码*/
|
||||
d->next_stop_executed_line = d->executed_lines + 1;
|
||||
}
|
||||
ret = d->paused == TRUE ? RET_OK : RET_FAIL;
|
||||
debugger_fscript_unlock(debugger);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool_t debugger_fscript_match(debugger_t* debugger, const char* code_id) {
|
||||
debugger_fscript_t* d = DEBUGGER_FSCRIPT(debugger);
|
||||
return_value_if_fail(d != NULL, FALSE);
|
||||
|
||||
return tk_str_eq(d->code_id, code_id);
|
||||
}
|
||||
|
||||
static bool_t debugger_fscript_is_paused(debugger_t* debugger) {
|
||||
bool_t ret = FALSE;
|
||||
debugger_fscript_t* d = DEBUGGER_FSCRIPT(debugger);
|
||||
return_value_if_fail(d != NULL, FALSE);
|
||||
|
||||
if (debugger_fscript_lock(debugger) == RET_OK) {
|
||||
ret = d->paused;
|
||||
debugger_fscript_unlock(debugger);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ret_t debugger_fscript_next(debugger_t* debugger) {
|
||||
ret_t ret = RET_FAIL;
|
||||
debugger_fscript_t* d = DEBUGGER_FSCRIPT(debugger);
|
||||
return_value_if_fail(d != NULL, RET_BAD_PARAMS);
|
||||
|
||||
if (debugger_fscript_lock(debugger) == RET_OK) {
|
||||
if (d->fscript != NULL && d->paused) {
|
||||
ret = RET_OK;
|
||||
d->next_stop_executed_line = d->executed_lines + 1;
|
||||
}
|
||||
debugger_fscript_unlock(debugger);
|
||||
|
||||
if (ret == RET_OK) {
|
||||
tk_cond_var_awake(d->cond_var);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ret_t debugger_fscript_step_in(debugger_t* debugger) {
|
||||
/*TODO*/
|
||||
return debugger_fscript_next(debugger);
|
||||
}
|
||||
|
||||
static ret_t debugger_fscript_step_out(debugger_t* debugger) {
|
||||
/*TODO*/
|
||||
return debugger_fscript_next(debugger);
|
||||
}
|
||||
|
||||
static ret_t debugger_fscript_step_over(debugger_t* debugger) {
|
||||
/*TODO*/
|
||||
return debugger_fscript_next(debugger);
|
||||
}
|
||||
|
||||
static ret_t debugger_fscript_continue(debugger_t* debugger) {
|
||||
ret_t ret = RET_FAIL;
|
||||
debugger_fscript_t* d = DEBUGGER_FSCRIPT(debugger);
|
||||
return_value_if_fail(d != NULL, RET_BAD_PARAMS);
|
||||
|
||||
if (debugger_fscript_lock(debugger) == RET_OK) {
|
||||
if (d->fscript != NULL && d->paused) {
|
||||
ret = RET_OK;
|
||||
d->next_stop_line = -1;
|
||||
d->next_stop_executed_line = -1;
|
||||
}
|
||||
debugger_fscript_unlock(debugger);
|
||||
|
||||
if (ret == RET_OK) {
|
||||
tk_cond_var_awake(d->cond_var);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ret_t debugger_fscript_get_code(debugger_t* debugger, binary_data_t* code) {
|
||||
debugger_fscript_t* d = DEBUGGER_FSCRIPT(debugger);
|
||||
return_value_if_fail(d != NULL && d->fscript != NULL, RET_BAD_PARAMS);
|
||||
|
||||
code->data = d->code.str;
|
||||
code->size = d->code.size + 1;
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
tk_object_t* debugger_fscript_get_local(debugger_t* debugger, uint32_t frame_index) {
|
||||
debugger_fscript_t* d = DEBUGGER_FSCRIPT(debugger);
|
||||
return_value_if_fail(d != NULL && d->fscript != NULL, NULL);
|
||||
/*FIXME: 目前只支持当前*/
|
||||
return TK_OBJECT_REF(d->fscript->locals);
|
||||
}
|
||||
|
||||
tk_object_t* debugger_fscript_get_self(debugger_t* debugger) {
|
||||
debugger_fscript_t* d = DEBUGGER_FSCRIPT(debugger);
|
||||
return_value_if_fail(d != NULL && d->fscript != NULL, NULL);
|
||||
|
||||
return TK_OBJECT_REF(d->fscript->obj);
|
||||
}
|
||||
|
||||
tk_object_t* debugger_fscript_get_global(debugger_t* debugger) {
|
||||
return TK_OBJECT_REF(fscript_get_global_object());
|
||||
}
|
||||
|
||||
static ret_t debugger_fscript_get_callstack(debugger_t* debugger, binary_data_t* callstack) {
|
||||
debugger_fscript_t* d = DEBUGGER_FSCRIPT(debugger);
|
||||
return_value_if_fail(d != NULL && d->fscript != NULL, RET_BAD_PARAMS);
|
||||
|
||||
callstack->data = d->callstack.str;
|
||||
callstack->size = d->callstack.size + 1;
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static ret_t debugger_fscript_clear_break_points(debugger_t* debugger) {
|
||||
ret_t ret = RET_FAIL;
|
||||
bool_t paused = FALSE;
|
||||
debugger_fscript_t* d = DEBUGGER_FSCRIPT(debugger);
|
||||
return_value_if_fail(d != NULL, RET_BAD_PARAMS);
|
||||
|
||||
if (debugger_fscript_lock(debugger) == RET_OK) {
|
||||
d->next_stop_line = -1;
|
||||
d->next_stop_executed_line = -1;
|
||||
ret = darray_clear(&(d->break_points));
|
||||
|
||||
paused = d->paused;
|
||||
debugger_fscript_unlock(debugger);
|
||||
}
|
||||
|
||||
if (paused) {
|
||||
tk_cond_var_awake(d->cond_var);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ret_t debugger_fscript_set_break_point(debugger_t* debugger, uint32_t line) {
|
||||
ret_t ret = RET_FAIL;
|
||||
debugger_fscript_t* d = DEBUGGER_FSCRIPT(debugger);
|
||||
return_value_if_fail(d != NULL, RET_BAD_PARAMS);
|
||||
|
||||
if (debugger_fscript_lock(debugger) == RET_OK) {
|
||||
ret = darray_push_unique(&(d->break_points), tk_pointer_from_int(line));
|
||||
debugger_fscript_unlock(debugger);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ret_t debugger_fscript_remove_break_point(debugger_t* debugger, uint32_t line) {
|
||||
ret_t ret = RET_FAIL;
|
||||
debugger_fscript_t* d = DEBUGGER_FSCRIPT(debugger);
|
||||
return_value_if_fail(d != NULL, RET_BAD_PARAMS);
|
||||
|
||||
if (debugger_fscript_lock(debugger) == RET_OK) {
|
||||
ret = darray_remove(&(d->break_points), tk_pointer_from_int(line));
|
||||
debugger_fscript_unlock(debugger);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ret_t debugger_fscript_init(debugger_t* debugger, const char* lang, const char* code_id) {
|
||||
ret_t ret = RET_FAIL;
|
||||
debugger_fscript_t* d = DEBUGGER_FSCRIPT(debugger);
|
||||
return_value_if_fail(d != NULL, RET_BAD_PARAMS);
|
||||
|
||||
if (debugger_fscript_lock(debugger) == RET_OK) {
|
||||
ret = RET_OK;
|
||||
d->code_id = tk_str_copy(d->code_id, code_id);
|
||||
debugger_fscript_unlock(debugger);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ret_t debugger_fscript_deinit(debugger_t* debugger) {
|
||||
debugger_fscript_clear_break_points(debugger);
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static ret_t debugger_fscript_update_code(debugger_t* debugger, const binary_data_t* code) {
|
||||
ret_t ret = RET_FAIL;
|
||||
debugger_fscript_t* d = DEBUGGER_FSCRIPT(debugger);
|
||||
return_value_if_fail(d != NULL, RET_BAD_PARAMS);
|
||||
|
||||
if (debugger_fscript_lock(debugger) == RET_OK) {
|
||||
ret = RET_OK;
|
||||
str_set_with_len(&(d->code), (char*)(code->data), code->size);
|
||||
/*TODO*/
|
||||
debugger_fscript_unlock(debugger);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const debugger_vtable_t s_debugger_fscript_vtable = {
|
||||
.lang = DEBUGGER_LANG_FSCRIPT,
|
||||
.init = debugger_fscript_init,
|
||||
.lock = debugger_fscript_lock,
|
||||
.unlock = debugger_fscript_unlock,
|
||||
.stop = debugger_fscript_stop,
|
||||
.pause = debugger_fscript_pause,
|
||||
.match = debugger_fscript_match,
|
||||
.is_paused = debugger_fscript_is_paused,
|
||||
.next = debugger_fscript_next,
|
||||
.step_in = debugger_fscript_step_in,
|
||||
.step_out = debugger_fscript_step_out,
|
||||
.step_over = debugger_fscript_step_over,
|
||||
.continve = debugger_fscript_continue,
|
||||
.get_local = debugger_fscript_get_local,
|
||||
.get_self = debugger_fscript_get_self,
|
||||
.get_global = debugger_fscript_get_global,
|
||||
.get_callstack = debugger_fscript_get_callstack,
|
||||
.get_code = debugger_fscript_get_code,
|
||||
.update_code = debugger_fscript_update_code,
|
||||
.set_break_point = debugger_fscript_set_break_point,
|
||||
.remove_break_point = debugger_fscript_remove_break_point,
|
||||
.clear_break_points = debugger_fscript_clear_break_points,
|
||||
.deinit = debugger_fscript_deinit,
|
||||
};
|
||||
|
||||
debugger_fscript_t* debugger_fscript_cast(debugger_t* debugger) {
|
||||
return_value_if_fail(debugger != NULL && debugger->vt == &s_debugger_fscript_vtable, NULL);
|
||||
|
||||
return (debugger_fscript_t*)debugger;
|
||||
}
|
||||
|
||||
static ret_t debugger_fscript_on_destroy(tk_object_t* obj) {
|
||||
debugger_fscript_t* debugger = DEBUGGER_FSCRIPT(obj);
|
||||
return_value_if_fail(debugger != NULL, RET_BAD_PARAMS);
|
||||
|
||||
tk_mutex_nest_destroy(debugger->mutex);
|
||||
debugger->mutex = NULL;
|
||||
tk_cond_var_destroy(debugger->cond_var);
|
||||
debugger->cond_var = NULL;
|
||||
|
||||
TKMEM_FREE(debugger->code_id);
|
||||
str_reset(&(debugger->code));
|
||||
str_reset(&(debugger->callstack));
|
||||
darray_deinit(&(debugger->break_points));
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static const object_vtable_t s_object_debugger_fscript_vtable = {
|
||||
.type = "object_debugger_fscript",
|
||||
.desc = "object_debugger_fscript",
|
||||
.size = sizeof(debugger_fscript_t),
|
||||
.is_collection = FALSE,
|
||||
.on_destroy = debugger_fscript_on_destroy};
|
||||
|
||||
debugger_t* debugger_fscript_create(void) {
|
||||
debugger_fscript_t* debugger = NULL;
|
||||
debugger = (debugger_fscript_t*)tk_object_create(&s_object_debugger_fscript_vtable);
|
||||
return_value_if_fail(debugger != NULL, NULL);
|
||||
|
||||
debugger->mutex = tk_mutex_nest_create();
|
||||
debugger->cond_var = tk_cond_var_create();
|
||||
debugger->debugger.vt = &s_debugger_fscript_vtable;
|
||||
|
||||
str_init(&(debugger->code), 100);
|
||||
str_init(&(debugger->callstack), 100);
|
||||
darray_init(&(debugger->break_points), 10, NULL, NULL);
|
||||
|
||||
return (debugger_t*)debugger;
|
||||
}
|
||||
|
||||
ret_t debugger_fscript_print_func(fscript_t* fscript, fscript_args_t* args, value_t* result) {
|
||||
str_t str;
|
||||
char buff[32];
|
||||
uint32_t i = 0;
|
||||
debugger_log_event_t event;
|
||||
debugger_t* debugger = NULL;
|
||||
|
||||
if (fscript != NULL && fscript->code_id != NULL) {
|
||||
debugger = debugger_server_find_debugger(fscript->code_id);
|
||||
}
|
||||
|
||||
if (debugger != NULL) {
|
||||
uint32_t line = fscript->curr->row;
|
||||
value_set_bool(result, TRUE);
|
||||
str_init(&str, 100);
|
||||
for (i = 0; i < args->size; i++) {
|
||||
str_append(&str, value_str_ex(args->args + i, buff, sizeof(buff) - 1));
|
||||
}
|
||||
|
||||
debugger_log_event_init(&event, line, str.str);
|
||||
emitter_dispatch(EMITTER(debugger), (event_t*)&event);
|
||||
str_reset(&str);
|
||||
}
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static ret_t debugger_fscript_on_error(void* ctx, fscript_t* fscript) {
|
||||
debugger_error_event_t event;
|
||||
debugger_t* debugger = DEBUGGER(ctx);
|
||||
|
||||
debugger_error_event_init(&event, fscript->error_row, fscript->error_message);
|
||||
emitter_dispatch(EMITTER(debugger), (event_t*)&event);
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
ret_t debugger_fscript_set_fscript(debugger_t* debugger, fscript_t* fscript) {
|
||||
debugger_fscript_t* d = DEBUGGER_FSCRIPT(debugger);
|
||||
return_value_if_fail(d != NULL, RET_BAD_PARAMS);
|
||||
|
||||
d->fscript = fscript;
|
||||
if (fscript) {
|
||||
d->executed_lines = 0;
|
||||
d->last_executed_line = 0;
|
||||
d->next_stop_line = -1;
|
||||
d->prev_breaked_line = -1;
|
||||
d->next_stop_executed_line = -1;
|
||||
str_set(&(d->callstack), "<root>\n");
|
||||
fscript_set_print_func(fscript, debugger_fscript_print_func);
|
||||
fscript_set_on_error(fscript, debugger_fscript_on_error, d);
|
||||
} else {
|
||||
emitter_dispatch_simple_event(EMITTER(debugger), DEBUGGER_RESP_MSG_COMPLETED);
|
||||
}
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static ret_t debugger_fscript_before_exec_func(debugger_t* debugger, int32_t line) {
|
||||
bool_t paused = FALSE;
|
||||
debugger_fscript_t* d = DEBUGGER_FSCRIPT(debugger);
|
||||
|
||||
if (debugger_fscript_lock(debugger) == RET_OK) {
|
||||
bool_t bp = darray_find_index(&(d->break_points), tk_pointer_from_int(line)) >= 0;
|
||||
|
||||
d->paused = bp || line == d->next_stop_line || d->executed_lines == d->next_stop_executed_line;
|
||||
paused = d->paused && (d->prev_breaked_line != line);
|
||||
|
||||
if (paused) {
|
||||
debugger_breaked_event_t event;
|
||||
debugger_breaked_event_init(&event, line);
|
||||
emitter_dispatch(EMITTER(debugger), (event_t*)&event);
|
||||
d->prev_breaked_line = line;
|
||||
}
|
||||
debugger_fscript_unlock(debugger);
|
||||
|
||||
if (paused) {
|
||||
log_debug("debugger wait for instruction\n");
|
||||
tk_cond_var_wait(d->cond_var, 0xffffff);
|
||||
}
|
||||
}
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static ret_t debugger_fscript_after_exec_func(debugger_t* debugger, int32_t line) {
|
||||
debugger_fscript_t* d = DEBUGGER_FSCRIPT(debugger);
|
||||
|
||||
if (debugger_fscript_lock(debugger) == RET_OK) {
|
||||
if (d->last_executed_line != line) {
|
||||
d->executed_lines++;
|
||||
}
|
||||
d->last_executed_line = line;
|
||||
debugger_fscript_unlock(debugger);
|
||||
}
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
ret_t debugger_fscript_exec_func(fscript_t* fscript, const char* name, fscript_func_call_t* iter,
|
||||
value_t* result) {
|
||||
ret_t ret = RET_FAIL;
|
||||
debugger_t* debugger = NULL;
|
||||
return_value_if_fail(fscript != NULL, RET_BAD_PARAMS);
|
||||
|
||||
if (fscript != NULL && fscript->code_id != NULL) {
|
||||
debugger = debugger_server_find_debugger(fscript->code_id);
|
||||
}
|
||||
|
||||
if (debugger == NULL) {
|
||||
return fscript_exec_func_default(fscript, iter, result);
|
||||
} else {
|
||||
int32_t line = iter->row;
|
||||
|
||||
debugger_fscript_before_exec_func(debugger, line);
|
||||
ret = fscript_exec_func_default(fscript, iter, result);
|
||||
debugger_fscript_after_exec_func(debugger, line);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret_t debugger_fscript_set_var(fscript_t* fscript, const char* name, const value_t* v) {
|
||||
ret_t ret = RET_FAIL;
|
||||
debugger_t* debugger = NULL;
|
||||
return_value_if_fail(fscript != NULL, RET_BAD_PARAMS);
|
||||
|
||||
if (fscript != NULL && fscript->code_id != NULL) {
|
||||
debugger = debugger_server_find_debugger(fscript->code_id);
|
||||
}
|
||||
|
||||
if (debugger == NULL) {
|
||||
return fscript_set_var_default(fscript, name, v);
|
||||
} else {
|
||||
int32_t line = fscript->curr->row;
|
||||
|
||||
debugger_fscript_before_exec_func(debugger, line);
|
||||
ret = fscript_set_var_default(fscript, name, v);
|
||||
debugger_fscript_after_exec_func(debugger, line);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
97
src/debugger/debugger_fscript.h
Normal file
97
src/debugger/debugger_fscript.h
Normal file
@ -0,0 +1,97 @@
|
||||
/**
|
||||
* File: fscript_debugger_fscript.h
|
||||
* Author: AWTK Develop Team
|
||||
* Brief: debugger_fscript for fscript
|
||||
*
|
||||
* Copyright (c) 2022 - 2022 Guangzhou ZHIYUAN Electronics Co.,Ltd.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* License file for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* History:
|
||||
* ================================================================
|
||||
* 2022-01-12 Li XianJing <xianjimli@hotmail.com> created
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TK_DEBUGGER_FSCRIPT_H
|
||||
#define TK_DEBUGGER_FSCRIPT_H
|
||||
|
||||
#include "tkc/fscript.h"
|
||||
#include "tkc/cond_var.h"
|
||||
#include "tkc/mutex_nest.h"
|
||||
#include "debugger/debugger.h"
|
||||
|
||||
BEGIN_C_DECLS
|
||||
|
||||
/**
|
||||
* @class debugger_fscript_t
|
||||
* fscript调试器
|
||||
*
|
||||
*/
|
||||
typedef struct _debugger_fscript_t {
|
||||
debugger_t debugger;
|
||||
|
||||
/*private*/
|
||||
char* code_id;
|
||||
fscript_t* fscript;
|
||||
|
||||
uint32_t last_executed_line;
|
||||
|
||||
int32_t next_stop_line;
|
||||
int32_t prev_breaked_line;
|
||||
uint32_t executed_lines;
|
||||
uint32_t next_stop_executed_line;
|
||||
|
||||
str_t code;
|
||||
bool_t paused;
|
||||
str_t callstack;
|
||||
tk_mutex_nest_t* mutex;
|
||||
tk_cond_var_t* cond_var;
|
||||
darray_t break_points;
|
||||
} debugger_fscript_t;
|
||||
|
||||
/**
|
||||
* @method debugger_fscript_create
|
||||
* 创建调试器对象。
|
||||
*
|
||||
* @return {debugger_t*} 返回debugger对象。
|
||||
*/
|
||||
debugger_t* debugger_fscript_create(void);
|
||||
|
||||
/**
|
||||
* @method debugger_fscript_set_fscript
|
||||
* 设置fscript对象。
|
||||
* @param {debugger_t*} debugger debugger对象。
|
||||
* @param {fscript_t*} fscript 脚本对象。
|
||||
*
|
||||
* @return {debugger_t*} 返回debugger对象。
|
||||
*/
|
||||
ret_t debugger_fscript_set_fscript(debugger_t* debugger, fscript_t* fscript);
|
||||
|
||||
/**
|
||||
* @method debugger_fscript_cast
|
||||
* 类型转换。
|
||||
* @param {debugger_t*} debugger debugger对象。
|
||||
*
|
||||
* @return {debugger_fscript_t*} 返回debugger对象。
|
||||
*/
|
||||
debugger_fscript_t* debugger_fscript_cast(debugger_t* debugger);
|
||||
|
||||
#define DEBUGGER_FSCRIPT(debugger) debugger_fscript_cast((debugger_t*)debugger);
|
||||
|
||||
#define DEBUGGER_LANG_FSCRIPT "fscript"
|
||||
|
||||
/*fscript hooks*/
|
||||
ret_t debugger_fscript_set_var(fscript_t* fscript, const char* name, const value_t* v);
|
||||
ret_t debugger_fscript_exec_func(fscript_t* fscript, const char* name, fscript_func_call_t* iter,
|
||||
value_t* result);
|
||||
|
||||
END_C_DECLS
|
||||
|
||||
#endif /*TK_DEBUGGER_FSCRIPT_H*/
|
95
src/debugger/debugger_global.c
Normal file
95
src/debugger/debugger_global.c
Normal file
@ -0,0 +1,95 @@
|
||||
/**
|
||||
* File: debugger_global.c
|
||||
* Author: AWTK Develop Team
|
||||
* Brief: debugger global
|
||||
*
|
||||
* Copyright (c) 2022 - 2022 Guangzhou ZHIYUAN Electronics Co.,Ltd.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* License file for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* History:
|
||||
* ================================================================
|
||||
* 2022-01-12 Li XianJing <xianjimli@hotmail.com> created
|
||||
*
|
||||
*/
|
||||
|
||||
#include "debugger/debugger_fscript.h"
|
||||
#include "debugger/debugger_factory.h"
|
||||
#include "debugger/debugger_global.h"
|
||||
#include "debugger/debugger_server.h"
|
||||
|
||||
static ret_t debugger_fscript_on_connect(fscript_t* fscript) {
|
||||
debugger_t* debugger = NULL;
|
||||
return_value_if_fail(fscript != NULL, RET_BAD_PARAMS);
|
||||
if (fscript->code_id == NULL) {
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
debugger = debugger_server_find_debugger(fscript->code_id);
|
||||
if (debugger != NULL) {
|
||||
debugger_fscript_set_fscript(debugger, fscript);
|
||||
}
|
||||
|
||||
return RET_FAIL;
|
||||
}
|
||||
|
||||
static ret_t debugger_fscript_on_disconnect(fscript_t* fscript) {
|
||||
debugger_t* debugger = NULL;
|
||||
return_value_if_fail(fscript != NULL, RET_BAD_PARAMS);
|
||||
if (fscript->code_id == NULL) {
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
debugger = debugger_server_find_debugger(fscript->code_id);
|
||||
if (debugger != NULL) {
|
||||
debugger_fscript_set_fscript(debugger, NULL);
|
||||
}
|
||||
|
||||
return RET_FAIL;
|
||||
}
|
||||
|
||||
static ret_t debugger_fscript_on_fscript_init(fscript_t* fscript, const char* code) {
|
||||
return debugger_fscript_on_connect(fscript);
|
||||
}
|
||||
|
||||
static ret_t debugger_fscript_on_fscript_before_exec(fscript_t* fscript) {
|
||||
return debugger_fscript_on_connect(fscript);
|
||||
}
|
||||
|
||||
static ret_t debugger_fscript_on_fscript_after_exec(fscript_t* fscript) {
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static ret_t debugger_fscript_on_fscript_deinit(fscript_t* fscript) {
|
||||
return debugger_fscript_on_disconnect(fscript);
|
||||
}
|
||||
|
||||
static const fscript_hooks_t s_fscript_hooks = {
|
||||
.on_init = debugger_fscript_on_fscript_init,
|
||||
.on_deinit = debugger_fscript_on_fscript_deinit,
|
||||
.set_var = debugger_fscript_set_var,
|
||||
.exec_func = debugger_fscript_exec_func,
|
||||
.before_exec = debugger_fscript_on_fscript_before_exec,
|
||||
.after_exec = debugger_fscript_on_fscript_after_exec,
|
||||
};
|
||||
|
||||
ret_t debugger_global_init(void) {
|
||||
debugger_factory_init();
|
||||
debugger_factory_reg(DEBUGGER_LANG_FSCRIPT, debugger_fscript_create);
|
||||
|
||||
fscript_set_hooks(&s_fscript_hooks);
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
ret_t debugger_global_deinit(void) {
|
||||
fscript_set_hooks(NULL);
|
||||
debugger_factory_deinit();
|
||||
return RET_OK;
|
||||
}
|
53
src/debugger/debugger_global.h
Normal file
53
src/debugger/debugger_global.h
Normal file
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* File: debugger_global.h
|
||||
* Author: AWTK Develop Team
|
||||
* Brief: debugger global
|
||||
*
|
||||
* Copyright (c) 2022 - 2022 Guangzhou ZHIYUAN Electronics Co.,Ltd.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* License file for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* History:
|
||||
* ================================================================
|
||||
* 2022-01-12 Li XianJing <xianjimli@hotmail.com> created
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TK_DEBUGGER_GLOBAL_H
|
||||
#define TK_DEBUGGER_GLOBAL_H
|
||||
|
||||
#include "debugger/debugger_fscript.h"
|
||||
|
||||
BEGIN_C_DECLS
|
||||
|
||||
/**
|
||||
* @class debugger_global_t
|
||||
* @annotaion ["fake"]
|
||||
* 调试器全局函数。
|
||||
*/
|
||||
|
||||
/**
|
||||
* @method debugger_global_init
|
||||
* 启用调试器。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t debugger_global_init(void);
|
||||
|
||||
/**
|
||||
* @method debugger_global_deinit
|
||||
* 禁用调试器。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t debugger_global_deinit(void);
|
||||
|
||||
END_C_DECLS
|
||||
|
||||
#endif /*TK_DEBUGGER_GLOBAL_H*/
|
79
src/debugger/debugger_message.c
Normal file
79
src/debugger/debugger_message.c
Normal file
@ -0,0 +1,79 @@
|
||||
/**
|
||||
* File: debugger_message.c
|
||||
* Author: AWTK Develop Team
|
||||
* Brief: debugger message
|
||||
*
|
||||
* Copyright (c) 2022 - 2022 Guangzhou ZHIYUAN Electronics Co.,Ltd.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* License file for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* History:
|
||||
* ================================================================
|
||||
* 2022-01-15 Li XianJing <xianjimli@hotmail.com> created
|
||||
*
|
||||
*/
|
||||
|
||||
#include "debugger/debugger_message.h"
|
||||
|
||||
event_t* debugger_log_event_init(debugger_log_event_t* event, uint32_t line, const char* message) {
|
||||
return_value_if_fail(event != NULL, NULL);
|
||||
|
||||
memset(event, 0x00, sizeof(*event));
|
||||
event->e = event_init(DEBUGGER_RESP_MSG_LOG, NULL);
|
||||
event->e.size = sizeof(*event);
|
||||
event->line = line;
|
||||
event->message = message;
|
||||
|
||||
return (event_t*)event;
|
||||
}
|
||||
|
||||
debugger_log_event_t* debugger_log_event_cast(event_t* event) {
|
||||
return_value_if_fail(event != NULL && event->type == DEBUGGER_RESP_MSG_LOG, NULL);
|
||||
return_value_if_fail(event->size == sizeof(debugger_log_event_t), NULL);
|
||||
|
||||
return (debugger_log_event_t*)event;
|
||||
}
|
||||
|
||||
event_t* debugger_error_event_init(debugger_error_event_t* event, uint32_t line,
|
||||
const char* message) {
|
||||
return_value_if_fail(event != NULL, NULL);
|
||||
|
||||
memset(event, 0x00, sizeof(*event));
|
||||
event->e = event_init(DEBUGGER_RESP_MSG_ERROR, NULL);
|
||||
event->e.size = sizeof(*event);
|
||||
event->line = line;
|
||||
event->message = message;
|
||||
|
||||
return (event_t*)event;
|
||||
}
|
||||
|
||||
debugger_error_event_t* debugger_error_event_cast(event_t* event) {
|
||||
return_value_if_fail(event != NULL && event->type == DEBUGGER_RESP_MSG_ERROR, NULL);
|
||||
return_value_if_fail(event->size == sizeof(debugger_error_event_t), NULL);
|
||||
|
||||
return (debugger_error_event_t*)event;
|
||||
}
|
||||
|
||||
event_t* debugger_breaked_event_init(debugger_breaked_event_t* event, uint32_t line) {
|
||||
return_value_if_fail(event != NULL, NULL);
|
||||
|
||||
memset(event, 0x00, sizeof(*event));
|
||||
event->e = event_init(DEBUGGER_RESP_MSG_BREAKED, NULL);
|
||||
event->e.size = sizeof(*event);
|
||||
event->line = line;
|
||||
|
||||
return (event_t*)event;
|
||||
}
|
||||
|
||||
debugger_breaked_event_t* debugger_breaked_event_cast(event_t* event) {
|
||||
return_value_if_fail(event != NULL && event->type == DEBUGGER_RESP_MSG_BREAKED, NULL);
|
||||
return_value_if_fail(event->size == sizeof(debugger_breaked_event_t), NULL);
|
||||
|
||||
return (debugger_breaked_event_t*)event;
|
||||
}
|
447
src/debugger/debugger_message.h
Normal file
447
src/debugger/debugger_message.h
Normal file
@ -0,0 +1,447 @@
|
||||
/**
|
||||
* File: debugger_message.h
|
||||
* Author: AWTK Develop Team
|
||||
* Brief: debugger message
|
||||
*
|
||||
* Copyright (c) 2022 - 2022 Guangzhou ZHIYUAN Electronics Co.,Ltd.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* License file for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* History:
|
||||
* ================================================================
|
||||
* 2022-01-12 Li XianJing <xianjimli@hotmail.com> created
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TK_DEBUGGER_MESSAGE_H
|
||||
#define TK_DEBUGGER_MESSAGE_H
|
||||
|
||||
#include "debugger/debugger_fscript.h"
|
||||
|
||||
BEGIN_C_DECLS
|
||||
|
||||
/**
|
||||
* @enum debugger_req_type_t
|
||||
* @prefix DEBUGGER_REQ
|
||||
* 请求的消息类型。
|
||||
*/
|
||||
typedef enum _debugger_req_type_t {
|
||||
DEBUGGER_REQ_NONE = 0,
|
||||
/**
|
||||
* @const DEBUGGER_REQ_INIT
|
||||
* 初始化请求码。
|
||||
*/
|
||||
DEBUGGER_REQ_INIT,
|
||||
/**
|
||||
* @const DEBUGGER_REQ_STOP
|
||||
* 停止运行请求码。
|
||||
*/
|
||||
DEBUGGER_REQ_STOP,
|
||||
/**
|
||||
* @const DEBUGGER_REQ_PAUSE
|
||||
* 暂停运行请求码。
|
||||
*/
|
||||
DEBUGGER_REQ_PAUSE,
|
||||
/**
|
||||
* @const DEBUGGER_REQ_IS_PAUSED
|
||||
* 查询是否处于暂停状态请求码。
|
||||
*/
|
||||
DEBUGGER_REQ_IS_PAUSED,
|
||||
/**
|
||||
* @const DEBUGGER_REQ_NEXT
|
||||
* 运行下一步请求码。
|
||||
*/
|
||||
DEBUGGER_REQ_NEXT,
|
||||
/**
|
||||
* @const DEBUGGER_REQ_STEP_IN
|
||||
* 运行进入函数请求码。
|
||||
*/
|
||||
DEBUGGER_REQ_STEP_IN,
|
||||
/**
|
||||
* @const DEBUGGER_REQ_STEP_OUT
|
||||
* 运行退出函数请求码。
|
||||
*/
|
||||
DEBUGGER_REQ_STEP_OUT,
|
||||
/**
|
||||
* @const DEBUGGER_REQ_NEXT
|
||||
* 运行下一行请求码。
|
||||
*/
|
||||
DEBUGGER_REQ_STEP_OVER,
|
||||
/**
|
||||
* @const DEBUGGER_REQ_CONTINUE
|
||||
* 运行到下一个断点请求码。
|
||||
*/
|
||||
DEBUGGER_REQ_CONTINUE,
|
||||
/**
|
||||
* @const DEBUGGER_REQ_SET_BREAK_POINT
|
||||
* 设置断点请求码。
|
||||
*/
|
||||
DEBUGGER_REQ_SET_BREAK_POINT,
|
||||
/**
|
||||
* @const DEBUGGER_REQ_REMOVE_BREAK_POINT
|
||||
* 移除断点请求码。
|
||||
*/
|
||||
DEBUGGER_REQ_REMOVE_BREAK_POINT,
|
||||
/**
|
||||
* @const DEBUGGER_REQ_CLEAR_BREAK_POINTS
|
||||
* 清除断点请求码。
|
||||
*/
|
||||
DEBUGGER_REQ_CLEAR_BREAK_POINTS,
|
||||
/**
|
||||
* @const DEBUGGER_REQ_GET_SELF
|
||||
* 获取self对象请求码。
|
||||
*/
|
||||
DEBUGGER_REQ_GET_SELF,
|
||||
/**
|
||||
* @const DEBUGGER_REQ_GET_LOCAL
|
||||
* 获取局部变量和函数参数对象请求码。
|
||||
*/
|
||||
DEBUGGER_REQ_GET_LOCAL,
|
||||
/**
|
||||
* @const DEBUGGER_REQ_GET_GLOBAL
|
||||
* 获取global对象请求码。
|
||||
*/
|
||||
DEBUGGER_REQ_GET_GLOBAL,
|
||||
/**
|
||||
* @const DEBUGGER_REQ_GET_CODE
|
||||
* 获取源代码请求码。
|
||||
*/
|
||||
DEBUGGER_REQ_GET_CODE,
|
||||
/**
|
||||
* @const DEBUGGER_REQ_GET_CALLSTACK
|
||||
* 获取callstack请求码。
|
||||
*/
|
||||
DEBUGGER_REQ_GET_CALLSTACK,
|
||||
/**
|
||||
* @const DEBUGGER_REQ_UPDATE_CODE
|
||||
* 更新源代码请求码。
|
||||
*/
|
||||
DEBUGGER_REQ_UPDATE_CODE,
|
||||
/**
|
||||
* @const DEBUGGER_REQ_DEINIT
|
||||
* 断开调试器请求码。
|
||||
*/
|
||||
DEBUGGER_REQ_DEINIT,
|
||||
} debugger_req_type_t;
|
||||
|
||||
/**
|
||||
* @class debugger_resp_type_t
|
||||
* @prefix DEBUGGER_RESP
|
||||
* 响应/推送的消息类型。
|
||||
*/
|
||||
typedef enum _debugger_resp_type_t {
|
||||
DEBUGGER_RESP_NONE = 0,
|
||||
/**
|
||||
* @const DEBUGGER_RESP_INIT
|
||||
* 初始化响应码。
|
||||
*/
|
||||
DEBUGGER_RESP_INIT = DEBUGGER_REQ_INIT,
|
||||
/**
|
||||
* @const DEBUGGER_RESP_STOP
|
||||
* 停止运行响应码。
|
||||
*/
|
||||
DEBUGGER_RESP_STOP = DEBUGGER_REQ_STOP,
|
||||
/**
|
||||
* @const DEBUGGER_RESP_IS_PAUSED
|
||||
* 查询是否处于暂停状态响应码。
|
||||
*/
|
||||
DEBUGGER_RESP_IS_PAUSED = DEBUGGER_REQ_IS_PAUSED,
|
||||
/**
|
||||
* @const DEBUGGER_RESP_PAUSE
|
||||
* 暂停运行响应码。
|
||||
*/
|
||||
DEBUGGER_RESP_PAUSE = DEBUGGER_REQ_PAUSE,
|
||||
/**
|
||||
* @const DEBUGGER_RESP_NEXT
|
||||
* 运行下一步响应码。
|
||||
*/
|
||||
DEBUGGER_RESP_NEXT = DEBUGGER_REQ_NEXT,
|
||||
/**
|
||||
* @const DEBUGGER_RESP_STEP_IN
|
||||
* 运行进入函数响应码。
|
||||
*/
|
||||
DEBUGGER_RESP_STEP_IN = DEBUGGER_REQ_STEP_IN,
|
||||
/**
|
||||
* @const DEBUGGER_RESP_STEP_OUT
|
||||
* 运行退出函数响应码。
|
||||
*/
|
||||
DEBUGGER_RESP_STEP_OUT = DEBUGGER_REQ_STEP_OUT,
|
||||
/**
|
||||
* @const DEBUGGER_RESP_NEXT
|
||||
* 运行下一行响应码。
|
||||
*/
|
||||
DEBUGGER_RESP_STEP_OVER = DEBUGGER_REQ_STEP_OVER,
|
||||
/**
|
||||
* @const DEBUGGER_RESP_CONTINUE
|
||||
* 运行到下一个断点响应码。
|
||||
*/
|
||||
DEBUGGER_RESP_CONTINUE = DEBUGGER_REQ_CONTINUE,
|
||||
/**
|
||||
* @const DEBUGGER_REQ_SET_BREAK_POINT
|
||||
* 设置断点响应码。
|
||||
*/
|
||||
DEBUGGER_RESP_SET_BREAK_POINT = DEBUGGER_REQ_SET_BREAK_POINT,
|
||||
/**
|
||||
* @const DEBUGGER_RESP_REMOVE_BREAK_POINT
|
||||
* 移除断点响应码。
|
||||
*/
|
||||
DEBUGGER_RESP_REMOVE_BREAK_POINT = DEBUGGER_REQ_REMOVE_BREAK_POINT,
|
||||
/**
|
||||
* @const DEBUGGER_RESP_CLEAR_BREAK_POINTS
|
||||
* 清除断点响应码。
|
||||
*/
|
||||
DEBUGGER_RESP_CLEAR_BREAK_POINTS = DEBUGGER_REQ_CLEAR_BREAK_POINTS,
|
||||
/**
|
||||
* @const DEBUGGER_RESP_GET_SELF
|
||||
* 获取self对象响应码。
|
||||
*/
|
||||
DEBUGGER_RESP_GET_SELF = DEBUGGER_REQ_GET_SELF,
|
||||
/**
|
||||
* @const DEBUGGER_RESP_GET_LOCAL
|
||||
* 获取局部变量和函数参数对象响应码。
|
||||
*/
|
||||
DEBUGGER_RESP_GET_LOCAL = DEBUGGER_REQ_GET_LOCAL,
|
||||
/**
|
||||
* @const DEBUGGER_RESP_GET_GLOBAL
|
||||
* 获取global对象响应码。
|
||||
*/
|
||||
DEBUGGER_RESP_GET_GLOBAL = DEBUGGER_REQ_GET_GLOBAL,
|
||||
/**
|
||||
* @const DEBUGGER_RESP_GET_CODE
|
||||
* 获取源代码响应码。
|
||||
*/
|
||||
DEBUGGER_RESP_GET_CODE = DEBUGGER_REQ_GET_CODE,
|
||||
/**
|
||||
* @const DEBUGGER_RESP_GET_CALLSTACK
|
||||
* 获取callstack响应码。
|
||||
*/
|
||||
DEBUGGER_RESP_GET_CALLSTACK = DEBUGGER_REQ_GET_CALLSTACK,
|
||||
/**
|
||||
* @const DEBUGGER_RESP_UPDATE_CODE
|
||||
* 更新源代码响应码。
|
||||
*/
|
||||
DEBUGGER_RESP_UPDATE_CODE = DEBUGGER_REQ_UPDATE_CODE,
|
||||
/**
|
||||
* @const DEBUGGER_RESP_DEINIT
|
||||
* 断开调试器响应码。
|
||||
*/
|
||||
DEBUGGER_RESP_DEINIT = DEBUGGER_REQ_DEINIT,
|
||||
/**
|
||||
* @const DEBUGGER_RESP_MSG_LOG
|
||||
* 程序打印日志的响应码/事件码。
|
||||
*/
|
||||
DEBUGGER_RESP_MSG_LOG = 0x1000,
|
||||
/**
|
||||
* @const DEBUGGER_RESP_MSG_ERROR
|
||||
* 程序出现错误的响应码/事件码。
|
||||
*/
|
||||
DEBUGGER_RESP_MSG_ERROR,
|
||||
/**
|
||||
* @const DEBUGGER_RESP_MSG_BREAKED
|
||||
* 程序中断的响应码/事件码。
|
||||
*/
|
||||
DEBUGGER_RESP_MSG_BREAKED,
|
||||
/**
|
||||
* @const DEBUGGER_RESP_MSG_COMPLETED
|
||||
* 程序执行完成的响应码/事件码。
|
||||
*/
|
||||
DEBUGGER_RESP_MSG_COMPLETED,
|
||||
} debugger_resp_type_t;
|
||||
|
||||
/**
|
||||
* @class debugger_req_t
|
||||
* 调试器请求头。
|
||||
*/
|
||||
typedef struct _debugger_req_t {
|
||||
/**
|
||||
* @property {uint32_t} code
|
||||
* @annotation ["readable"]
|
||||
* 请求码。
|
||||
*/
|
||||
uint32_t code;
|
||||
/**
|
||||
* @property {uint32_t} size
|
||||
* @annotation ["readable"]
|
||||
* 数据长度(除请求头外)。
|
||||
*/
|
||||
uint32_t size;
|
||||
/**
|
||||
* @property {uint32_t} version
|
||||
* @annotation ["readable"]
|
||||
* 版本号。
|
||||
*/
|
||||
uint32_t version;
|
||||
/**
|
||||
* @property {uint32_t} data
|
||||
* @annotation ["readable"]
|
||||
* 数据。
|
||||
*/
|
||||
uint32_t data;
|
||||
} debugger_req_t;
|
||||
|
||||
/**
|
||||
* @class debugger_resp_t
|
||||
* 调试器响应头。
|
||||
*/
|
||||
typedef struct _debugger_resp_t {
|
||||
/**
|
||||
* @property {uint32_t} code
|
||||
* @annotation ["readable"]
|
||||
* 响应码。
|
||||
*/
|
||||
uint32_t code;
|
||||
/**
|
||||
* @property {uint32_t} error
|
||||
* @annotation ["readable"]
|
||||
* 错误码(定义同ret_t)。
|
||||
*/
|
||||
uint32_t error;
|
||||
/**
|
||||
* @property {uint32_t} version
|
||||
* @annotation ["readable"]
|
||||
* 版本号。
|
||||
*/
|
||||
uint32_t version;
|
||||
/**
|
||||
* @property {uint32_t} size
|
||||
* @annotation ["readable"]
|
||||
* 数据长度(除请求头外)。
|
||||
*/
|
||||
uint32_t size;
|
||||
} debugger_resp_t;
|
||||
|
||||
/**
|
||||
* @class debugger_log_event_t
|
||||
* @parent event_t
|
||||
* 调试器打印日志的事件。
|
||||
*/
|
||||
typedef struct _debugger_log_event_t {
|
||||
event_t e;
|
||||
/**
|
||||
* @property {uint32_t} line
|
||||
* @annotation ["readable"]
|
||||
* 打印日志的行号。
|
||||
*/
|
||||
uint32_t line;
|
||||
/**
|
||||
* @property {const char*} message
|
||||
* @annotation ["readable"]
|
||||
* 日志消息。
|
||||
*/
|
||||
const char* message;
|
||||
} debugger_log_event_t;
|
||||
|
||||
/**
|
||||
* @method debugger_log_event_init
|
||||
* 初始调试器打印日志的事件。
|
||||
*
|
||||
* @param {debugger_log_event_t*} event event对象。
|
||||
* @param {uint32_t} line 打印日志的行号。
|
||||
* @param {const char*} message 日志。
|
||||
*
|
||||
* @return {event_t*} 返回event对象。
|
||||
*/
|
||||
event_t* debugger_log_event_init(debugger_log_event_t* event, uint32_t line, const char* message);
|
||||
|
||||
/**
|
||||
* @method debugger_log_event_cast
|
||||
* @annotation ["cast"]
|
||||
*
|
||||
* 把event对象转debugger_log_event_t对象。
|
||||
* @param {event_t*} event event对象。
|
||||
*
|
||||
* @return {debugger_log_event_t*} 返回event对象。
|
||||
*/
|
||||
debugger_log_event_t* debugger_log_event_cast(event_t* event);
|
||||
|
||||
/**
|
||||
* @class debugger_error_event_t
|
||||
* @parent event_t
|
||||
* 程序错误事件。
|
||||
*/
|
||||
typedef struct _debugger_error_event_t {
|
||||
event_t e;
|
||||
/**
|
||||
* @property {uint32_t} line
|
||||
* @annotation ["readable"]
|
||||
* 出现错误的行号。
|
||||
*/
|
||||
uint32_t line;
|
||||
/**
|
||||
* @property {const char*} message
|
||||
* @annotation ["readable"]
|
||||
* 错误消息。
|
||||
*/
|
||||
const char* message;
|
||||
} debugger_error_event_t;
|
||||
|
||||
/**
|
||||
* @method debugger_error_event_init
|
||||
* 初始程序错误的事件。
|
||||
*
|
||||
* @param {debugger_error_event_t*} event event对象。
|
||||
* @param {uint32_t} line 出现错误的行号。
|
||||
* @param {const char*} message 错误信息。
|
||||
*
|
||||
* @return {event_t*} 返回event对象。
|
||||
*/
|
||||
event_t* debugger_error_event_init(debugger_error_event_t* event, uint32_t line,
|
||||
const char* message);
|
||||
|
||||
/**
|
||||
* @method debugger_error_event_cast
|
||||
* @annotation ["cast"]
|
||||
*
|
||||
* 把event对象转debugger_error_event_t对象。
|
||||
* @param {event_t*} event event对象。
|
||||
*
|
||||
* @return {debugger_error_event_t*} 返回event对象。
|
||||
*/
|
||||
debugger_error_event_t* debugger_error_event_cast(event_t* event);
|
||||
|
||||
/**
|
||||
* @class debugger_breaked_event_t
|
||||
* @parent event_t
|
||||
* 调试器中断运行的事件。
|
||||
*/
|
||||
typedef struct _debugger_breaked_event_t {
|
||||
event_t e;
|
||||
/**
|
||||
* @property {uint32_t} line
|
||||
* @annotation ["readable"]
|
||||
* 中断运行的行号。
|
||||
*/
|
||||
uint32_t line;
|
||||
} debugger_breaked_event_t;
|
||||
|
||||
/**
|
||||
* @method debugger_breaked_event_init
|
||||
* 初始调试器中断运行的事件。
|
||||
*
|
||||
* @param {debugger_breaked_event_t*} event event对象。
|
||||
* @param {uint32_t} line 中断运行的行号。
|
||||
*
|
||||
* @return {event_t*} 返回event对象。
|
||||
*/
|
||||
event_t* debugger_breaked_event_init(debugger_breaked_event_t* event, uint32_t line);
|
||||
|
||||
/**
|
||||
* @method debugger_breaked_event_cast
|
||||
* @annotation ["cast"]
|
||||
*
|
||||
* 把event对象转debugger_breaked_event_t对象。
|
||||
* @param {event_t*} event event对象。
|
||||
*
|
||||
* @return {debugger_breaked_event_t*} 返回event对象。
|
||||
*/
|
||||
debugger_breaked_event_t* debugger_breaked_event_cast(event_t* event);
|
||||
|
||||
END_C_DECLS
|
||||
|
||||
#endif /*TK_DEBUGGER_MESSAGE_H*/
|
532
src/debugger/debugger_server.c
Normal file
532
src/debugger/debugger_server.c
Normal file
@ -0,0 +1,532 @@
|
||||
/**
|
||||
* File: debugger_server.h
|
||||
* Author: AWTK Develop Team
|
||||
* Brief: debugger server
|
||||
*
|
||||
* Copyright (c) 2022 - 2022 Guangzhou ZHIYUAN Electronics Co.,Ltd.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* License file for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* History:
|
||||
* ================================================================
|
||||
* 2022-01-12 Li XianJing <xianjimli@hotmail.com> created
|
||||
*
|
||||
*/
|
||||
|
||||
#include "tkc/thread.h"
|
||||
#include "tkc/buffer.h"
|
||||
#include "tkc/object_default.h"
|
||||
#include "ubjson/ubjson_writer.h"
|
||||
#include "debugger/debugger_factory.h"
|
||||
#include "debugger/debugger_server.h"
|
||||
#include "debugger/debugger_message.h"
|
||||
|
||||
typedef struct _debugger_server_t {
|
||||
darray_t debuggers;
|
||||
debugger_t* debugger;
|
||||
tk_thread_t* thread;
|
||||
|
||||
/*等待退出*/
|
||||
bool_t quiting;
|
||||
/*退出完成*/
|
||||
bool_t quited;
|
||||
/*启动完成*/
|
||||
bool_t started;
|
||||
|
||||
tk_istream_t* in;
|
||||
tk_ostream_t* out;
|
||||
tk_iostream_t* io;
|
||||
tk_mutex_nest_t* mutex;
|
||||
|
||||
/*读取数据的缓冲区*/
|
||||
void* buff;
|
||||
uint32_t capacity;
|
||||
} debugger_server_t;
|
||||
|
||||
static void* debugger_server_run(void* ctx);
|
||||
static ret_t debugger_server_send_object(debugger_server_t* server, debugger_resp_t* resp,
|
||||
tk_object_t* obj);
|
||||
|
||||
static debugger_server_t* debugger_server_create(tk_iostream_t* io) {
|
||||
debugger_server_t* server = TKMEM_ZALLOC(debugger_server_t);
|
||||
return_value_if_fail(server != NULL, NULL);
|
||||
|
||||
assert(io != NULL);
|
||||
|
||||
server->io = io;
|
||||
server->in = tk_iostream_get_istream(io);
|
||||
server->out = tk_iostream_get_ostream(io);
|
||||
darray_init(&(server->debuggers), 5, (tk_destroy_t)tk_object_unref, NULL);
|
||||
|
||||
server->mutex = tk_mutex_nest_create();
|
||||
goto_error_if_fail(server->mutex != NULL);
|
||||
|
||||
server->thread = tk_thread_create(debugger_server_run, server);
|
||||
goto_error_if_fail(server->thread != NULL);
|
||||
tk_thread_start(server->thread);
|
||||
|
||||
while (!(server->started)) {
|
||||
sleep_ms(100);
|
||||
}
|
||||
log_debug("debugger_server_thread started\n");
|
||||
|
||||
return server;
|
||||
|
||||
error:
|
||||
TK_OBJECT_UNREF(io);
|
||||
darray_deinit(&(server->debuggers));
|
||||
TKMEM_FREE(server);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ret_t debugger_server_extend(debugger_server_t* server, uint32_t size) {
|
||||
ret_t ret = RET_OK;
|
||||
return_value_if_fail(server != NULL, RET_BAD_PARAMS);
|
||||
|
||||
if (server->capacity < size) {
|
||||
void* buff = TKMEM_REALLOC(server->buff, size);
|
||||
if (buff != NULL) {
|
||||
server->buff = buff;
|
||||
server->capacity = size;
|
||||
} else {
|
||||
ret = RET_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ret_t debugger_server_write_data(tk_ostream_t* out, const void* data, uint32_t size) {
|
||||
return tk_ostream_write_len(out, data, size, DEBUGGER_IO_WRITE_TIMEOUT) == size ? RET_OK : RET_IO;
|
||||
}
|
||||
|
||||
static ret_t debugger_server_read_data(tk_istream_t* in, void* data, uint32_t size) {
|
||||
return tk_istream_read_len(in, data, size, DEBUGGER_IO_READ_TIMEOUT) == size ? RET_OK : RET_IO;
|
||||
}
|
||||
|
||||
static ret_t debugger_server_send_data_impl(tk_ostream_t* out, debugger_resp_t* resp,
|
||||
binary_data_t* data) {
|
||||
ret_t ret = RET_FAIL;
|
||||
resp->version = DEBUGGER_VERSION;
|
||||
resp->size = data != NULL ? data->size : 0;
|
||||
|
||||
ret = debugger_server_write_data(out, resp, sizeof(*resp));
|
||||
return_value_if_fail(ret == RET_OK, RET_IO);
|
||||
|
||||
if (resp->size > 0) {
|
||||
ret = debugger_server_write_data(out, data->data, data->size);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ret_t debugger_server_send_data(debugger_server_t* server, debugger_resp_t* resp,
|
||||
binary_data_t* data) {
|
||||
ret_t ret = RET_FAIL;
|
||||
|
||||
if (tk_mutex_nest_lock(server->mutex) == RET_OK) {
|
||||
ret = debugger_server_send_data_impl(server->out, resp, data);
|
||||
tk_mutex_nest_unlock(server->mutex);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ret_t debugger_server_send_object(debugger_server_t* server, debugger_resp_t* resp,
|
||||
tk_object_t* obj) {
|
||||
wbuffer_t wb;
|
||||
ret_t ret = RET_FAIL;
|
||||
ubjson_writer_t writer;
|
||||
binary_data_t data = {0, NULL};
|
||||
|
||||
if (obj != NULL) {
|
||||
return_value_if_fail(wbuffer_init_extendable(&wb) != NULL, RET_OOM);
|
||||
|
||||
ubjson_writer_init(&writer, (ubjson_write_callback_t)wbuffer_write_binary, &wb);
|
||||
ret = ubjson_writer_write_object(&writer, obj);
|
||||
goto_error_if_fail(ret == RET_OK);
|
||||
|
||||
data.data = wb.data;
|
||||
data.size = wb.cursor;
|
||||
}
|
||||
|
||||
ret = debugger_server_send_data(server, resp, &data);
|
||||
|
||||
error:
|
||||
wbuffer_deinit(&wb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ret_t debugger_server_read_request_impl(debugger_server_t* server, debugger_req_t* req) {
|
||||
tk_istream_t* in = server->in;
|
||||
ret_t ret = debugger_server_read_data(in, req, sizeof(*req));
|
||||
return_value_if_fail(ret == RET_OK, RET_IO);
|
||||
|
||||
assert(req->version == DEBUGGER_VERSION);
|
||||
return_value_if_fail(debugger_server_extend(server, req->size) == RET_OK, RET_OOM);
|
||||
|
||||
if (req->size > 0) {
|
||||
ret = debugger_server_read_data(in, server->buff, req->size);
|
||||
} else {
|
||||
ret = RET_OK;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ret_t debugger_server_read_request(debugger_server_t* server, debugger_req_t* req) {
|
||||
ret_t ret = tk_istream_wait_for_data(server->in, 500);
|
||||
|
||||
if (ret != RET_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (tk_mutex_nest_lock(server->mutex) == RET_OK) {
|
||||
ret = debugger_server_read_request_impl(server, req);
|
||||
tk_mutex_nest_unlock(server->mutex);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static debugger_t* debugger_server_find(debugger_server_t* server, const char* code_id) {
|
||||
uint32_t i = 0;
|
||||
debugger_t* debugger = NULL;
|
||||
return_value_if_fail(code_id != NULL, NULL);
|
||||
return_value_if_fail(server != NULL, NULL);
|
||||
|
||||
if (tk_mutex_nest_lock(server->mutex) == RET_OK) {
|
||||
for (i = 0; i < server->debuggers.size; i++) {
|
||||
debugger_t* iter = (debugger_t*)darray_get(&(server->debuggers), i);
|
||||
if (debugger_match(iter, code_id)) {
|
||||
debugger = iter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
tk_mutex_nest_unlock(server->mutex);
|
||||
}
|
||||
|
||||
return debugger;
|
||||
}
|
||||
|
||||
static ret_t debugger_server_on_events(void* ctx, event_t* e) {
|
||||
ret_t ret = RET_OK;
|
||||
debugger_resp_t msg;
|
||||
tk_object_t* obj = NULL;
|
||||
tk_ostream_t* out = NULL;
|
||||
debugger_t* debugger = DEBUGGER(e->target);
|
||||
debugger_server_t* server = (debugger_server_t*)ctx;
|
||||
return_value_if_fail(debugger != NULL && server != NULL, RET_BAD_PARAMS);
|
||||
|
||||
out = server->out;
|
||||
return_value_if_fail(out != NULL, RET_BAD_PARAMS);
|
||||
|
||||
obj = object_default_create();
|
||||
return_value_if_fail(obj != NULL, RET_BAD_PARAMS);
|
||||
|
||||
memset(&msg, 0x00, sizeof(msg));
|
||||
msg.code = e->type;
|
||||
msg.error = RET_OK;
|
||||
|
||||
switch (e->type) {
|
||||
case DEBUGGER_RESP_MSG_LOG: {
|
||||
debugger_log_event_t* event = debugger_log_event_cast(e);
|
||||
tk_object_set_prop_int(obj, STR_DEBUGGER_EVENT_PROP_LINE, event->line);
|
||||
tk_object_set_prop_str(obj, STR_DEBUGGER_EVENT_PROP_MESSAGE, event->message);
|
||||
ret = debugger_server_send_object(server, &msg, obj);
|
||||
break;
|
||||
}
|
||||
case DEBUGGER_RESP_MSG_ERROR: {
|
||||
debugger_error_event_t* event = debugger_error_event_cast(e);
|
||||
tk_object_set_prop_int(obj, STR_DEBUGGER_EVENT_PROP_LINE, event->line);
|
||||
tk_object_set_prop_str(obj, STR_DEBUGGER_EVENT_PROP_MESSAGE, event->message);
|
||||
ret = debugger_server_send_object(server, &msg, obj);
|
||||
break;
|
||||
}
|
||||
case DEBUGGER_RESP_MSG_BREAKED: {
|
||||
debugger_breaked_event_t* event = debugger_breaked_event_cast(e);
|
||||
tk_object_set_prop_int(obj, STR_DEBUGGER_EVENT_PROP_LINE, event->line);
|
||||
ret = debugger_server_send_object(server, &msg, obj);
|
||||
break;
|
||||
}
|
||||
case DEBUGGER_RESP_MSG_COMPLETED: {
|
||||
debugger_server_send_data(server, &msg, NULL);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
log_debug("not supported event:%d\n", (int)(e->type));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TK_OBJECT_UNREF(obj);
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static ret_t debugger_server_init_debugger(debugger_server_t* server, const char* arg) {
|
||||
ret_t ret = RET_FAIL;
|
||||
char lang[TK_NAME_LEN + 1];
|
||||
const char* code_id = NULL;
|
||||
return_value_if_fail(server != NULL, RET_BAD_PARAMS);
|
||||
code_id = strchr(arg, ':');
|
||||
return_value_if_fail(code_id != NULL, RET_BAD_PARAMS);
|
||||
|
||||
tk_strncpy_s(lang, TK_NAME_LEN, arg, code_id - arg);
|
||||
code_id++;
|
||||
if (tk_mutex_nest_lock(server->mutex) == RET_OK) {
|
||||
debugger_t* debugger = debugger_server_find(server, code_id);
|
||||
if (debugger == NULL) {
|
||||
debugger = debugger_factory_create_debugger(lang, code_id);
|
||||
if (debugger != NULL) {
|
||||
ret = darray_push(&(server->debuggers), debugger);
|
||||
if (ret != RET_OK) {
|
||||
OBJECT_UNREF(debugger);
|
||||
} else {
|
||||
emitter_on(EMITTER(debugger), DEBUGGER_RESP_MSG_LOG, debugger_server_on_events, server);
|
||||
emitter_on(EMITTER(debugger), DEBUGGER_RESP_MSG_ERROR, debugger_server_on_events, server);
|
||||
emitter_on(EMITTER(debugger), DEBUGGER_RESP_MSG_BREAKED, debugger_server_on_events,
|
||||
server);
|
||||
emitter_on(EMITTER(debugger), DEBUGGER_RESP_MSG_COMPLETED, debugger_server_on_events,
|
||||
server);
|
||||
}
|
||||
} else {
|
||||
log_debug("create debugger:%s %s failed\n", lang, code_id);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == RET_OK) {
|
||||
server->debugger = debugger;
|
||||
}
|
||||
tk_mutex_nest_unlock(server->mutex);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ret_t debugger_server_destroy(debugger_server_t* server) {
|
||||
return_value_if_fail(server != NULL, RET_BAD_PARAMS);
|
||||
|
||||
server->quiting = TRUE;
|
||||
while (!server->quited) {
|
||||
sleep_ms(100);
|
||||
}
|
||||
|
||||
tk_mutex_nest_destroy(server->mutex);
|
||||
server->mutex = NULL;
|
||||
|
||||
darray_deinit(&(server->debuggers));
|
||||
tk_thread_destroy(server->thread);
|
||||
TK_OBJECT_UNREF(server->io);
|
||||
TKMEM_FREE(server->buff);
|
||||
TKMEM_FREE(server);
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static ret_t debugger_server_dispatch(debugger_server_t* server) {
|
||||
ret_t ret = RET_OK;
|
||||
debugger_req_t req;
|
||||
debugger_resp_t resp;
|
||||
debugger_t* debugger = NULL;
|
||||
|
||||
server->started = TRUE;
|
||||
while (!(server->quiting)) {
|
||||
ret = debugger_server_read_request(server, &req);
|
||||
if (ret == RET_TIMEOUT) {
|
||||
continue;
|
||||
} else if (ret != RET_OK) {
|
||||
break;
|
||||
}
|
||||
resp.size = 0;
|
||||
resp.code = req.code;
|
||||
resp.error = RET_OK;
|
||||
|
||||
if (req.code == DEBUGGER_REQ_INIT) {
|
||||
resp.error = debugger_server_init_debugger(server, (const char*)(server->buff));
|
||||
break_if_fail(debugger_server_send_data(server, &resp, NULL) == RET_OK);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (server->debugger == NULL) {
|
||||
resp.error = RET_NOT_FOUND;
|
||||
break_if_fail(debugger_server_send_data(server, &resp, NULL) == RET_OK);
|
||||
continue;
|
||||
}
|
||||
|
||||
debugger = server->debugger;
|
||||
|
||||
switch (req.code) {
|
||||
case DEBUGGER_REQ_STOP: {
|
||||
resp.error = debugger_stop(debugger);
|
||||
break;
|
||||
}
|
||||
case DEBUGGER_REQ_PAUSE: {
|
||||
resp.error = debugger_pause(debugger);
|
||||
break;
|
||||
}
|
||||
case DEBUGGER_REQ_IS_PAUSED: {
|
||||
resp.error = debugger_is_paused(debugger) ? RET_OK : RET_FAIL;
|
||||
break;
|
||||
}
|
||||
case DEBUGGER_REQ_NEXT: {
|
||||
resp.error = debugger_next(debugger);
|
||||
break;
|
||||
}
|
||||
case DEBUGGER_REQ_STEP_IN: {
|
||||
resp.error = debugger_step_in(debugger);
|
||||
break;
|
||||
}
|
||||
case DEBUGGER_REQ_STEP_OUT: {
|
||||
resp.error = debugger_step_out(debugger);
|
||||
break;
|
||||
}
|
||||
case DEBUGGER_REQ_STEP_OVER: {
|
||||
resp.error = debugger_step_over(debugger);
|
||||
break;
|
||||
}
|
||||
case DEBUGGER_REQ_CONTINUE: {
|
||||
resp.error = debugger_continue(debugger);
|
||||
break;
|
||||
}
|
||||
case DEBUGGER_REQ_SET_BREAK_POINT: {
|
||||
resp.error = debugger_set_break_point(debugger, req.data);
|
||||
break;
|
||||
}
|
||||
case DEBUGGER_REQ_REMOVE_BREAK_POINT: {
|
||||
resp.error = debugger_remove_break_point(debugger, req.data);
|
||||
break;
|
||||
}
|
||||
case DEBUGGER_REQ_CLEAR_BREAK_POINTS: {
|
||||
resp.error = debugger_clear_break_points(debugger);
|
||||
break;
|
||||
}
|
||||
case DEBUGGER_REQ_GET_SELF: {
|
||||
if (debugger_lock(debugger) == RET_OK) {
|
||||
tk_object_t* obj = debugger_get_self(debugger);
|
||||
ret = debugger_server_send_object(server, &resp, obj);
|
||||
TK_OBJECT_UNREF(obj);
|
||||
debugger_unlock(debugger);
|
||||
goto_error_if_fail(ret == RET_OK);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DEBUGGER_REQ_GET_LOCAL: {
|
||||
if (debugger_lock(debugger) == RET_OK) {
|
||||
tk_object_t* obj = debugger_get_local(debugger, req.data);
|
||||
ret = debugger_server_send_object(server, &resp, obj);
|
||||
TK_OBJECT_UNREF(obj);
|
||||
debugger_unlock(debugger);
|
||||
goto_error_if_fail(ret == RET_OK);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DEBUGGER_REQ_GET_GLOBAL: {
|
||||
if (debugger_lock(debugger) == RET_OK) {
|
||||
tk_object_t* obj = debugger_get_global(debugger);
|
||||
ret = debugger_server_send_object(server, &resp, obj);
|
||||
TK_OBJECT_UNREF(obj);
|
||||
debugger_unlock(debugger);
|
||||
goto_error_if_fail(ret == RET_OK);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DEBUGGER_REQ_GET_CODE: {
|
||||
binary_data_t data = {0, NULL};
|
||||
if (debugger_lock(debugger) == RET_OK) {
|
||||
resp.error = debugger_get_code(debugger, &data);
|
||||
ret = debugger_server_send_data(server, &resp, &data);
|
||||
debugger_unlock(debugger);
|
||||
goto_error_if_fail(ret == RET_OK);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
case DEBUGGER_REQ_GET_CALLSTACK: {
|
||||
binary_data_t data = {0, NULL};
|
||||
if (debugger_lock(debugger) == RET_OK) {
|
||||
resp.error = debugger_get_callstack(debugger, &data);
|
||||
ret = debugger_server_send_data(server, &resp, &data);
|
||||
debugger_unlock(debugger);
|
||||
goto_error_if_fail(ret == RET_OK);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
case DEBUGGER_REQ_UPDATE_CODE: {
|
||||
binary_data_t code = {req.size, server->buff};
|
||||
resp.error = debugger_update_code(debugger, &code);
|
||||
break;
|
||||
}
|
||||
case DEBUGGER_REQ_DEINIT: {
|
||||
resp.error = debugger_deinit(debugger);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
log_debug("not supported code:%u\n", req.code);
|
||||
resp.error = RET_NOT_IMPL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break_if_fail(debugger_server_send_data(server, &resp, NULL) == RET_OK);
|
||||
|
||||
if (req.code == DEBUGGER_REQ_DEINIT) {
|
||||
log_debug("quit because deinit\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
error:
|
||||
server->quited = TRUE;
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static void* debugger_server_run(void* ctx) {
|
||||
debugger_server_dispatch((debugger_server_t*)ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static debugger_server_t* s_debugger_server = NULL;
|
||||
|
||||
ret_t debugger_server_start(tk_iostream_t* io) {
|
||||
debugger_server_t* server = NULL;
|
||||
return_value_if_fail(io != NULL && s_debugger_server == NULL, RET_BAD_PARAMS);
|
||||
|
||||
server = debugger_server_create(io);
|
||||
return_value_if_fail(server != NULL, RET_BAD_PARAMS);
|
||||
|
||||
s_debugger_server = server;
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
ret_t debugger_server_stop(void) {
|
||||
return_value_if_fail(s_debugger_server != NULL, RET_BAD_PARAMS);
|
||||
|
||||
debugger_server_destroy(s_debugger_server);
|
||||
s_debugger_server = NULL;
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
debugger_t* debugger_server_find_debugger(const char* code_id) {
|
||||
debugger_server_t* server = s_debugger_server;
|
||||
return_value_if_fail(code_id != NULL, NULL);
|
||||
|
||||
return debugger_server_find(server, code_id);
|
||||
}
|
||||
|
||||
bool_t debugger_server_is_running(void) {
|
||||
return s_debugger_server != NULL;
|
||||
}
|
72
src/debugger/debugger_server.h
Normal file
72
src/debugger/debugger_server.h
Normal file
@ -0,0 +1,72 @@
|
||||
/**
|
||||
* File: debugger_server.h
|
||||
* Author: AWTK Develop Team
|
||||
* Brief: debugger server
|
||||
*
|
||||
* Copyright (c) 2022 - 2022 Guangzhou ZHIYUAN Electronics Co.,Ltd.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* License file for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* History:
|
||||
* ================================================================
|
||||
* 2022-01-12 Li XianJing <xianjimli@hotmail.com> created
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TK_DEBUGGER_SERVER_H
|
||||
#define TK_DEBUGGER_SERVER_H
|
||||
|
||||
#include "tkc/iostream.h"
|
||||
#include "debugger/debugger.h"
|
||||
|
||||
BEGIN_C_DECLS
|
||||
|
||||
/**
|
||||
* @class debugger_server_t
|
||||
* @annotaion ["fake"]
|
||||
* 调试器服务器。
|
||||
*/
|
||||
|
||||
/**
|
||||
* @method debugger_server_start
|
||||
* 启用调试器服务。
|
||||
* @param {tk_iostream_t*} io IO对象,用于和客户端通信。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t debugger_server_start(tk_iostream_t* io);
|
||||
|
||||
/**
|
||||
* @method debugger_server_stop
|
||||
* 停用调试器服务。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t debugger_server_stop(void);
|
||||
|
||||
/**
|
||||
* @method debugger_server_is_running
|
||||
* 判断服务是否在运行。
|
||||
*
|
||||
* @return {bool_t} 返回TRUE表示在运行,否则表示没有运行。
|
||||
*/
|
||||
bool_t debugger_server_is_running(void);
|
||||
|
||||
/**
|
||||
* @method debugger_server_find_debugger
|
||||
* 查找调试器对象。
|
||||
* @param {const char*} code_id 代码ID。
|
||||
*
|
||||
* @return {debugger_t*} 返回debugger对象或者NULL。
|
||||
*/
|
||||
debugger_t* debugger_server_find_debugger(const char* code_id);
|
||||
|
||||
END_C_DECLS
|
||||
|
||||
#endif /*TK_DEBUGGER_SERVER_H*/
|
67
src/debugger/debugger_server_tcp.c
Normal file
67
src/debugger/debugger_server_tcp.c
Normal file
@ -0,0 +1,67 @@
|
||||
/**
|
||||
* File: debugger_server.h
|
||||
* Author: AWTK Develop Team
|
||||
* Brief: debugger server
|
||||
*
|
||||
* Copyright (c) 2022 - 2022 Guangzhou ZHIYUAN Electronics Co.,Ltd.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* License file for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* History:
|
||||
* ================================================================
|
||||
* 2022-01-14 Li XianJing <xianjimli@hotmail.com> created
|
||||
*
|
||||
*/
|
||||
|
||||
#include "tkc/socket_helper.h"
|
||||
#include "streams/inet/iostream_tcp.h"
|
||||
#include "debugger/debugger_server_tcp.h"
|
||||
|
||||
static int s_server_sock = -1;
|
||||
|
||||
ret_t debugger_server_tcp_init(uint32_t port) {
|
||||
int sock = -1;
|
||||
return_value_if_fail(!debugger_server_is_running(), RET_BAD_PARAMS);
|
||||
|
||||
sock = tk_tcp_listen(port);
|
||||
return_value_if_fail(sock >= 0, RET_BAD_PARAMS);
|
||||
|
||||
s_server_sock = sock;
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
ret_t debugger_server_tcp_start(void) {
|
||||
int sock = -1;
|
||||
return_value_if_fail(s_server_sock >= 0, RET_BAD_PARAMS);
|
||||
return_value_if_fail(!debugger_server_is_running(), RET_BAD_PARAMS);
|
||||
|
||||
sock = tk_tcp_accept(s_server_sock);
|
||||
if (sock >= 0) {
|
||||
tk_iostream_t* io = tk_iostream_tcp_create(sock);
|
||||
if (io != NULL) {
|
||||
debugger_server_start(io);
|
||||
} else {
|
||||
log_warn("tk_iostream_tcp_create failed\n");
|
||||
}
|
||||
} else {
|
||||
log_warn("tk_tcp_accept failed\n");
|
||||
}
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
ret_t debugger_server_tcp_deinit(void) {
|
||||
return_value_if_fail(s_server_sock >= 0, RET_BAD_PARAMS);
|
||||
socket_close(s_server_sock);
|
||||
s_server_sock = -1;
|
||||
debugger_server_stop();
|
||||
|
||||
return RET_OK;
|
||||
}
|
64
src/debugger/debugger_server_tcp.h
Normal file
64
src/debugger/debugger_server_tcp.h
Normal file
@ -0,0 +1,64 @@
|
||||
/**
|
||||
* File: debugger_server_tcp.h
|
||||
* Author: AWTK Develop Team
|
||||
* Brief: debugger server_tcp
|
||||
*
|
||||
* Copyright (c) 2022 - 2022 Guangzhou ZHIYUAN Electronics Co.,Ltd.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* License file for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* History:
|
||||
* ================================================================
|
||||
* 2022-01-14 Li XianJing <xianjimli@hotmail.com> created
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TK_DEBUGGER_SERVER_TCP_H
|
||||
#define TK_DEBUGGER_SERVER_TCP_H
|
||||
|
||||
#include "tkc/iostream.h"
|
||||
#include "debugger/debugger.h"
|
||||
#include "debugger/debugger_server.h"
|
||||
|
||||
BEGIN_C_DECLS
|
||||
|
||||
/**
|
||||
* @class debugger_server_tcp_t
|
||||
* 调试器TCP服务端。
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @method debugger_server_tcp_init
|
||||
* 初始化调试器服务。
|
||||
* @param {uint32_t} port 监听端口。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t debugger_server_tcp_init(uint32_t port);
|
||||
|
||||
/**
|
||||
* @method debugger_server_tcp_start
|
||||
* 启动调试器服务。
|
||||
* > 接收客户端请求,并启动服务。
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t debugger_server_tcp_start(void);
|
||||
|
||||
/**
|
||||
* @method debugger_server_tcp_deinit
|
||||
* 停止调试器服务。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t debugger_server_tcp_deinit(void);
|
||||
|
||||
END_C_DECLS
|
||||
|
||||
#endif /*TK_DEBUGGER_SERVER_TCP_H*/
|
@ -325,13 +325,13 @@ static ret_t func_flow_get(fscript_t* fscript, fscript_args_t* args, value_t* re
|
||||
const char* subname = NULL;
|
||||
FSCRIPT_FUNC_CHECK(args->size >= 2, RET_BAD_PARAMS);
|
||||
type = value_str(args->args);
|
||||
subname = value_str(args->args+1);
|
||||
subname = value_str(args->args + 1);
|
||||
FSCRIPT_FUNC_CHECK(name != NULL && subname != NULL, RET_BAD_PARAMS);
|
||||
|
||||
tk_snprintf(name, sizeof(name)-1, "%s.%s", type, subname);
|
||||
tk_snprintf(name, sizeof(name) - 1, "%s.%s", type, subname);
|
||||
if (tk_object_get_prop(fscript->obj, name, result) != RET_OK) {
|
||||
if(args->size > 2) {
|
||||
value_copy(result, args->args+2);
|
||||
if (args->size > 2) {
|
||||
value_copy(result, args->args + 2);
|
||||
} else {
|
||||
result->type = VALUE_TYPE_INVALID;
|
||||
}
|
||||
@ -346,10 +346,10 @@ static ret_t func_flow_set(fscript_t* fscript, fscript_args_t* args, value_t* re
|
||||
const char* subname = NULL;
|
||||
FSCRIPT_FUNC_CHECK(args->size == 3, RET_BAD_PARAMS);
|
||||
type = value_str(args->args);
|
||||
subname = value_str(args->args+1);
|
||||
subname = value_str(args->args + 1);
|
||||
FSCRIPT_FUNC_CHECK(name != NULL && subname != NULL, RET_BAD_PARAMS);
|
||||
|
||||
tk_snprintf(name, sizeof(name)-1, "%s.%s", type, subname);
|
||||
tk_snprintf(name, sizeof(name) - 1, "%s.%s", type, subname);
|
||||
value_set_bool(result, tk_object_set_prop(fscript->obj, name, args->args + 2) == RET_OK);
|
||||
|
||||
return RET_OK;
|
||||
|
@ -242,7 +242,7 @@ static ret_t func_is_positive(fscript_t* fscript, fscript_args_t* args, value_t*
|
||||
}
|
||||
|
||||
static ret_t func_random0to1(fscript_t* fscript, fscript_args_t* args, value_t* result) {
|
||||
double v = (double)(random()%100000)/100000.0f;
|
||||
double v = (double)(random() % 100000) / 100000.0f;
|
||||
|
||||
value_set_double(result, v);
|
||||
|
||||
|
@ -27,6 +27,13 @@
|
||||
#define STR_GLOBAL_PREFIX "global."
|
||||
#define GLOBAL_PREFIX_LEN 7
|
||||
|
||||
static const fscript_hooks_t* s_hooks;
|
||||
|
||||
ret_t fscript_set_hooks(const fscript_hooks_t* hooks) {
|
||||
s_hooks = hooks;
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static ret_t func_function_def(fscript_t* fscript, fscript_args_t* args, value_t* result) {
|
||||
return RET_OK;
|
||||
}
|
||||
@ -64,9 +71,17 @@ static ret_t func_return(fscript_t* fscript, fscript_args_t* args, value_t* resu
|
||||
static ret_t func_get(fscript_t* fscript, fscript_args_t* args, value_t* result);
|
||||
static ret_t func_set(fscript_t* fscript, fscript_args_t* args, value_t* result);
|
||||
static ret_t func_unset(fscript_t* fscript, fscript_args_t* args, value_t* result);
|
||||
static ret_t fscript_exec_func(fscript_t* fscript, fscript_func_call_t* iter, value_t* result);
|
||||
static ret_t func_set_local(fscript_t* fscript, fscript_args_t* args, value_t* result);
|
||||
|
||||
static ret_t fscript_exec_func(fscript_t* fscript, const char* name, fscript_func_call_t* iter,
|
||||
value_t* result) {
|
||||
if (s_hooks != NULL && s_hooks->exec_func != NULL) {
|
||||
return s_hooks->exec_func(fscript, name, iter, result);
|
||||
} else {
|
||||
return fscript_exec_func_default(fscript, iter, result);
|
||||
}
|
||||
}
|
||||
|
||||
ret_t fscript_set_error(fscript_t* fscript, ret_t code, const char* func, const char* message) {
|
||||
fscript->error_code = code;
|
||||
fscript->error_func = fscript->curr;
|
||||
@ -93,6 +108,14 @@ ret_t fscript_set_on_error(fscript_t* fscript, fscript_on_error_t on_error, void
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
ret_t fscript_set_print_func(fscript_t* fscript, fscript_func_t print) {
|
||||
return_value_if_fail(fscript != NULL, RET_BAD_PARAMS);
|
||||
|
||||
fscript->print = print;
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static value_t* value_set_func(value_t* v, fscript_func_call_t* func) {
|
||||
value_set_pointer(v, func);
|
||||
v->type = VALUE_TYPE_FSCRIPT_FUNC;
|
||||
@ -193,14 +216,17 @@ static ret_t fscript_func_call_destroy(fscript_func_call_t* call) {
|
||||
}
|
||||
|
||||
typedef struct _fscript_function_def_t {
|
||||
char* name;
|
||||
darray_t params;
|
||||
fscript_func_call_t* body;
|
||||
} fscript_function_def_t;
|
||||
|
||||
static fscript_function_def_t* fscript_function_def_create(fscript_func_call_t* body) {
|
||||
static fscript_function_def_t* fscript_function_def_create(const char* name,
|
||||
fscript_func_call_t* body) {
|
||||
fscript_function_def_t* func = TKMEM_ZALLOC(fscript_function_def_t);
|
||||
return_value_if_fail(func != NULL, NULL);
|
||||
func->body = body;
|
||||
func->name = tk_strdup(name);
|
||||
darray_init(&(func->params), 3, default_destroy, NULL);
|
||||
return func;
|
||||
}
|
||||
@ -209,6 +235,7 @@ static ret_t fscript_function_def_destroy(fscript_function_def_t* func) {
|
||||
return_value_if_fail(func != NULL, RET_BAD_PARAMS);
|
||||
darray_deinit(&(func->params));
|
||||
fscript_func_call_destroy(func->body);
|
||||
TKMEM_FREE(func->name);
|
||||
memset(func, 0x00, sizeof(fscript_function_def_t));
|
||||
TKMEM_FREE(func);
|
||||
|
||||
@ -288,7 +315,7 @@ static ret_t fscript_get_var(fscript_t* fscript, const char* name, value_t* valu
|
||||
return tk_object_get_prop(fscript->obj, name, value);
|
||||
}
|
||||
|
||||
static ret_t fscript_set_var(fscript_t* fscript, const char* name, const value_t* value) {
|
||||
ret_t fscript_set_var_default(fscript_t* fscript, const char* name, const value_t* value) {
|
||||
value_t v;
|
||||
ret_t ret = RET_FAIL;
|
||||
|
||||
@ -305,6 +332,14 @@ static ret_t fscript_set_var(fscript_t* fscript, const char* name, const value_t
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret_t fscript_set_var(fscript_t* fscript, const char* name, const value_t* value) {
|
||||
if (s_hooks != NULL && s_hooks->set_var != NULL) {
|
||||
return s_hooks->set_var(fscript, name, value);
|
||||
} else {
|
||||
return fscript_set_var_default(fscript, name, value);
|
||||
}
|
||||
}
|
||||
|
||||
static ret_t fscript_eval_arg(fscript_t* fscript, fscript_func_call_t* iter, uint32_t i,
|
||||
value_t* d) {
|
||||
value_t v;
|
||||
@ -352,7 +387,7 @@ static ret_t fscript_eval_arg(fscript_t* fscript, fscript_func_call_t* iter, uin
|
||||
}
|
||||
}
|
||||
} else if (s->type == VALUE_TYPE_FSCRIPT_FUNC) {
|
||||
fscript_exec_func(fscript, value_func(s), d);
|
||||
fscript_exec_func(fscript, NULL, value_func(s), d);
|
||||
} else {
|
||||
value_copy(d, s);
|
||||
}
|
||||
@ -603,7 +638,7 @@ static ret_t fscript_exec_ext_func(fscript_t* fscript, fscript_func_call_t* iter
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ret_t fscript_exec_func(fscript_t* fscript, fscript_func_call_t* iter, value_t* result) {
|
||||
ret_t fscript_exec_func_default(fscript_t* fscript, fscript_func_call_t* iter, value_t* result) {
|
||||
fscript->curr = iter;
|
||||
result->type = VALUE_TYPE_INVALID;
|
||||
if (fscript_exec_core_func(fscript, iter, result) == RET_NOT_FOUND) {
|
||||
@ -617,12 +652,16 @@ ret_t fscript_exec(fscript_t* fscript, value_t* result) {
|
||||
fscript_func_call_t* iter = NULL;
|
||||
return_value_if_fail(fscript != NULL, RET_FAIL);
|
||||
|
||||
if (s_hooks != NULL && s_hooks->before_exec != NULL) {
|
||||
s_hooks->before_exec(fscript);
|
||||
}
|
||||
|
||||
value_set_str(result, NULL);
|
||||
iter = fscript->first;
|
||||
while (iter != NULL) {
|
||||
return_value_if_fail(iter->func != NULL, RET_FAIL);
|
||||
value_reset(result);
|
||||
return_value_if_fail(fscript_exec_func(fscript, iter, result) == RET_OK, RET_FAIL);
|
||||
return_value_if_fail(fscript_exec_func(fscript, NULL, iter, result) == RET_OK, RET_FAIL);
|
||||
if (fscript->returned) {
|
||||
fscript->returned = FALSE;
|
||||
break;
|
||||
@ -630,6 +669,10 @@ ret_t fscript_exec(fscript_t* fscript, value_t* result) {
|
||||
iter = iter->next;
|
||||
}
|
||||
|
||||
if (s_hooks != NULL && s_hooks->after_exec != NULL) {
|
||||
s_hooks->after_exec(fscript);
|
||||
}
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
@ -643,6 +686,10 @@ static ret_t on_free_func_def(void* ctx, const void* data) {
|
||||
ret_t fscript_destroy(fscript_t* fscript) {
|
||||
return_value_if_fail(fscript != NULL, RET_FAIL);
|
||||
|
||||
if (s_hooks != NULL && s_hooks->on_deinit != NULL) {
|
||||
s_hooks->on_deinit(fscript);
|
||||
}
|
||||
|
||||
str_reset(&(fscript->str));
|
||||
TK_OBJECT_UNREF(fscript->locals);
|
||||
if (fscript->funcs_def != NULL) {
|
||||
@ -1559,6 +1606,11 @@ fscript_t* fscript_create_impl(fscript_parser_t* parser) {
|
||||
fscript->first = parser->first;
|
||||
fscript->funcs_def = parser->funcs_def;
|
||||
fscript->code_id = parser->code_id;
|
||||
fscript->lines = parser->row + 1;
|
||||
|
||||
if (s_hooks != NULL && s_hooks->on_init != NULL) {
|
||||
s_hooks->on_init(fscript, parser->str);
|
||||
}
|
||||
|
||||
parser->obj = NULL;
|
||||
parser->first = NULL;
|
||||
@ -1588,7 +1640,7 @@ static ret_t func_function(fscript_t* fscript, fscript_args_t* args, value_t* re
|
||||
}
|
||||
}
|
||||
|
||||
ret = fscript_exec_func(fscript, func, result);
|
||||
ret = fscript_exec_func(fscript, func_def->name, func, result);
|
||||
|
||||
TK_OBJECT_UNREF(fscript->locals);
|
||||
fscript->locals = saved_locals;
|
||||
@ -1617,7 +1669,7 @@ static ret_t fscript_parse_function_def(fscript_parser_t* parser, fscript_func_c
|
||||
statements = fscript_func_call_create(parser, "func", 4);
|
||||
return_value_if_fail(statements != NULL, RET_OOM);
|
||||
|
||||
func_def = fscript_function_def_create(statements);
|
||||
func_def = fscript_function_def_create(func_name, statements);
|
||||
return_value_if_fail(func_def != NULL, RET_OOM);
|
||||
tk_object_set_prop_pointer(parser->funcs_def, func_name, func_def);
|
||||
|
||||
@ -2029,22 +2081,20 @@ static ret_t func_expr(fscript_t* fscript, fscript_args_t* args, value_t* result
|
||||
}
|
||||
|
||||
static ret_t func_print(fscript_t* fscript, fscript_args_t* args, value_t* result) {
|
||||
fscript_func_t func = NULL;
|
||||
func =
|
||||
(fscript_func_t)tk_object_get_prop_pointer(fscript->obj, STR_FSCRIPT_FUNCTION_PREFIX "print");
|
||||
uint32_t i = 0;
|
||||
char buff[64];
|
||||
|
||||
if (func != NULL) {
|
||||
return func(fscript, args, result);
|
||||
} else {
|
||||
uint32_t i = 0;
|
||||
char buff[64];
|
||||
value_set_bool(result, TRUE);
|
||||
for (i = 0; i < args->size; i++) {
|
||||
log_info("%s ", value_str_ex(args->args + i, buff, sizeof(buff) - 1));
|
||||
}
|
||||
log_info("\n");
|
||||
return RET_OK;
|
||||
value_set_bool(result, TRUE);
|
||||
for (i = 0; i < args->size; i++) {
|
||||
log_info("%s ", value_str_ex(args->args + i, buff, sizeof(buff) - 1));
|
||||
}
|
||||
log_info("\n");
|
||||
|
||||
if (fscript->print != NULL) {
|
||||
return fscript->print(fscript, args, result);
|
||||
}
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static ret_t func_iformat(fscript_t* fscript, fscript_args_t* args, value_t* result) {
|
||||
|
@ -135,7 +135,6 @@ struct _fscript_t {
|
||||
* 脚本执行上下文。
|
||||
*/
|
||||
tk_object_t* obj;
|
||||
|
||||
/**
|
||||
* @property {ret_t} error_code
|
||||
* @annotation ["readable"]
|
||||
@ -160,6 +159,12 @@ struct _fscript_t {
|
||||
* 运行时错误的列号。
|
||||
*/
|
||||
int32_t error_col;
|
||||
/**
|
||||
* @property {uint16_t} lines
|
||||
* @annotation ["readable"]
|
||||
* 代码总行数。
|
||||
*/
|
||||
uint16_t lines;
|
||||
|
||||
/*private*/
|
||||
char* code_id;
|
||||
@ -178,6 +183,7 @@ struct _fscript_t {
|
||||
|
||||
void* on_error_ctx;
|
||||
fscript_on_error_t on_error;
|
||||
fscript_func_t print;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -253,6 +259,16 @@ ret_t fscript_set_error(fscript_t* fscript, ret_t code, const char* func, const
|
||||
*/
|
||||
ret_t fscript_set_on_error(fscript_t* fscript, fscript_on_error_t on_error, void* ctx);
|
||||
|
||||
/**
|
||||
* @method fscript_set_print_func
|
||||
* 设置打印日志的函数。
|
||||
* @param {fscript_t*} fscript 脚本引擎对象。
|
||||
* @param {fscript_func_t} print_func 打印日志的函数。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t fscript_set_print_func(fscript_t* fscript, fscript_func_t print);
|
||||
|
||||
/**
|
||||
* @method fscript_destroy
|
||||
* 销毁引擎对象。
|
||||
@ -386,6 +402,28 @@ static inline const char* value_id(const value_t* v) {
|
||||
return (const char*)(v->value.str);
|
||||
}
|
||||
|
||||
typedef ret_t (*fscript_on_init_t)(fscript_t* fscript, const char* code);
|
||||
typedef ret_t (*fscript_on_deinit_t)(fscript_t* fscript);
|
||||
typedef ret_t (*fscript_before_exec_t)(fscript_t* fscript);
|
||||
typedef ret_t (*fscript_after_exec_t)(fscript_t* fscript);
|
||||
typedef ret_t (*fscript_set_var_t)(fscript_t* fscript, const char* name, const value_t* v);
|
||||
typedef ret_t (*fscript_exec_func_t)(fscript_t* fscript, const char* name,
|
||||
fscript_func_call_t* iter, value_t* result);
|
||||
|
||||
ret_t fscript_set_var_default(fscript_t* fscript, const char* name, const value_t* value);
|
||||
ret_t fscript_exec_func_default(fscript_t* fscript, fscript_func_call_t* iter, value_t* result);
|
||||
|
||||
typedef struct _fscript_hooks_t {
|
||||
fscript_on_init_t on_init;
|
||||
fscript_on_deinit_t on_deinit;
|
||||
fscript_set_var_t set_var;
|
||||
fscript_exec_func_t exec_func;
|
||||
fscript_before_exec_t before_exec;
|
||||
fscript_after_exec_t after_exec;
|
||||
} fscript_hooks_t;
|
||||
|
||||
ret_t fscript_set_hooks(const fscript_hooks_t* hooks);
|
||||
|
||||
END_C_DECLS
|
||||
|
||||
#endif /*TK_FSCRIPTS_H*/
|
||||
|
@ -195,11 +195,11 @@ void awtk_ios_log(const char* message, ...);
|
||||
#define log_debug(format, args...) \
|
||||
if (log_get_log_level() <= LOG_LEVEL_DEBUG) printf(format, ##args); \
|
||||
fflush(stdout)
|
||||
#define log_info(format, args...) \
|
||||
if (log_get_log_level() <= LOG_LEVEL_INFO) printf(format, ##args); \
|
||||
#define log_info(format, args...) \
|
||||
if (log_get_log_level() <= LOG_LEVEL_INFO) printf(format, ##args); \
|
||||
fflush(stdout)
|
||||
#define log_warn(format, args...) \
|
||||
if (log_get_log_level() <= LOG_LEVEL_WARN) printf(format, ##args); \
|
||||
#define log_warn(format, args...) \
|
||||
if (log_get_log_level() <= LOG_LEVEL_WARN) printf(format, ##args); \
|
||||
fflush(stdout)
|
||||
#define log_error(format, args...) \
|
||||
if (log_get_log_level() <= LOG_LEVEL_ERROR) printf(format, ##args); \
|
||||
|
@ -5,7 +5,7 @@ cp -fv ../tkc/tools/idl_gen/* tools/idl_gen/.
|
||||
rm -f tools/dll_def_gen/package-lock.json
|
||||
cp -rvf ../tkc/awtk_config_common.py .
|
||||
|
||||
for f in tkc.h compressors fscript_ext platforms conf_io hal streams ubjson charset csv misc tkc xml
|
||||
for f in tkc.h compressors fscript_ext platforms conf_io hal streams ubjson charset csv misc tkc xml debugger
|
||||
do
|
||||
cp -rvf ../tkc/src/$f src
|
||||
done
|
||||
|
@ -191,24 +191,41 @@ TEST(DateTime, fscript_object) {
|
||||
value_t v1;
|
||||
tk_object_t* obj = object_default_create();
|
||||
|
||||
fscript_eval(obj, "a=date_time_create();date_time_set_prop(a,\"year\",2050);b=date_time_get_prop(a,\"year\");b", &v1);
|
||||
fscript_eval(
|
||||
obj,
|
||||
"a=date_time_create();date_time_set_prop(a,\"year\",2050);b=date_time_get_prop(a,\"year\");b",
|
||||
&v1);
|
||||
ASSERT_EQ(value_int(&v1), 2050);
|
||||
|
||||
fscript_eval(obj, "a=date_time_create();date_time_set_prop(a,\"month\",10);b=date_time_get_prop(a,\"month\");b", &v1);
|
||||
fscript_eval(
|
||||
obj,
|
||||
"a=date_time_create();date_time_set_prop(a,\"month\",10);b=date_time_get_prop(a,\"month\");b",
|
||||
&v1);
|
||||
ASSERT_EQ(value_int(&v1), 10);
|
||||
|
||||
fscript_eval(obj, "a=date_time_create();date_time_set_prop(a,\"day\",11);b=date_time_get_prop(a,\"day\");b", &v1);
|
||||
fscript_eval(
|
||||
obj,
|
||||
"a=date_time_create();date_time_set_prop(a,\"day\",11);b=date_time_get_prop(a,\"day\");b",
|
||||
&v1);
|
||||
ASSERT_EQ(value_int(&v1), 11);
|
||||
|
||||
fscript_eval(obj, "a=date_time_create();date_time_set_prop(a,\"hour\",8);b=date_time_get_prop(a,\"hour\");b", &v1);
|
||||
ASSERT_EQ(value_int(&v1), 8);
|
||||
|
||||
fscript_eval(obj, "a=date_time_create();date_time_set_prop(a,\"minute\",9);b=date_time_get_prop(a,\"minute\");b", &v1);
|
||||
ASSERT_EQ(value_int(&v1), 9);
|
||||
|
||||
fscript_eval(obj, "a=date_time_create();date_time_set_prop(a,\"second\",5);b=date_time_get_prop(a,\"second\");b", &v1);
|
||||
ASSERT_EQ(value_int(&v1), 5);
|
||||
|
||||
fscript_eval(
|
||||
obj,
|
||||
"a=date_time_create();date_time_set_prop(a,\"hour\",8);b=date_time_get_prop(a,\"hour\");b",
|
||||
&v1);
|
||||
ASSERT_EQ(value_int(&v1), 8);
|
||||
|
||||
fscript_eval(obj,
|
||||
"a=date_time_create();date_time_set_prop(a,\"minute\",9);b=date_time_get_prop(a,"
|
||||
"\"minute\");b",
|
||||
&v1);
|
||||
ASSERT_EQ(value_int(&v1), 9);
|
||||
|
||||
fscript_eval(obj,
|
||||
"a=date_time_create();date_time_set_prop(a,\"second\",5);b=date_time_get_prop(a,"
|
||||
"\"second\");b",
|
||||
&v1);
|
||||
ASSERT_EQ(value_int(&v1), 5);
|
||||
|
||||
TK_OBJECT_UNREF(obj);
|
||||
}
|
||||
|
432
tests/debugger_test.cc
Normal file
432
tests/debugger_test.cc
Normal file
@ -0,0 +1,432 @@
|
||||
#include "debugger/debugger_fscript.h"
|
||||
#include "debugger/debugger_global.h"
|
||||
#include "debugger/debugger_server.h"
|
||||
#include "debugger/debugger_message.h"
|
||||
#include "debugger/debugger_server_tcp.h"
|
||||
#include "debugger/debugger_client_tcp.h"
|
||||
|
||||
#include "tkc/fscript.h"
|
||||
#include "tkc/thread.h"
|
||||
#include "tkc/object_default.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#define DEBUGGER_TCP_PORT 8080
|
||||
|
||||
static void* fscript_thread_entry(void* args) {
|
||||
value_t v;
|
||||
fscript_t* fscript = (fscript_t*)args;
|
||||
fscript_exec(fscript, &v);
|
||||
fscript_destroy(fscript);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ret_t on_debugger_client_event(void* ctx, event_t* e) {
|
||||
str_t* str = (str_t*)ctx;
|
||||
switch (e->type) {
|
||||
case DEBUGGER_RESP_MSG_BREAKED: {
|
||||
debugger_breaked_event_t* event = debugger_breaked_event_cast(e);
|
||||
str_append(str, "breaked");
|
||||
str_append(str, "(");
|
||||
str_append_int(str, event->line);
|
||||
str_append(str, ")");
|
||||
break;
|
||||
}
|
||||
case DEBUGGER_RESP_MSG_LOG: {
|
||||
debugger_log_event_t* event = debugger_log_event_cast(e);
|
||||
str_append(str, "log");
|
||||
str_append(str, "(");
|
||||
str_append_int(str, event->line);
|
||||
str_append(str, ",\"");
|
||||
str_append(str, event->message);
|
||||
str_append(str, "\")");
|
||||
break;
|
||||
}
|
||||
case DEBUGGER_RESP_MSG_ERROR: {
|
||||
debugger_error_event_t* event = debugger_error_event_cast(e);
|
||||
str_append(str, "error");
|
||||
str_append(str, "(");
|
||||
str_append_int(str, event->line);
|
||||
str_append(str, ",\"");
|
||||
str_append(str, event->message);
|
||||
str_append(str, "\")");
|
||||
break;
|
||||
}
|
||||
case DEBUGGER_RESP_MSG_COMPLETED: {
|
||||
str_append(str, "completed()");
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
TEST(Debugger, event1) {
|
||||
const char* code =
|
||||
"print(1)\nprint(2)\nprint(3)\n//"
|
||||
"code_id(\"85e86311e2d595c65b745d8143b6085efe819c354584742f72aeacd3336a0a5e\")";
|
||||
str_t str;
|
||||
str_init(&str, 100);
|
||||
debugger_global_init();
|
||||
|
||||
tk_object_t* obj = object_default_create();
|
||||
fscript_t* fscript = fscript_create(obj, code);
|
||||
tk_thread_t* thread = tk_thread_create(fscript_thread_entry, fscript);
|
||||
|
||||
debugger_server_tcp_init(DEBUGGER_TCP_PORT);
|
||||
debugger_t* client = debugger_client_tcp_create("localhost", DEBUGGER_TCP_PORT);
|
||||
ASSERT_EQ(client != NULL, TRUE);
|
||||
emitter_on(EMITTER(client), DEBUGGER_RESP_MSG_BREAKED, on_debugger_client_event, &str);
|
||||
emitter_on(EMITTER(client), DEBUGGER_RESP_MSG_LOG, on_debugger_client_event, &str);
|
||||
emitter_on(EMITTER(client), DEBUGGER_RESP_MSG_ERROR, on_debugger_client_event, &str);
|
||||
emitter_on(EMITTER(client), DEBUGGER_RESP_MSG_COMPLETED, on_debugger_client_event, &str);
|
||||
|
||||
debugger_server_tcp_start();
|
||||
ASSERT_EQ(debugger_init(client, DEBUGGER_LANG_FSCRIPT, fscript->code_id), RET_OK);
|
||||
ASSERT_EQ(debugger_set_break_point(client, 0), RET_OK);
|
||||
|
||||
tk_thread_start(thread);
|
||||
sleep_ms(500);
|
||||
|
||||
debugger_t* debugger = debugger_server_find_debugger(fscript->code_id);
|
||||
ASSERT_EQ(debugger_is_paused(debugger), TRUE);
|
||||
ASSERT_EQ(debugger_is_paused(client), TRUE);
|
||||
|
||||
ASSERT_EQ(debugger_continue(client), RET_OK);
|
||||
ASSERT_EQ(debugger_clear_break_points(client), RET_OK);
|
||||
|
||||
debugger_client_wait_for_completed(client);
|
||||
tk_thread_join(thread);
|
||||
tk_thread_destroy(thread);
|
||||
|
||||
TK_OBJECT_UNREF(obj);
|
||||
TK_OBJECT_UNREF(client);
|
||||
debugger_server_tcp_deinit();
|
||||
debugger_global_deinit();
|
||||
ASSERT_STREQ(str.str, "breaked(0)log(0,\"1\")log(1,\"2\")log(2,\"3\")completed()");
|
||||
str_reset(&str);
|
||||
}
|
||||
|
||||
#if 1
|
||||
TEST(Debugger, event2) {
|
||||
const char* code =
|
||||
"print(1)\nprint(2)\nprint(3)\n//"
|
||||
"code_id(\"85e86311e2d595c65b745d8143b6085efe819c354584742f72aeacd3336a0a5e\")";
|
||||
str_t str;
|
||||
str_init(&str, 100);
|
||||
debugger_global_init();
|
||||
|
||||
tk_object_t* obj = object_default_create();
|
||||
fscript_t* fscript = fscript_create(obj, code);
|
||||
tk_thread_t* thread = tk_thread_create(fscript_thread_entry, fscript);
|
||||
|
||||
debugger_server_tcp_init(DEBUGGER_TCP_PORT);
|
||||
debugger_t* client = debugger_client_tcp_create("localhost", DEBUGGER_TCP_PORT);
|
||||
ASSERT_EQ(client != NULL, TRUE);
|
||||
emitter_on(EMITTER(client), DEBUGGER_RESP_MSG_BREAKED, on_debugger_client_event, &str);
|
||||
emitter_on(EMITTER(client), DEBUGGER_RESP_MSG_LOG, on_debugger_client_event, &str);
|
||||
emitter_on(EMITTER(client), DEBUGGER_RESP_MSG_ERROR, on_debugger_client_event, &str);
|
||||
emitter_on(EMITTER(client), DEBUGGER_RESP_MSG_COMPLETED, on_debugger_client_event, &str);
|
||||
|
||||
debugger_server_tcp_start();
|
||||
ASSERT_EQ(debugger_init(client, DEBUGGER_LANG_FSCRIPT, fscript->code_id), RET_OK);
|
||||
ASSERT_EQ(debugger_set_break_point(client, 2), RET_OK);
|
||||
|
||||
tk_thread_start(thread);
|
||||
sleep_ms(500);
|
||||
|
||||
debugger_t* debugger = debugger_server_find_debugger(fscript->code_id);
|
||||
ASSERT_EQ(debugger_is_paused(debugger), TRUE);
|
||||
|
||||
ASSERT_EQ(debugger_continue(client), RET_OK);
|
||||
ASSERT_EQ(debugger_clear_break_points(client), RET_OK);
|
||||
|
||||
debugger_client_wait_for_completed(client);
|
||||
tk_thread_join(thread);
|
||||
tk_thread_destroy(thread);
|
||||
|
||||
TK_OBJECT_UNREF(obj);
|
||||
TK_OBJECT_UNREF(client);
|
||||
debugger_server_tcp_deinit();
|
||||
debugger_global_deinit();
|
||||
|
||||
ASSERT_STREQ(str.str, "log(0,\"1\")log(1,\"2\")breaked(2)log(2,\"3\")completed()");
|
||||
str_reset(&str);
|
||||
}
|
||||
|
||||
TEST(Debugger, basic) {
|
||||
const char* code =
|
||||
"print(1)\nprint(2)\nprint(3)\n//"
|
||||
"code_id(\"85e86311e2d595c65b745d8143b6085efe819c354584742f72aeacd3336a0a5e\")";
|
||||
|
||||
debugger_global_init();
|
||||
|
||||
tk_object_t* obj = object_default_create();
|
||||
fscript_t* fscript = fscript_create(obj, code);
|
||||
tk_thread_t* thread = tk_thread_create(fscript_thread_entry, fscript);
|
||||
|
||||
debugger_server_tcp_init(DEBUGGER_TCP_PORT);
|
||||
debugger_t* client = debugger_client_tcp_create("localhost", DEBUGGER_TCP_PORT);
|
||||
ASSERT_EQ(client != NULL, TRUE);
|
||||
|
||||
debugger_server_tcp_start();
|
||||
ASSERT_EQ(debugger_init(client, DEBUGGER_LANG_FSCRIPT, fscript->code_id), RET_OK);
|
||||
ASSERT_EQ(debugger_set_break_point(client, 0), RET_OK);
|
||||
|
||||
tk_thread_start(thread);
|
||||
sleep_ms(500);
|
||||
|
||||
debugger_t* debugger = debugger_server_find_debugger(fscript->code_id);
|
||||
ASSERT_EQ(debugger_is_paused(debugger), TRUE);
|
||||
|
||||
ASSERT_EQ(debugger_continue(client), RET_OK);
|
||||
ASSERT_EQ(debugger_clear_break_points(client), RET_OK);
|
||||
tk_thread_join(thread);
|
||||
tk_thread_destroy(thread);
|
||||
|
||||
TK_OBJECT_UNREF(obj);
|
||||
TK_OBJECT_UNREF(client);
|
||||
debugger_server_tcp_deinit();
|
||||
debugger_global_deinit();
|
||||
}
|
||||
|
||||
TEST(Debugger, basic1) {
|
||||
const char* code =
|
||||
"print(1)\nprint(2)\nprint(3)\n//"
|
||||
"code_id(\"85e86311e2d595c65b745d8143b6085efe819c354584742f72aeacd3336a0a5e\")";
|
||||
|
||||
debugger_global_init();
|
||||
|
||||
tk_object_t* obj = object_default_create();
|
||||
fscript_t* fscript = fscript_create(obj, code);
|
||||
tk_thread_t* thread = tk_thread_create(fscript_thread_entry, fscript);
|
||||
|
||||
debugger_server_tcp_init(DEBUGGER_TCP_PORT);
|
||||
debugger_t* client = debugger_client_tcp_create("localhost", DEBUGGER_TCP_PORT);
|
||||
ASSERT_EQ(client != NULL, TRUE);
|
||||
|
||||
debugger_server_tcp_start();
|
||||
ASSERT_EQ(debugger_init(client, DEBUGGER_LANG_FSCRIPT, fscript->code_id), RET_OK);
|
||||
ASSERT_EQ(debugger_set_break_point(client, 0), RET_OK);
|
||||
ASSERT_EQ(debugger_set_break_point(client, 1), RET_OK);
|
||||
ASSERT_EQ(debugger_set_break_point(client, 2), RET_OK);
|
||||
|
||||
tk_thread_start(thread);
|
||||
sleep_ms(500);
|
||||
|
||||
ASSERT_EQ(debugger_next(client), RET_OK);
|
||||
ASSERT_EQ(debugger_next(client), RET_OK);
|
||||
ASSERT_EQ(debugger_remove_break_point(client, 2), RET_OK);
|
||||
ASSERT_EQ(debugger_clear_break_points(client), RET_OK);
|
||||
|
||||
tk_thread_join(thread);
|
||||
tk_thread_destroy(thread);
|
||||
|
||||
TK_OBJECT_UNREF(obj);
|
||||
TK_OBJECT_UNREF(client);
|
||||
debugger_server_tcp_deinit();
|
||||
debugger_global_deinit();
|
||||
}
|
||||
|
||||
TEST(Debugger, local) {
|
||||
const char* code =
|
||||
"var aaa=111\nvar bbb =222;\nvar "
|
||||
"ccc=\"abc\"\nprint(a);\n//"
|
||||
"code_id(\"85e86311e2d595c65b745d8143b6085efe819c354584742f72aeacd3336a0a5e\")";
|
||||
|
||||
debugger_global_init();
|
||||
|
||||
tk_object_t* obj = object_default_create();
|
||||
fscript_t* fscript = fscript_create(obj, code);
|
||||
tk_thread_t* thread = tk_thread_create(fscript_thread_entry, fscript);
|
||||
|
||||
debugger_server_tcp_init(DEBUGGER_TCP_PORT);
|
||||
debugger_t* client = debugger_client_tcp_create("localhost", DEBUGGER_TCP_PORT);
|
||||
ASSERT_EQ(client != NULL, TRUE);
|
||||
|
||||
debugger_server_tcp_start();
|
||||
ASSERT_EQ(debugger_init(client, DEBUGGER_LANG_FSCRIPT, fscript->code_id), RET_OK);
|
||||
ASSERT_EQ(debugger_set_break_point(client, 3), RET_OK);
|
||||
|
||||
tk_thread_start(thread);
|
||||
sleep_ms(500);
|
||||
|
||||
tk_object_t* local = debugger_get_local(client, 0);
|
||||
ASSERT_EQ(tk_object_get_prop_int(local, "aaa", 0), 111);
|
||||
ASSERT_EQ(tk_object_get_prop_int(local, "bbb", 0), 222);
|
||||
ASSERT_STREQ(tk_object_get_prop_str(local, "ccc"), "abc");
|
||||
|
||||
TK_OBJECT_UNREF(local);
|
||||
ASSERT_EQ(debugger_clear_break_points(client), RET_OK);
|
||||
|
||||
tk_thread_join(thread);
|
||||
tk_thread_destroy(thread);
|
||||
|
||||
TK_OBJECT_UNREF(obj);
|
||||
TK_OBJECT_UNREF(client);
|
||||
debugger_server_tcp_deinit();
|
||||
debugger_global_deinit();
|
||||
}
|
||||
|
||||
TEST(Debugger, self) {
|
||||
const char* code =
|
||||
"aaa=111\nbbb "
|
||||
"=222;\nccc=\"abc\"\nprint(a);\n//"
|
||||
"code_id(\"85e86311e2d595c65b745d8143b6085efe819c354584742f72aeacd3336a0a5e\")";
|
||||
|
||||
debugger_global_init();
|
||||
|
||||
tk_object_t* obj = object_default_create();
|
||||
fscript_t* fscript = fscript_create(obj, code);
|
||||
tk_thread_t* thread = tk_thread_create(fscript_thread_entry, fscript);
|
||||
|
||||
debugger_server_tcp_init(DEBUGGER_TCP_PORT);
|
||||
debugger_t* client = debugger_client_tcp_create("localhost", DEBUGGER_TCP_PORT);
|
||||
ASSERT_EQ(client != NULL, TRUE);
|
||||
|
||||
debugger_server_tcp_start();
|
||||
ASSERT_EQ(debugger_init(client, DEBUGGER_LANG_FSCRIPT, fscript->code_id), RET_OK);
|
||||
ASSERT_EQ(debugger_set_break_point(client, 3), RET_OK);
|
||||
|
||||
tk_thread_start(thread);
|
||||
sleep_ms(500);
|
||||
|
||||
tk_object_t* self = debugger_get_self(client);
|
||||
ASSERT_EQ(tk_object_get_prop_int(self, "aaa", 0), 111);
|
||||
ASSERT_EQ(tk_object_get_prop_int(self, "bbb", 0), 222);
|
||||
ASSERT_STREQ(tk_object_get_prop_str(self, "ccc"), "abc");
|
||||
TK_OBJECT_UNREF(self);
|
||||
|
||||
ASSERT_EQ(debugger_clear_break_points(client), RET_OK);
|
||||
|
||||
tk_thread_join(thread);
|
||||
tk_thread_destroy(thread);
|
||||
|
||||
TK_OBJECT_UNREF(obj);
|
||||
TK_OBJECT_UNREF(client);
|
||||
debugger_server_tcp_deinit();
|
||||
debugger_global_deinit();
|
||||
}
|
||||
|
||||
TEST(Debugger, global) {
|
||||
const char* code =
|
||||
"global.aaa=111\nglobal.bbb "
|
||||
"=222;\nglobal.ccc=\"abc\"\nprint(a);\n//"
|
||||
"code_id(\"85e86311e2d595c65b745d8143b6085efe819c354584742f72aeacd3336a0a5e\")";
|
||||
|
||||
debugger_global_init();
|
||||
|
||||
tk_object_t* obj = object_default_create();
|
||||
fscript_t* fscript = fscript_create(obj, code);
|
||||
tk_thread_t* thread = tk_thread_create(fscript_thread_entry, fscript);
|
||||
|
||||
debugger_server_tcp_init(DEBUGGER_TCP_PORT);
|
||||
debugger_t* client = debugger_client_tcp_create("localhost", DEBUGGER_TCP_PORT);
|
||||
ASSERT_EQ(client != NULL, TRUE);
|
||||
|
||||
debugger_server_tcp_start();
|
||||
ASSERT_EQ(debugger_init(client, DEBUGGER_LANG_FSCRIPT, fscript->code_id), RET_OK);
|
||||
ASSERT_EQ(debugger_set_break_point(client, 3), RET_OK);
|
||||
|
||||
tk_thread_start(thread);
|
||||
sleep_ms(500);
|
||||
|
||||
tk_object_t* global = debugger_get_global(client);
|
||||
ASSERT_EQ(tk_object_get_prop_int(global, "aaa", 0), 111);
|
||||
ASSERT_EQ(tk_object_get_prop_int(global, "bbb", 0), 222);
|
||||
ASSERT_STREQ(tk_object_get_prop_str(global, "ccc"), "abc");
|
||||
TK_OBJECT_UNREF(global);
|
||||
|
||||
ASSERT_EQ(debugger_clear_break_points(client), RET_OK);
|
||||
|
||||
tk_thread_join(thread);
|
||||
tk_thread_destroy(thread);
|
||||
|
||||
TK_OBJECT_UNREF(obj);
|
||||
TK_OBJECT_UNREF(client);
|
||||
debugger_server_tcp_deinit();
|
||||
debugger_global_deinit();
|
||||
}
|
||||
|
||||
TEST(Debugger, callstack) {
|
||||
const char* code =
|
||||
"global.aaa=111\nglobal.bbb "
|
||||
"=222;\nglobal.ccc=\"abc\"\nprint(a);\n//"
|
||||
"code_id(\"85e86311e2d595c65b745d8143b6085efe819c354584742f72aeacd3336a0a5e\")";
|
||||
|
||||
debugger_global_init();
|
||||
|
||||
tk_object_t* obj = object_default_create();
|
||||
fscript_t* fscript = fscript_create(obj, code);
|
||||
tk_thread_t* thread = tk_thread_create(fscript_thread_entry, fscript);
|
||||
|
||||
debugger_server_tcp_init(DEBUGGER_TCP_PORT);
|
||||
debugger_t* client = debugger_client_tcp_create("localhost", DEBUGGER_TCP_PORT);
|
||||
ASSERT_EQ(client != NULL, TRUE);
|
||||
|
||||
debugger_server_tcp_start();
|
||||
ASSERT_EQ(debugger_init(client, DEBUGGER_LANG_FSCRIPT, fscript->code_id), RET_OK);
|
||||
ASSERT_EQ(debugger_set_break_point(client, 3), RET_OK);
|
||||
|
||||
tk_thread_start(thread);
|
||||
sleep_ms(500);
|
||||
|
||||
binary_data_t data = {0, NULL};
|
||||
ASSERT_EQ(debugger_get_callstack(client, &data), RET_OK);
|
||||
ASSERT_STREQ((char*)(data.data), "<root>\n");
|
||||
|
||||
ASSERT_EQ(debugger_clear_break_points(client), RET_OK);
|
||||
|
||||
tk_thread_join(thread);
|
||||
tk_thread_destroy(thread);
|
||||
|
||||
TK_OBJECT_UNREF(obj);
|
||||
TK_OBJECT_UNREF(client);
|
||||
debugger_server_tcp_deinit();
|
||||
debugger_global_deinit();
|
||||
}
|
||||
|
||||
TEST(Debugger, code) {
|
||||
const char* code =
|
||||
"global.aaa=111\nglobal.bbb "
|
||||
"=222;\nglobal.ccc=\"abc\"\nprint(a);\n//"
|
||||
"code_id(\"85e86311e2d595c65b745d8143b6085efe819c354584742f72aeacd3336a0a5e\")";
|
||||
|
||||
debugger_global_init();
|
||||
|
||||
tk_object_t* obj = object_default_create();
|
||||
fscript_t* fscript = fscript_create(obj, code);
|
||||
tk_thread_t* thread = tk_thread_create(fscript_thread_entry, fscript);
|
||||
|
||||
debugger_server_tcp_init(DEBUGGER_TCP_PORT);
|
||||
debugger_t* client = debugger_client_tcp_create("localhost", DEBUGGER_TCP_PORT);
|
||||
ASSERT_EQ(client != NULL, TRUE);
|
||||
|
||||
debugger_server_tcp_start();
|
||||
ASSERT_EQ(debugger_init(client, DEBUGGER_LANG_FSCRIPT, fscript->code_id), RET_OK);
|
||||
ASSERT_EQ(debugger_set_break_point(client, 3), RET_OK);
|
||||
|
||||
tk_thread_start(thread);
|
||||
sleep_ms(500);
|
||||
|
||||
binary_data_t data;
|
||||
data.data = (void*)"print(1)";
|
||||
data.size = strlen((char*)data.data);
|
||||
ASSERT_EQ(debugger_update_code(client, &data), RET_OK);
|
||||
|
||||
data.data = NULL;
|
||||
ASSERT_EQ(debugger_get_code(client, &data), RET_OK);
|
||||
ASSERT_STREQ((char*)data.data, "print(1)");
|
||||
|
||||
ASSERT_EQ(debugger_clear_break_points(client), RET_OK);
|
||||
|
||||
tk_thread_join(thread);
|
||||
tk_thread_destroy(thread);
|
||||
|
||||
TK_OBJECT_UNREF(obj);
|
||||
TK_OBJECT_UNREF(client);
|
||||
debugger_server_tcp_deinit();
|
||||
debugger_global_deinit();
|
||||
}
|
||||
#endif
|
@ -9,15 +9,14 @@ TEST(FScriptArray, size) {
|
||||
fscript_eval(obj, "a=array_create();array_push(a, 1);array_push(a, 2);array_size(a)", &v);
|
||||
ASSERT_EQ(value_uint32(&v), 2);
|
||||
value_reset(&v);
|
||||
|
||||
|
||||
fscript_eval(obj, "a=array_create();array_is_empty(a)", &v);
|
||||
ASSERT_EQ(value_bool(&v), TRUE);
|
||||
value_reset(&v);
|
||||
|
||||
|
||||
fscript_eval(obj, "a=array_create();array_push(a, 1);array_push(a, 2);array_is_empty(a)", &v);
|
||||
ASSERT_EQ(value_bool(&v), FALSE);
|
||||
value_reset(&v);
|
||||
|
||||
TK_OBJECT_UNREF(obj);
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@ TEST(FScript, args) {
|
||||
fscript_eval(obj, "sum(1,2,3,4,5,6,7,8,9,10,11)", &v);
|
||||
ASSERT_EQ(66, value_int(&v));
|
||||
value_reset(&v);
|
||||
TK_OBJECT_UNREF(obj);
|
||||
}
|
||||
|
||||
TEST(FScript, basic0) {
|
||||
@ -1728,6 +1729,7 @@ TEST(FScript, convert1) {
|
||||
ASSERT_EQ(v.type == VALUE_TYPE_DOUBLE, true);
|
||||
ASSERT_EQ(123, value_double(&v));
|
||||
|
||||
TK_OBJECT_UNREF(obj);
|
||||
value_reset(&v);
|
||||
}
|
||||
|
||||
@ -1752,10 +1754,12 @@ TEST(FScript, print) {
|
||||
str_init(&str, 10);
|
||||
|
||||
tk_object_set_prop_pointer(obj, "str", &str);
|
||||
tk_object_set_prop_pointer(obj, STR_FSCRIPT_FUNCTION_PREFIX "print", (void*)my_print);
|
||||
fscript_eval(obj, "print(\"hello\")", &v);
|
||||
fscript_t* fscript = fscript_create(obj, "print(\"hello\")");
|
||||
fscript_set_print_func(fscript, my_print);
|
||||
fscript_exec(fscript, &v);
|
||||
ASSERT_STREQ(str.str, "hello");
|
||||
str_reset(&str);
|
||||
fscript_destroy(fscript);
|
||||
|
||||
value_reset(&v);
|
||||
TK_OBJECT_UNREF(obj);
|
||||
@ -1783,6 +1787,7 @@ TEST(FScript, on_error) {
|
||||
|
||||
value_reset(&v);
|
||||
TK_OBJECT_UNREF(obj);
|
||||
fscript_destroy(fscript);
|
||||
}
|
||||
|
||||
TEST(FScript, while_return) {
|
||||
@ -1940,6 +1945,8 @@ TEST(FExr, str_append) {
|
||||
|
||||
fscript_eval(obj, "aa=\"hello \";bb=str_append(aa, \"world\");bb", &v1);
|
||||
ASSERT_STREQ(value_str(&v1), "hello world");
|
||||
value_reset(&v1);
|
||||
|
||||
TK_OBJECT_UNREF(obj);
|
||||
}
|
||||
|
||||
@ -1975,21 +1982,31 @@ TEST(FExr, char_at) {
|
||||
|
||||
fscript_eval(obj, "char_at(\"hello\", 0)", &v1);
|
||||
ASSERT_STREQ(value_str(&v1), "h");
|
||||
value_reset(&v1);
|
||||
|
||||
fscript_eval(obj, "char_at(\"hello\", 1)", &v1);
|
||||
ASSERT_STREQ(value_str(&v1), "e");
|
||||
value_reset(&v1);
|
||||
|
||||
fscript_eval(obj, "char_at(\"hello\", -1)", &v1);
|
||||
ASSERT_STREQ(value_str(&v1), "o");
|
||||
value_reset(&v1);
|
||||
|
||||
fscript_eval(obj, "char_at(\"hello\", -2)", &v1);
|
||||
ASSERT_STREQ(value_str(&v1), "l");
|
||||
value_reset(&v1);
|
||||
|
||||
fscript_eval(obj, "char_at_first(\"hello\")", &v1);
|
||||
ASSERT_STREQ(value_str(&v1), "h");
|
||||
value_reset(&v1);
|
||||
|
||||
fscript_eval(obj, "char_at_last(\"hello\")", &v1);
|
||||
ASSERT_STREQ(value_str(&v1), "o");
|
||||
value_reset(&v1);
|
||||
|
||||
fscript_eval(obj, "char_at_random(\"hhh\")", &v1);
|
||||
ASSERT_STREQ(value_str(&v1), "h");
|
||||
value_reset(&v1);
|
||||
|
||||
TK_OBJECT_UNREF(obj);
|
||||
}
|
||||
|
@ -730,6 +730,7 @@ TEST(ObjectArray, reverse) {
|
||||
ASSERT_EQ(object_array_join(obj, ",", &s), RET_OK);
|
||||
ASSERT_STREQ(s.str, "1,0");
|
||||
str_clear(&s);
|
||||
TK_OBJECT_UNREF(obj);
|
||||
|
||||
obj = object_array_create_with_str("0,1,2", ",", VALUE_TYPE_INT32);
|
||||
ASSERT_EQ(object_array_reverse(obj), RET_OK);
|
||||
|
Loading…
x
Reference in New Issue
Block a user