| /******************************************************************************* |
| * Copyright (c) 2007, 2016 Wind River Systems, Inc. and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * and Eclipse Distribution License v1.0 which accompany this distribution. |
| * The Eclipse Public License is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * and the Eclipse Distribution License is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * You may elect to redistribute this code under either of these licenses. |
| * |
| * Contributors: |
| * Wind River Systems - initial API and implementation |
| *******************************************************************************/ |
| |
| /* |
| * Expression evaluation service. |
| * |
| * Extensions to regular C/C++ syntax: |
| * 1. Special characters in identifiers: $"X", or just "X" if followed by :: |
| * where X is object name that can contain any characters. |
| * 2. Symbol IDs in expressions: ${X} |
| * where X is symbol ID as returned by symbols service. |
| * 3. CPU registers: $X |
| * where X is a register name, e.g. $ax |
| */ |
| |
| #include <tcf/config.h> |
| |
| #if !defined(ENABLE_Expressions) |
| # define ENABLE_Expressions (SERVICE_Expressions) |
| #endif |
| |
| #if ENABLE_Expressions |
| |
| #include <stdlib.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <assert.h> |
| #include <string.h> |
| #include <tcf/framework/myalloc.h> |
| #include <tcf/framework/exceptions.h> |
| #include <tcf/framework/json.h> |
| #include <tcf/framework/cache.h> |
| #include <tcf/framework/trace.h> |
| #include <tcf/framework/context.h> |
| #include <tcf/services/runctrl.h> |
| #include <tcf/services/symbols.h> |
| #include <tcf/services/funccall.h> |
| #include <tcf/services/stacktrace.h> |
| #include <tcf/services/memoryservice.h> |
| #include <tcf/services/breakpoints.h> |
| #include <tcf/services/registers.h> |
| #include <tcf/services/expressions.h> |
| #include <tcf/main/test.h> |
| |
| #define SY_LEQ 256 |
| #define SY_GEQ 257 |
| #define SY_EQU 258 |
| #define SY_NEQ 259 |
| #define SY_AND 260 |
| #define SY_OR 261 |
| #define SY_SHL 262 |
| #define SY_SHR 263 |
| #define SY_VAL 264 |
| #define SY_ID 265 |
| #define SY_REF 266 |
| #define SY_DEC 267 |
| #define SY_INC 268 |
| #define SY_A_SUB 269 |
| #define SY_A_ADD 270 |
| #define SY_A_SHL 271 |
| #define SY_A_SHR 272 |
| #define SY_A_OR 273 |
| #define SY_A_XOR 274 |
| #define SY_A_AND 275 |
| #define SY_A_MUL 276 |
| #define SY_A_DIV 277 |
| #define SY_A_MOD 278 |
| #define SY_SIZEOF 279 |
| #define SY_NAME 280 |
| #define SY_SCOPE 281 |
| #define SY_PM_D 282 |
| #define SY_PM_R 283 |
| |
| #define MODE_NORMAL EXPRESSION_MODE_NORMAL |
| #define MODE_TYPE EXPRESSION_MODE_TYPE |
| #define MODE_SKIP EXPRESSION_MODE_SKIP |
| |
| #define VAL_FLAG_X 0x01 |
| #define VAL_FLAG_U 0x02 |
| #define VAL_FLAG_L 0x04 |
| #define VAL_FLAG_F 0x08 |
| #define VAL_FLAG_C 0x10 |
| #define VAL_FLAG_S 0x20 |
| |
| #define SYM_FLAG_TYPE 0xf0000000 |
| |
| static char * text = NULL; |
| static int text_pos = 0; |
| static int text_len = 0; |
| static int text_ch = 0; |
| static int text_sy = 0; |
| static int sy_pos = 0; |
| static Value text_val; |
| static int text_val_flags = 0; |
| |
| /* Host endianness */ |
| static int big_endian = 0; |
| |
| static Context * expression_context = NULL; |
| static int expression_frame = STACK_NO_FRAME; |
| static ContextAddress expression_addr = 0; |
| static int expression_has_func_call = 0; |
| |
| #define bit_mask(v, n) (1u << ((v)->big_endian ? 7 - (n) % 8 : (n) % 8)) |
| |
| #ifndef ENABLE_FuncCallInjection |
| # define ENABLE_FuncCallInjection (ENABLE_Symbols && SERVICE_RunControl && SERVICE_Breakpoints && ENABLE_DebugContext) |
| #endif |
| #ifndef ENABLE_ExpressionSerialization |
| # define ENABLE_ExpressionSerialization ENABLE_SymbolsProxy |
| #endif |
| |
| #if ENABLE_FuncCallInjection |
| typedef struct FuncCallState { |
| LINK link_all; |
| unsigned id; /* ACPM transaction ID */ |
| int pos; /* Text position in the expression */ |
| int started; /* Target has started to execute the function call */ |
| int intercepted; /* Intercepted during or after the function call */ |
| int committed; /* ACPM transaction finished */ |
| int finished; /* Target has finished the function call */ |
| Context * ctx; |
| uint64_t ret_addr; |
| ContextAddress stk_addr; |
| ContextAddress func_addr; |
| AbstractCache cache; |
| BreakpointInfo * bp; |
| ErrorReport * error; |
| |
| /* Actual arguments */ |
| Value * args; |
| unsigned args_cnt; |
| unsigned args_max; |
| |
| /* Returned value */ |
| void * ret_value; |
| size_t ret_size; |
| int ret_big_endian; |
| |
| /* After call commands */ |
| LocationExpressionCommand * cmds; |
| unsigned cmds_cnt; |
| |
| /* Saved registers */ |
| RegisterDefinition ** regs; |
| unsigned regs_cnt; |
| uint8_t * regs_data; |
| } FuncCallState; |
| |
| static LINK func_call_state = TCF_LIST_INIT(func_call_state); |
| |
| #define link_all2fc(A) ((FuncCallState *)((char *)(A) - offsetof(FuncCallState, link_all))) |
| |
| #endif /* ENABLE_FuncCallInjection */ |
| |
| static ExpressionIdentifierCallBack ** id_callbacks = NULL; |
| static int id_callback_max = 0; |
| static int id_callback_cnt = 0; |
| |
| static void ini_value(Value * v) { |
| memset(v, 0, sizeof(Value)); |
| v->ctx = expression_context; |
| } |
| |
| void set_value(Value * v, void * data, size_t size, int big_endian) { |
| v->sym = NULL; |
| v->reg = NULL; |
| v->loc = NULL; |
| v->remote = 0; |
| v->address = 0; |
| v->function = 0; |
| v->func_cb = NULL; |
| v->field_cb = NULL; |
| v->sym_list = NULL; |
| v->size = (ContextAddress)size; |
| v->big_endian = big_endian; |
| v->binary_scale = 0; |
| v->decimal_scale = 0; |
| v->bit_stride = 0; |
| v->value = tmp_alloc(size); |
| if (data == NULL) memset(v->value, 0, size); |
| else memcpy(v->value, data, size); |
| } |
| |
| static void set_int_value(Value * v, size_t size, uint64_t n) { |
| union { |
| uint8_t u8; |
| uint16_t u16; |
| uint32_t u32; |
| uint64_t u64; |
| } buf; |
| switch (size) { |
| case 1: buf.u8 = (uint8_t)n; break; |
| case 2: buf.u16 = (uint16_t)n; break; |
| case 4: buf.u32 = (uint32_t)n; break; |
| case 8: buf.u64 = n; break; |
| default: assert(0); |
| } |
| set_value(v, &buf, size, big_endian); |
| } |
| |
| static void set_fp_value(Value * v, size_t size, double n) { |
| union { |
| float f; |
| double d; |
| } buf; |
| switch (size) { |
| case 4: buf.f = (float)n; break; |
| case 8: buf.d = n; break; |
| default: assert(0); |
| } |
| set_value(v, &buf, size, big_endian); |
| } |
| |
| static void set_complex_value(Value * v, size_t size, double r, double i) { |
| float f; |
| uint8_t buf[16]; |
| switch (size) { |
| case 8: |
| assert(sizeof(f) == 4); |
| f = (float)r; |
| memcpy(buf, &f, 4); |
| f = (float)i; |
| memcpy(buf + 4, &f, 4); |
| set_value(v, buf, size, big_endian); |
| break; |
| case 16: |
| assert(sizeof(r) == 8); |
| memcpy(buf, &r, 8); |
| memcpy(buf + 8, &i, 8); |
| set_value(v, buf, size, big_endian); |
| break; |
| default: assert(0); |
| } |
| } |
| |
| static void set_ctx_word_value(Value * v, ContextAddress data) { |
| set_int_value(v, context_word_size(expression_context), data); |
| } |
| |
| static void set_string_value(Value * v, char * str) { |
| v->type_class = TYPE_CLASS_ARRAY; |
| if (str != NULL) set_value(v, str, strlen(str) + 1, 0); |
| } |
| |
| static void error(int no, const char * fmt, ...) { |
| va_list ap; |
| char buf[256]; |
| size_t l = 0; |
| |
| va_start(ap, fmt); |
| l = snprintf(buf, sizeof(buf), "At col %d: ", sy_pos); |
| vsnprintf(buf + l, sizeof(buf) - l, fmt, ap); |
| va_end(ap); |
| str_exception(no, buf); |
| } |
| |
| #define next_ch_fast() { \ |
| if (text_pos < text_len) text_ch = (unsigned char)text[text_pos++]; \ |
| } |
| |
| static void next_ch(void) { |
| next_ch_fast(); |
| } |
| |
| static int next_hex(void) { |
| int ch = text_ch; |
| next_ch_fast(); |
| if (ch >= '0' && ch <= '9') return ch - '0'; |
| if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10; |
| if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10; |
| error(ERR_INV_EXPRESSION, "Invalid hexadecimal number"); |
| return 0; |
| } |
| |
| static int next_oct(void) { |
| int ch = text_ch; |
| next_ch_fast(); |
| if (ch >= '0' && ch <= '7') return ch - '0'; |
| error(ERR_INV_EXPRESSION, "Invalid octal number"); |
| return 0; |
| } |
| |
| static int next_dec(void) { |
| int ch = text_ch; |
| next_ch_fast(); |
| if (ch >= '0' && ch <= '9') return ch - '0'; |
| error(ERR_INV_EXPRESSION, "Invalid decimal number"); |
| return 0; |
| } |
| |
| static int next_char_val(void) { |
| int n = 0; |
| if (text_ch == 0) { |
| error(ERR_INV_EXPRESSION, "Unexpected end of expression"); |
| } |
| else if (text_ch == '\\') { |
| next_ch(); |
| switch (text_ch) { |
| case 'n' : n = '\n'; break; |
| case 't' : n = '\t'; break; |
| case 'v' : n = '\v'; break; |
| case 'b' : n = '\b'; break; |
| case 'r' : n = '\r'; break; |
| case 'f' : n = '\f'; break; |
| case 'a' : n = '\a'; break; |
| case '\\': n = '\\'; break; |
| case '\'': n = '\''; break; |
| case '"' : n = '"'; break; |
| case 'x' : |
| next_ch(); |
| n = next_hex() << 8; |
| n |= next_hex() << 4; |
| n |= next_hex(); |
| return n; |
| case '0' : |
| case '1' : |
| case '2' : |
| case '3' : |
| n = next_oct() << 6; |
| n |= next_oct() << 3; |
| n |= next_oct(); |
| return n; |
| default : |
| n = text_ch; |
| break; |
| } |
| } |
| else { |
| n = text_ch; |
| } |
| next_ch_fast(); |
| return n; |
| } |
| |
| static void set_string_text_val(int pos, int len, int in_quotes, size_t char_size) { |
| int cnt = 0; |
| memset(&text_val, 0, sizeof(text_val)); |
| text_val.type_class = TYPE_CLASS_ARRAY; |
| text_val.size = (len + 1) * char_size; |
| text_val.value = tmp_alloc((size_t)text_val.size); |
| text_val.constant = 1; |
| text_pos = pos - 1; |
| next_ch(); |
| if (in_quotes) { |
| while (cnt < len) { |
| int ch = next_char_val(); |
| if (big_endian) swap_bytes(&ch, sizeof(ch)); |
| memcpy((uint8_t *)text_val.value + char_size * cnt++, &ch, char_size); |
| } |
| } |
| else { |
| while (cnt < len) { |
| int ch = text_ch; |
| if (big_endian) swap_bytes(&ch, sizeof(ch)); |
| memcpy((uint8_t *)text_val.value + char_size * cnt++, &ch, char_size); |
| next_ch_fast(); |
| } |
| } |
| memset((uint8_t *)text_val.value + char_size * cnt++, 0, char_size); |
| } |
| |
| static int is_name_character(int ch) { |
| if (ch >= 'A' && ch <= 'Z') return 1; |
| if (ch >= 'a' && ch <= 'z') return 1; |
| if (ch >= '0' && ch <= '9') return 1; |
| if (ch == '_') return 1; |
| if (ch == '$') return 1; |
| if (ch == '@') return 1; |
| return 0; |
| } |
| |
| static void parse_fp_number(int pos) { |
| char * end = NULL; |
| double x = str_to_double(text + pos, &end); |
| text_pos = end - text; |
| next_ch(); |
| text_val.type_class = TYPE_CLASS_REAL; |
| if (text_ch == 'f' || text_ch == 'F') { |
| next_ch(); |
| text_val_flags |= VAL_FLAG_F; |
| set_fp_value(&text_val, sizeof(float), x); |
| } |
| else { |
| if (text_ch == 'l' || text_ch == 'L') { |
| next_ch(); |
| text_val_flags |= VAL_FLAG_L; |
| } |
| set_fp_value(&text_val, sizeof(double), x); |
| } |
| } |
| |
| static void parse_char_value(int l) { |
| int value = next_char_val(); |
| memset(&text_val, 0, sizeof(text_val)); |
| if (l) { |
| text_val.type_class = TYPE_CLASS_CARDINAL; |
| if (value >= 0 && value <= 0xffff) set_int_value(&text_val, 2, value); |
| else set_int_value(&text_val, 4, value); |
| text_val_flags |= VAL_FLAG_L; |
| } |
| else { |
| text_val.type_class = TYPE_CLASS_INTEGER; |
| if (value >= 0 && value <= 0x7f) set_int_value(&text_val, 1, value); |
| else set_int_value(&text_val, 2, value); |
| } |
| text_val_flags |= VAL_FLAG_C; |
| text_val.constant = 1; |
| if (text_ch != '\'') error(ERR_INV_EXPRESSION, "Missing 'single quote'"); |
| next_ch(); |
| text_sy = SY_VAL; |
| } |
| |
| static void parse_string_value(int l) { |
| int len = 0; |
| int pos = text_pos; |
| while (text_ch != '"') { |
| next_char_val(); |
| len++; |
| } |
| set_string_text_val(pos, len, 1, l ? 4 : 1); |
| text_val_flags |= VAL_FLAG_S; |
| if (l) text_val_flags |= VAL_FLAG_L; |
| text_val.constant = 1; |
| text_sy = SY_VAL; |
| next_ch(); |
| } |
| |
| static void next_sy(void) { |
| for (;;) { |
| int ch = text_ch; |
| sy_pos = text_pos - 1; |
| text_val_flags = 0; |
| next_ch_fast(); |
| switch (ch) { |
| case 0: |
| text_sy = 0; |
| return; |
| case ' ': |
| case '\r': |
| case '\n': |
| case '\t': |
| continue; |
| case '(': |
| case ')': |
| case '{': |
| case '}': |
| case '~': |
| case '[': |
| case ']': |
| case ';': |
| case '?': |
| case ',': |
| text_sy = ch; |
| return; |
| case '.': |
| if (text_ch == '*') { |
| next_ch(); |
| text_sy = SY_PM_D; |
| return; |
| } |
| text_sy = ch; |
| return; |
| case ':': |
| if (text_ch == ':') { |
| next_ch(); |
| text_sy = SY_SCOPE; |
| return; |
| } |
| text_sy = ch; |
| return; |
| case '-': |
| if (text_ch == '>') { |
| next_ch(); |
| if (text_ch == '*') { |
| next_ch(); |
| text_sy = SY_PM_R; |
| return; |
| } |
| text_sy = SY_REF; |
| return; |
| } |
| if (text_ch == '-') { |
| next_ch(); |
| text_sy = SY_DEC; |
| return; |
| } |
| if (text_ch == '=') { |
| next_ch(); |
| text_sy = SY_A_SUB; |
| return; |
| } |
| text_sy = ch; |
| return; |
| case '+': |
| if (text_ch == '+') { |
| next_ch(); |
| text_sy = SY_INC; |
| return; |
| } |
| if (text_ch == '=') { |
| next_ch(); |
| text_sy = SY_A_ADD; |
| return; |
| } |
| text_sy = ch; |
| return; |
| case '<': |
| if (text_ch == '<') { |
| next_ch(); |
| if (text_ch == '=') { |
| next_ch(); |
| text_sy = SY_A_SHL; |
| return; |
| } |
| text_sy = SY_SHL; |
| return; |
| } |
| if (text_ch == '=') { |
| next_ch(); |
| text_sy = SY_LEQ; |
| return; |
| } |
| text_sy = ch; |
| return; |
| case '>': |
| if (text_ch == '>') { |
| next_ch(); |
| if (text_ch == '=') { |
| next_ch(); |
| text_sy = SY_A_SHR; |
| return; |
| } |
| text_sy = SY_SHR; |
| return; |
| } |
| if (text_ch == '=') { |
| next_ch(); |
| text_sy = SY_GEQ; |
| return; |
| } |
| text_sy = ch; |
| return; |
| case '=': |
| if (text_ch == '=') { |
| next_ch(); |
| text_sy = SY_EQU; |
| return; |
| } |
| text_sy = ch; |
| return; |
| case '!': |
| if (text_ch == '=') { |
| next_ch(); |
| text_sy = SY_NEQ; |
| return; |
| } |
| text_sy = ch; |
| return; |
| case '&': |
| if (text_ch == '&') { |
| next_ch(); |
| text_sy = SY_AND; |
| return; |
| } |
| if (text_ch == '=') { |
| next_ch(); |
| text_sy = SY_A_AND; |
| return; |
| } |
| text_sy = ch; |
| return; |
| case '|': |
| if (text_ch == '|') { |
| next_ch(); |
| text_sy = SY_OR; |
| return; |
| } |
| if (text_ch == '=') { |
| next_ch(); |
| text_sy = SY_A_OR; |
| return; |
| } |
| text_sy = ch; |
| return; |
| case '*': |
| if (text_ch == '=') { |
| next_ch(); |
| text_sy = SY_A_MUL; |
| return; |
| } |
| text_sy = ch; |
| return; |
| case '/': |
| if (text_ch == '|') { |
| next_ch(); |
| text_sy = SY_A_DIV; |
| return; |
| } |
| text_sy = ch; |
| return; |
| case '%': |
| if (text_ch == '|') { |
| next_ch(); |
| text_sy = SY_A_MOD; |
| return; |
| } |
| text_sy = ch; |
| return; |
| case '^': |
| if (text_ch == '=') { |
| next_ch(); |
| text_sy = SY_A_XOR; |
| return; |
| } |
| text_sy = ch; |
| return; |
| case '\'': |
| parse_char_value(0); |
| return; |
| case '"': |
| parse_string_value(0); |
| return; |
| case '0': |
| { |
| uint64_t value = 0; |
| int pos = text_pos - 2; |
| memset(&text_val, 0, sizeof(text_val)); |
| if (text_ch == '.' || text_ch == 'e' || text_ch == 'E' || text_ch == 'f' || text_ch == 'F') { |
| parse_fp_number(pos); |
| text_val.constant = 1; |
| text_sy = SY_VAL; |
| return; |
| } |
| else if (text_ch == 'x') { |
| next_ch(); |
| text_val_flags |= VAL_FLAG_X; |
| text_val.type_class = TYPE_CLASS_CARDINAL; |
| while ((text_ch >= '0' && text_ch <= '9') || |
| (text_ch >= 'A' && text_ch <= 'F') || |
| (text_ch >= 'a' && text_ch <= 'f')) { |
| value = (value << 4) | next_hex(); |
| } |
| } |
| else { |
| text_val.type_class = TYPE_CLASS_INTEGER; |
| while (text_ch >= '0' && text_ch <= '7') { |
| value = (value << 3) | next_oct(); |
| text_val.type_class = TYPE_CLASS_CARDINAL; |
| text_val_flags |= VAL_FLAG_X; |
| } |
| } |
| if (value <= 0xff) set_int_value(&text_val, sizeof(uint8_t), value); |
| else if (value <= 0xffff) set_int_value(&text_val, sizeof(uint16_t), value); |
| else if (value <= 0xffffffff) set_int_value(&text_val, sizeof(uint32_t), value); |
| else set_int_value(&text_val, sizeof(uint64_t), value); |
| text_val.constant = 1; |
| for (;;) { |
| if (text_ch == 'l' || text_ch == 'L') { |
| text_val_flags |= VAL_FLAG_L; |
| next_ch(); |
| } |
| else if (text_ch == 'u' || text_ch == 'U') { |
| text_val_flags |= VAL_FLAG_U; |
| next_ch(); |
| } |
| else { |
| break; |
| } |
| } |
| } |
| text_sy = SY_VAL; |
| return; |
| default: |
| if (ch >= '0' && ch <= '9') { |
| int pos = text_pos - 2; |
| uint64_t value = ch - '0'; |
| while (text_ch >= '0' && text_ch <= '9') { |
| value = (value * 10) + next_dec(); |
| } |
| memset(&text_val, 0, sizeof(text_val)); |
| if (text_ch == '.' || text_ch == 'e' || text_ch == 'E' || text_ch == 'f' || text_ch == 'F') { |
| parse_fp_number(pos); |
| } |
| else { |
| text_val.type_class = TYPE_CLASS_INTEGER; |
| for (;;) { |
| if (text_ch == 'l' || text_ch == 'L') { |
| text_val_flags |= VAL_FLAG_L; |
| next_ch(); |
| } |
| else if (text_ch == 'u' || text_ch == 'U') { |
| text_val.type_class = TYPE_CLASS_CARDINAL; |
| text_val_flags |= VAL_FLAG_U; |
| next_ch(); |
| } |
| else { |
| break; |
| } |
| } |
| if (text_val.type_class == TYPE_CLASS_INTEGER) { |
| if (value <= 0x7f) set_int_value(&text_val, 1, value); |
| else if (value <= 0x7fff) set_int_value(&text_val, 2, value); |
| else if (value <= 0x7fffffff) set_int_value(&text_val, 4, value); |
| else set_int_value(&text_val, 8, value); |
| } |
| else { |
| if (value <= 0xff) set_int_value(&text_val, 1, value); |
| else if (value <= 0xffff) set_int_value(&text_val, 2, value); |
| else if (value <= 0xffffffff) set_int_value(&text_val, 4, value); |
| else set_int_value(&text_val, 8, value); |
| } |
| } |
| text_val.constant = 1; |
| text_sy = SY_VAL; |
| return; |
| } |
| if (ch == '$') { |
| if (text_ch == '"') { |
| int len = 0; |
| int pos = text_pos + 1; |
| next_char_val(); |
| while (text_ch != '"') { |
| next_char_val(); |
| len++; |
| } |
| set_string_text_val(pos, len, 1, 1); |
| text_sy = SY_NAME; |
| next_ch(); |
| return; |
| } |
| if (text_ch == '{') { |
| int len = 0; |
| int pos = text_pos + 1; |
| next_ch(); |
| while (text_ch != '}') { |
| next_ch_fast(); |
| len++; |
| } |
| set_string_text_val(pos, len, 0, 1); |
| text_sy = SY_ID; |
| next_ch(); |
| return; |
| } |
| } |
| if (ch == 'L' && text_ch == '\'') { |
| next_ch(); |
| parse_char_value(1); |
| return; |
| } |
| if (ch == 'L' && text_ch == '"') { |
| next_ch(); |
| parse_string_value(1); |
| return; |
| } |
| if (is_name_character(ch)) { |
| int len = 1; |
| int pos = text_pos - 1; |
| while (is_name_character(text_ch)) { |
| next_ch_fast(); |
| len++; |
| } |
| set_string_text_val(pos, len, 0, 1); |
| if (strcmp((const char *)text_val.value, "sizeof") == 0) text_sy = (int)SY_SIZEOF; |
| else text_sy = SY_NAME; |
| return; |
| } |
| error(ERR_INV_EXPRESSION, "Illegal character"); |
| break; |
| } |
| } |
| } |
| |
| static void reg2value(int mode, Context * ctx, int frame, RegisterDefinition * def, Value * v) { |
| ini_value(v); |
| set_value(v, NULL, def->size, def->big_endian); |
| v->type_class = def->fp_value ? TYPE_CLASS_REAL : TYPE_CLASS_CARDINAL; |
| v->reg = def; |
| v->loc = (LocationExpressionState *)tmp_alloc_zero(sizeof(LocationExpressionState)); |
| v->loc->ctx = ctx; |
| if (def->size == 0 && def->bits != NULL) { |
| unsigned bit_cnt = 0; |
| int * bits = def->bits; |
| while (bits[bit_cnt] >= 0) bit_cnt++; |
| for (;;) { |
| unsigned i = 0; |
| def = def->parent; |
| if (def->bits != NULL) { |
| /* The parent is also a bitfield */ |
| int * pbits = (int *)tmp_alloc_zero(sizeof(int) * (bit_cnt + 1)); |
| while (i < bit_cnt) { |
| pbits[i] = def->bits[bits[i]]; |
| i++; |
| } |
| pbits[i] = -1; |
| bits = pbits; |
| } |
| else { |
| v->loc->pieces = (LocationPiece *)tmp_alloc_zero(sizeof(LocationPiece) * bit_cnt); |
| v->loc->pieces_cnt = v->loc->pieces_max = bit_cnt; |
| while (i < bit_cnt) { |
| v->loc->pieces[i].reg = def; |
| v->loc->pieces[i].bit_size = 1; |
| if ((def->big_endian && !def->left_to_right) || (!def->big_endian && def->left_to_right)) { |
| v->loc->pieces[i].bit_offs = (bits[i] & ~7) | (7 - (bits[i] & 7)); |
| } |
| else { |
| v->loc->pieces[i].bit_offs = bits[i]; |
| } |
| i++; |
| } |
| v->size = def->size; |
| v->value = tmp_alloc((size_t)v->size); |
| break; |
| } |
| } |
| } |
| else { |
| v->loc->pieces = (LocationPiece *)tmp_alloc_zero(sizeof(LocationPiece)); |
| v->loc->pieces_cnt = v->loc->pieces_max = 1; |
| v->loc->pieces->reg = def; |
| v->loc->pieces->size = def->size; |
| } |
| assert(v->size == def->size); |
| if (def->size > 0 && mode == MODE_NORMAL) { |
| if (ctx->exited) { |
| exception(ERR_ALREADY_EXITED); |
| } |
| else if (def->memory_context != NULL) { |
| if (context_read_reg(ctx, def, 0, def->size, v->value) < 0) exception(errno); |
| } |
| else if (!context_has_state(ctx)) { |
| if (frame != STACK_NO_FRAME) str_exception(ERR_INV_CONTEXT, "Invalid stack frame ID"); |
| if (context_read_reg(ctx, def, 0, def->size, v->value) < 0) exception(errno); |
| } |
| else if (!ctx->stopped && (ctx->reg_access & REG_ACCESS_RD_RUNNING) == 0) { |
| str_exception(ERR_IS_RUNNING, "Cannot read CPU register"); |
| } |
| else if (frame == STACK_TOP_FRAME || frame == STACK_NO_FRAME) { |
| if (context_read_reg(ctx, def, 0, def->size, v->value) < 0) exception(errno); |
| } |
| else { |
| StackFrame * info = NULL; |
| if (get_frame_info(ctx, frame, &info) < 0) exception(errno); |
| if (read_reg_bytes(info, def, 0, def->size, (uint8_t *)v->value) < 0) exception(errno); |
| v->loc->stack_frame = info; |
| } |
| if (def != v->reg) { |
| unsigned i; |
| uint8_t * value = (uint8_t *)v->value; |
| v->size = (v->loc->pieces_cnt + 7) / 8; |
| v->value = tmp_alloc_zero((size_t)v->size); |
| for (i = 0; i < v->loc->pieces_cnt; i++) { |
| LocationPiece * p = v->loc->pieces + i; |
| unsigned bit = p->bit_offs; |
| assert(p->reg == def); |
| assert(p->bit_size == 1); |
| assert(p->bit_offs / 8 < def->size); |
| if ((value[bit / 8] & bit_mask(v, bit)) == 0) continue; |
| ((uint8_t *)v->value)[i / 8] |= bit_mask(v, i); |
| } |
| } |
| } |
| } |
| |
| #if ENABLE_Symbols |
| static void set_value_endianness(Value * v, Symbol * sym, Symbol * type) { |
| SYM_FLAGS flags = 0; |
| if (sym != NULL && get_symbol_flags(sym, &flags) < 0) { |
| error(errno, "Cannot retrieve symbol flags"); |
| } |
| if (flags & SYM_FLAG_BIG_ENDIAN) v->big_endian = 1; |
| else if (flags & SYM_FLAG_LITTLE_ENDIAN) v->big_endian = 0; |
| else { |
| if (type != NULL && get_symbol_flags(type, &flags) < 0) { |
| error(errno, "Cannot retrieve symbol flags"); |
| } |
| if (flags & SYM_FLAG_BIG_ENDIAN) v->big_endian = 1; |
| else if (flags & SYM_FLAG_LITTLE_ENDIAN) v->big_endian = 0; |
| else v->big_endian = expression_context->big_endian; |
| } |
| } |
| |
| static void bit_sign_extend(Value * v, unsigned bit_cnt) { |
| if (bit_cnt > 0 && bit_cnt < v->size * 8) { |
| uint8_t * buf = (uint8_t *)v->value; |
| if (v->big_endian) { |
| unsigned offs = (unsigned)v->size * 8 - bit_cnt; |
| if (buf[offs / 8] & (1 << (7 - offs % 8))) { |
| /* Negative integer number */ |
| while (offs > 0) { |
| offs--; |
| buf[offs / 8] |= 1 << (7 - offs % 8); |
| } |
| } |
| } |
| else { |
| unsigned offs = bit_cnt - 1; |
| if (buf[offs / 8] & (1 << (offs % 8))) { |
| /* Negative integer number */ |
| while (offs < v->size * 8 - 1) { |
| offs++; |
| buf[offs / 8] |= 1 << (offs % 8); |
| } |
| } |
| } |
| } |
| } |
| |
| static void sign_extend(Value * v, LocationExpressionState * loc) { |
| ContextAddress type_size = 0; |
| assert(v->value != NULL); |
| if (v->type == NULL) return; |
| if (get_symbol_size(v->type, &type_size) < 0) { |
| error(errno, "Cannot retrieve value type size"); |
| } |
| if (type_size > v->size) { |
| /* Extend size */ |
| uint8_t * buf = (uint8_t *)tmp_alloc_zero((size_t)type_size); |
| if (!v->big_endian) memcpy(buf, v->value, (size_t)v->size); |
| else memcpy(buf + (size_t)(type_size - v->size), v->value, (size_t)v->size); |
| v->size = type_size; |
| v->value = buf; |
| } |
| if (v->type_class == TYPE_CLASS_INTEGER) { |
| /* Extend sign */ |
| unsigned i; |
| unsigned bit_cnt = 0; |
| for (i = 0; i < loc->pieces_cnt; i++) { |
| LocationPiece * piece = loc->pieces + i; |
| bit_cnt += piece->bit_size ? piece->bit_size : piece->size * 8; |
| } |
| bit_sign_extend(v, bit_cnt); |
| } |
| } |
| |
| static void check_hidden_redirection(Value * v) { |
| Symbol * type = v->type; |
| if (!v->remote) return; |
| if (type == NULL) return; |
| for (;;) { |
| SYM_FLAGS flags = 0; |
| Symbol * next = NULL; |
| if (get_symbol_flags(type, &flags) < 0) { |
| error(errno, "Cannot retrieve symbol flags"); |
| } |
| if (flags & SYM_FLAG_INDIRECT) { |
| LocationExpressionState * state = NULL; |
| LocationInfo * loc_info = NULL; |
| StackFrame * frame_info = NULL; |
| uint64_t args[1]; |
| args[0] = v->address; |
| if (get_location_info(type, &loc_info) < 0) { |
| if (get_error_code(errno) == ERR_SYM_NOT_FOUND) break; |
| error(errno, "Cannot get symbol data location information"); |
| } |
| if (expression_frame != STACK_NO_FRAME && get_frame_info(expression_context, expression_frame, &frame_info) < 0) { |
| error(errno, "Cannot get stack frame info"); |
| } |
| state = evaluate_location_expression(expression_context, frame_info, |
| loc_info->value_cmds.cmds, loc_info->value_cmds.cnt, args, 1); |
| if (state->stk_pos == 1) { |
| v->address = (ContextAddress)state->stk[0]; |
| v->remote = 1; |
| v->loc = NULL; |
| } |
| else { |
| if (state->pieces_cnt == 1 && state->pieces->implicit_pointer == 0 && |
| state->pieces->reg != NULL && state->pieces->reg->size == state->pieces->size) { |
| v->reg = state->pieces->reg; |
| } |
| v->remote = 0; |
| v->loc = state; |
| } |
| v->big_endian = loc_info->big_endian; |
| if (v->sym != NULL && v->size == 0 && get_symbol_size(v->sym, &v->size) < 0) { |
| error(errno, "Cannot retrieve symbol size"); |
| } |
| v->sym = NULL; |
| if (!v->remote) break; |
| } |
| if (get_symbol_type(type, &next) < 0) { |
| error(errno, "Cannot retrieve symbol type"); |
| } |
| if (next == type) break; |
| type = next; |
| } |
| } |
| |
| static void set_value_props(Value * v) { |
| v->bit_stride = 0; |
| v->binary_scale = 0; |
| v->decimal_scale = 0; |
| if (v->type == NULL) return; |
| if (v->type_class == TYPE_CLASS_ARRAY) { |
| Symbol * type = v->type; |
| for (;;) { |
| SymbolProperties props; |
| Symbol * next = NULL; |
| if (get_symbol_props(type, &props) < 0) { |
| error(errno, "Cannot get symbol properties"); |
| } |
| if (props.bit_stride) { |
| v->bit_stride = props.bit_stride; |
| break; |
| } |
| if (get_symbol_type(type, &next) < 0) { |
| error(errno, "Cannot retrieve symbol type"); |
| } |
| if (next == type) break; |
| type = next; |
| } |
| } |
| if (v->type_class == TYPE_CLASS_CARDINAL || v->type_class == TYPE_CLASS_INTEGER) { |
| Symbol * type = v->type; |
| for (;;) { |
| SymbolProperties props; |
| Symbol * next = NULL; |
| if (get_symbol_props(type, &props) < 0) { |
| error(errno, "Cannot get symbol properties"); |
| } |
| if (props.binary_scale) { |
| v->binary_scale = props.binary_scale; |
| break; |
| } |
| if (props.decimal_scale) { |
| v->decimal_scale = props.decimal_scale; |
| break; |
| } |
| if (get_symbol_type(type, &next) < 0) { |
| error(errno, "Cannot retrieve symbol type"); |
| } |
| if (next == type) break; |
| type = next; |
| } |
| } |
| } |
| |
| /* Note: sym2value() does NOT set v->size if v->sym != NULL */ |
| static int sym2value(int mode, Symbol * sym, Value * v) { |
| int sym_class = 0; |
| ini_value(v); |
| if (get_symbol_class(sym, &sym_class) < 0) { |
| error(errno, "Cannot retrieve symbol class"); |
| } |
| if (get_symbol_type(sym, &v->type) < 0) { |
| error(errno, "Cannot retrieve symbol type"); |
| } |
| if (get_symbol_type_class(sym, &v->type_class) < 0) { |
| error(errno, "Cannot retrieve symbol type class"); |
| } |
| switch (sym_class) { |
| case SYM_CLASS_VALUE: |
| case SYM_CLASS_REFERENCE: |
| case SYM_CLASS_COMP_UNIT: |
| if (mode == MODE_NORMAL) { |
| LocationExpressionState * state = NULL; |
| LocationInfo * loc_info = NULL; |
| StackFrame * frame_info = NULL; |
| if (get_location_info(sym, &loc_info) < 0) { |
| error(errno, "Cannot get symbol location information"); |
| } |
| if (expression_frame != STACK_NO_FRAME && get_frame_info(expression_context, expression_frame, &frame_info) < 0) { |
| error(errno, "Cannot get stack frame info"); |
| } |
| state = evaluate_location_expression(expression_context, frame_info, |
| loc_info->value_cmds.cmds, loc_info->value_cmds.cnt, NULL, 0); |
| if (state->stk_pos == 1) { |
| v->address = (ContextAddress)state->stk[0]; |
| v->remote = 1; |
| } |
| else { |
| if (state->pieces_cnt == 1 && state->pieces->implicit_pointer == 0 && |
| state->pieces->reg != NULL && state->pieces->reg->size == state->pieces->size) { |
| v->reg = state->pieces->reg; |
| } |
| v->loc = state; |
| } |
| v->big_endian = loc_info->big_endian; |
| } |
| else { |
| v->remote = 1; |
| } |
| v->constant = sym_class == SYM_CLASS_VALUE; |
| set_value_props(v); |
| break; |
| case SYM_CLASS_FUNCTION: |
| { |
| ContextAddress word = 0; |
| v->type_class = TYPE_CLASS_POINTER; |
| if (v->type != NULL) get_array_symbol(v->type, 0, &v->type); |
| if (mode == MODE_NORMAL && get_symbol_address(sym, &word) < 0) { |
| error(errno, "Cannot retrieve symbol address"); |
| } |
| set_ctx_word_value(v, word); |
| v->function = 1; |
| } |
| break; |
| default: |
| v->type = sym; |
| if (mode == MODE_NORMAL) { |
| LocationExpressionState * state = NULL; |
| LocationInfo * loc_info = NULL; |
| StackFrame * frame_info = NULL; |
| if (get_location_info(sym, &loc_info) < 0) { |
| break; |
| } |
| if (expression_frame != STACK_NO_FRAME && get_frame_info(expression_context, expression_frame, &frame_info) < 0) { |
| error(errno, "Cannot get stack frame info"); |
| } |
| state = evaluate_location_expression(expression_context, frame_info, |
| loc_info->value_cmds.cmds, loc_info->value_cmds.cnt, NULL, 0); |
| if (state->stk_pos == 1) { |
| v->address = (ContextAddress)state->stk[0]; |
| v->remote = 1; |
| } |
| else { |
| if (state->pieces_cnt == 1 && state->pieces->implicit_pointer == 0 && |
| state->pieces->reg != NULL && state->pieces->reg->size == state->pieces->size) { |
| v->reg = state->pieces->reg; |
| } |
| v->loc = state; |
| } |
| v->big_endian = loc_info->big_endian; |
| } |
| break; |
| } |
| v->sym = sym; |
| if (sym_class == SYM_CLASS_REFERENCE && mode == MODE_NORMAL) { |
| check_hidden_redirection(v); |
| } |
| return sym_class; |
| } |
| |
| static SYM_FLAGS get_all_symbol_flags(Symbol * sym) { |
| SYM_FLAGS all_flags = 0; |
| for (;;) { |
| Symbol * nxt = NULL; |
| SYM_FLAGS sym_flags = 0; |
| int sym_class = 0; |
| if (get_symbol_flags(sym, &sym_flags) < 0) error(errno, "Cannot get symbol flags"); |
| all_flags |= sym_flags; |
| if (get_symbol_class(sym, &sym_class) < 0) error(errno, "Cannot get symbol class"); |
| if (sym_class != SYM_CLASS_TYPE) break; |
| all_flags |= SYM_FLAG_TYPE; |
| if (get_symbol_type(sym, &nxt) < 0) error(errno, "Cannot get symbol type"); |
| if (nxt == sym) break; |
| sym = nxt; |
| } |
| return all_flags; |
| } |
| |
| static unsigned flag_count(SYM_FLAGS flags) { |
| unsigned i; |
| unsigned cnt = 0; |
| for (i = 0; i < sizeof(flags) * 8; i++) { |
| if (flags & ((SYM_FLAGS)1 << i)) cnt++; |
| } |
| return cnt; |
| } |
| #endif /* ENABLE_Symbols */ |
| |
| static int identifier(int mode, Value * scope, char * name, SYM_FLAGS flags, Value * v) { |
| ini_value(v); |
| if (scope == NULL) { |
| int i; |
| for (i = 0; i < id_callback_cnt; i++) { |
| if (id_callbacks[i](expression_context, expression_frame, name, v)) return SYM_CLASS_VALUE; |
| } |
| if (expression_context == NULL) { |
| exception(ERR_INV_CONTEXT); |
| } |
| if (name[0] == '$') { |
| Context * ctx = expression_context; |
| for (;;) { |
| RegisterDefinition * def = get_reg_definitions(ctx); |
| if (def != NULL) { |
| while (def->name != NULL) { |
| if (strcmp(name + 1, def->name) == 0) { |
| int frame = STACK_NO_FRAME; |
| if (ctx == expression_context) frame = expression_frame; |
| reg2value(mode, ctx, frame, def, v); |
| return SYM_CLASS_REFERENCE; |
| } |
| def++; |
| } |
| } |
| ctx = ctx->parent; |
| if (ctx == NULL) break; |
| } |
| } |
| if (strcmp(name, "$thread") == 0) { |
| set_string_value(v, expression_context->id); |
| v->constant = 1; |
| return SYM_CLASS_VALUE; |
| } |
| } |
| #if ENABLE_Symbols |
| { |
| Symbol * sym = NULL; |
| int n = 0; |
| |
| if (scope != NULL) { |
| int scope_class = 0; |
| Symbol * scope_sym = scope->sym; |
| if (scope->type != NULL) { |
| if (scope_sym != NULL && get_symbol_class(scope_sym, &scope_class) < 0) { |
| error(errno, "Cannot retrieve symbol class"); |
| } |
| if (scope_class != SYM_CLASS_FUNCTION) { |
| scope_sym = scope->type; |
| } |
| } |
| n = find_symbol_in_scope(expression_context, expression_frame, expression_addr, scope_sym, name, &sym); |
| } |
| else { |
| n = find_symbol_by_name(expression_context, expression_frame, expression_addr, name, &sym); |
| } |
| |
| if (n < 0) { |
| if (get_error_code(errno) != ERR_SYM_NOT_FOUND) error(errno, "Cannot read symbol data"); |
| } |
| else { |
| unsigned cnt = 0; |
| unsigned max = 8; |
| Symbol ** list = (Symbol **)tmp_alloc(sizeof(Symbol *) * max); |
| unsigned val_cnt = 0; |
| const SYM_FLAGS cmx_type = SYM_FLAG_STRUCT_TYPE | SYM_FLAG_CLASS_TYPE | SYM_FLAG_UNION_TYPE | SYM_FLAG_ENUM_TYPE; |
| const SYM_FLAGS flag_mask = SYM_FLAG_TYPE | SYM_FLAG_CONST_TYPE | SYM_FLAG_VOLATILE_TYPE | cmx_type; |
| SYM_FLAGS sym_flags; |
| int sym_class; |
| unsigned i; |
| |
| list[cnt++] = sym; |
| while (find_next_symbol(&sym) == 0) { |
| if (cnt + 1 >= max) list = (Symbol **)tmp_realloc(list, sizeof(Symbol *) * (max *= 2)); |
| list[cnt++] = sym; |
| } |
| assert(cnt < max); |
| list[cnt] = NULL; |
| /* Count variables. In C, variables eclipse composite types */ |
| for (i = 0; i < cnt; i++) { |
| if (get_symbol_class(list[i], &sym_class) < 0) error(errno, "Cannot retrieve symbol class"); |
| if (sym_class == SYM_CLASS_VALUE || sym_class == SYM_CLASS_REFERENCE) val_cnt++; |
| } |
| /* Search for best match */ |
| sym = list[0]; |
| sym_flags = (get_all_symbol_flags(sym) ^ flags) & flag_mask; |
| for (i = 1; i < cnt; i++) { |
| SYM_FLAGS nxt_flags = (get_all_symbol_flags(list[i]) ^ flags) & flag_mask; |
| if (val_cnt > 0 && (nxt_flags & cmx_type) != 0) continue; |
| if (flag_count(nxt_flags) >= flag_count(sym_flags)) continue; |
| sym_flags = nxt_flags; |
| sym = list[i]; |
| } |
| sym_class = sym2value(mode, sym, v); |
| if (cnt > 1) v->sym_list = list; |
| return sym_class; |
| } |
| } |
| #endif |
| #if ENABLE_RCBP_TEST |
| { |
| void * ptr = NULL; |
| int sym_class = 0; |
| if (find_test_symbol(expression_context, name, &ptr, &sym_class) >= 0) { |
| if (sym_class == SYM_CLASS_FUNCTION) { |
| set_ctx_word_value(v, (ContextAddress)ptr); |
| v->type_class = TYPE_CLASS_POINTER; |
| v->function = 1; |
| } |
| else { |
| v->address = (ContextAddress)ptr; |
| v->remote = 1; |
| } |
| return sym_class; |
| } |
| } |
| #endif |
| return -1; |
| } |
| |
| static int qualified_name(int mode, Value * scope, SYM_FLAGS flags, Value * v) { |
| Value x; |
| int sym_class = 0; |
| for (;;) { |
| ini_value(v); |
| if (text_sy == SY_NAME || (scope != NULL && text_sy == '~')) { |
| int tilda = text_sy == '~'; |
| if (tilda) { |
| next_sy(); |
| if (text_sy != SY_NAME) error(ERR_INV_EXPRESSION, "Identifier expected"); |
| } |
| if (mode != MODE_SKIP) { |
| SYM_FLAGS f = flags; |
| char * name = (char *)text_val.value; |
| name = tilda ? tmp_strdup2("~", name) : tmp_strdup(name); |
| next_sy(); |
| if (text_sy == SY_SCOPE) f |= SYM_FLAG_TYPE; |
| sym_class = identifier(text_sy != SY_SCOPE ? mode : MODE_TYPE, scope, name, f, v); |
| if (sym_class < 0) error(ERR_INV_EXPRESSION, "Undefined identifier '%s'", name); |
| } |
| else { |
| next_sy(); |
| } |
| } |
| else if (text_sy == SY_ID) { |
| if (mode != MODE_SKIP) { |
| int ok = 0; |
| Context * ctx = NULL; |
| int frame = STACK_NO_FRAME; |
| RegisterDefinition * def = NULL; |
| const char * id = tmp_strdup((char *)text_val.value); |
| next_sy(); |
| if (id2register(id, &ctx, &frame, &def) >= 0) { |
| if (frame == STACK_TOP_FRAME) frame = expression_frame; |
| sym_class = SYM_CLASS_UNKNOWN; |
| reg2value(mode, ctx, frame, def, v); |
| ok = 1; |
| } |
| #if ENABLE_Symbols |
| if (!ok) { |
| Symbol * sym = NULL; |
| if (id2symbol(id, &sym) >= 0) { |
| sym_class = sym2value(text_sy != SY_SCOPE ? mode : MODE_TYPE, sym, v); |
| ok = 1; |
| } |
| } |
| #endif |
| if (!ok) error(ERR_INV_EXPRESSION, "Symbol not found: %s", id); |
| } |
| else { |
| next_sy(); |
| } |
| } |
| else { |
| error(ERR_INV_EXPRESSION, "Identifier expected"); |
| } |
| if (text_sy != SY_SCOPE) break; |
| next_sy(); |
| scope = &x; |
| x = *v; |
| } |
| return sym_class; |
| } |
| |
| static int64_t to_int(int mode, Value * v); |
| #define TYPE_EXPR_LENGTH 64 |
| |
| static int type_expression(int mode, int * buf) { |
| int i = 0; |
| int pos = 0; |
| int expr_buf[TYPE_EXPR_LENGTH]; |
| int expr_len = 0; |
| int arr_buf[TYPE_EXPR_LENGTH]; |
| int arr_len = 0; |
| while (text_sy == '*') { |
| next_sy(); |
| if (pos >= TYPE_EXPR_LENGTH) error(ERR_BUFFER_OVERFLOW, "Type expression is too long"); |
| buf[pos++] = 1; |
| } |
| if (text_sy == '(') { |
| next_sy(); |
| expr_len = type_expression(mode, expr_buf); |
| if (text_sy != ')') error(ERR_INV_EXPRESSION, "')' expected"); |
| next_sy(); |
| } |
| while (text_sy == '[') { |
| next_sy(); |
| if (text_sy != SY_VAL) error(ERR_INV_EXPRESSION, "Number expected"); |
| if (arr_len >= TYPE_EXPR_LENGTH) error(ERR_BUFFER_OVERFLOW, "Type expression is too long"); |
| arr_buf[arr_len] = (int)to_int(mode, &text_val); |
| if (mode == MODE_NORMAL && arr_buf[arr_len] < 1) error(ERR_INV_EXPRESSION, "Positive number expected"); |
| arr_len++; |
| next_sy(); |
| if (text_sy != ']') error(ERR_INV_EXPRESSION, "']' expected"); |
| next_sy(); |
| } |
| for (i = 0; i < arr_len; i++) { |
| if (pos >= TYPE_EXPR_LENGTH) error(ERR_BUFFER_OVERFLOW, "Type expression is too long"); |
| buf[pos++] = arr_buf[arr_len - i - 1]; |
| } |
| for (i = 0; i < expr_len; i++) { |
| if (pos >= TYPE_EXPR_LENGTH) error(ERR_BUFFER_OVERFLOW, "Type expression is too long"); |
| buf[pos++] = expr_buf[i]; |
| } |
| return pos; |
| } |
| |
| static int type_name(int mode, Symbol ** type) { |
| Value v; |
| int expr_buf[TYPE_EXPR_LENGTH]; |
| int expr_len = 0; |
| char * name = NULL; |
| int sym_class; |
| SYM_FLAGS sym_flags = 0; |
| int name_cnt = 0; |
| |
| while (text_sy == SY_NAME) { |
| if (strcmp((const char *)(text_val.value), "const") == 0) { |
| sym_flags |= SYM_FLAG_CONST_TYPE; |
| next_sy(); |
| } |
| else if (strcmp((const char *)(text_val.value), "volatile") == 0) { |
| sym_flags |= SYM_FLAG_VOLATILE_TYPE; |
| next_sy(); |
| } |
| else { |
| break; |
| } |
| } |
| if (text_sy == SY_NAME) { |
| if (strcmp((const char *)(text_val.value), "struct") == 0) { |
| sym_flags |= SYM_FLAG_STRUCT_TYPE; |
| next_sy(); |
| } |
| else if (strcmp((const char *)(text_val.value), "class") == 0) { |
| sym_flags |= SYM_FLAG_CLASS_TYPE; |
| next_sy(); |
| } |
| else if (strcmp((const char *)(text_val.value), "union") == 0) { |
| sym_flags |= SYM_FLAG_UNION_TYPE; |
| next_sy(); |
| } |
| else if (strcmp((const char *)(text_val.value), "enum") == 0) { |
| sym_flags |= SYM_FLAG_ENUM_TYPE; |
| next_sy(); |
| } |
| } |
| |
| if (text_sy == SY_NAME) { |
| do { |
| if (name == NULL) { |
| name = tmp_strdup((char *)text_val.value); |
| } |
| else { |
| name = tmp_strdup2(name, " "); |
| name = tmp_strdup2(name, (char *)text_val.value); |
| } |
| name_cnt++; |
| next_sy(); |
| } |
| while (text_sy == SY_NAME); |
| if (text_sy == '<') { |
| int prev_sy = 0; |
| unsigned cnt = 0; |
| uint64_t val = 0; |
| char tmp_buf[40]; |
| do { |
| switch (text_sy) { |
| case SY_NAME: |
| if (prev_sy == SY_NAME || prev_sy == '*' || prev_sy == '&') |
| name = tmp_strdup2(name, " "); |
| name = tmp_strdup2(name, (char *)text_val.value); |
| break; |
| case SY_SCOPE: |
| name = tmp_strdup2(name, "::"); |
| break; |
| case SY_VAL: |
| value_to_unsigned(&text_val, &val); |
| snprintf(tmp_buf, sizeof(tmp_buf), "%lu", (unsigned long)val); |
| name = tmp_strdup2(name, tmp_buf); |
| break; |
| case '*': |
| case '&': |
| case '[': |
| case ']': |
| case '(': |
| case ')': |
| case '{': |
| case '}': |
| tmp_buf[0] = (char)text_sy; |
| tmp_buf[1] = 0; |
| name = tmp_strdup2(name, tmp_buf); |
| break; |
| case ',': |
| name = tmp_strdup2(name, ", "); |
| break; |
| case '<': |
| name = tmp_strdup2(name, "<"); |
| cnt++; |
| break; |
| case '>': |
| if (prev_sy == '>') name = tmp_strdup2(name, " "); |
| name = tmp_strdup2(name, ">"); |
| cnt--; |
| break; |
| default: |
| return 0; |
| } |
| prev_sy = text_sy; |
| next_sy(); |
| } |
| while (cnt > 0); |
| } |
| sym_class = identifier(mode, NULL, name, sym_flags | SYM_FLAG_TYPE, &v); |
| } |
| #if ENABLE_Symbols |
| else if (text_sy == SY_ID) { |
| Symbol * sym = NULL; |
| const char * id = (const char *)text_val.value; |
| if (id2symbol(id, &sym) < 0) return 0; |
| sym_class = sym2value(mode, sym, &v); |
| name = tmp_strdup(id); |
| next_sy(); |
| } |
| #endif |
| else { |
| if (sym_flags) error(ERR_INV_EXPRESSION, "Identifier expected"); |
| return 0; |
| } |
| |
| if (sym_class < 0) return 0; |
| if (text_sy == SY_SCOPE) { |
| Value scope = v; |
| next_sy(); |
| sym_class = qualified_name(mode, &scope, sym_flags | SYM_FLAG_TYPE, &v); |
| } |
| if (sym_class != SYM_CLASS_TYPE) { |
| if (sym_flags) error(ERR_INV_EXPRESSION, "Type '%s' not found", name); |
| return 0; |
| } |
| expr_len = type_expression(mode, expr_buf); |
| if (mode != MODE_SKIP) { |
| int i; |
| for (i = 0; i < expr_len; i++) { |
| #if ENABLE_Symbols |
| if (expr_buf[i] == 1) { |
| if (get_array_symbol(v.type, 0, &v.type)) { |
| error(errno, "Cannot create pointer type"); |
| } |
| } |
| else { |
| if (get_array_symbol(v.type, expr_buf[i], &v.type)) { |
| error(errno, "Cannot create array type"); |
| } |
| } |
| #else |
| v.type = NULL; |
| #endif |
| } |
| } |
| *type = v.type; |
| return 1; |
| } |
| |
| static void load_value(Value * v) { |
| if (v->remote) { |
| size_t size = (size_t)v->size; |
| void * buf = tmp_alloc(size); |
| assert(!v->constant); |
| if (context_read_mem(expression_context, v->address, buf, size) < 0) { |
| error(errno, "Can't read variable value"); |
| } |
| v->value = buf; |
| v->remote = 0; |
| } |
| else if (v->value == NULL) { |
| size_t size = 0; |
| void * value = NULL; |
| LocationExpressionState * loc = v->loc; |
| read_location_pieces(expression_context, loc->stack_frame, |
| loc->pieces, loc->pieces_cnt, v->big_endian, &value, &size); |
| v->value = value; |
| v->size = (ContextAddress)size; |
| sign_extend(v, loc); |
| } |
| } |
| |
| static int is_number(Value * v) { |
| switch (v->type_class) { |
| case TYPE_CLASS_INTEGER: |
| case TYPE_CLASS_CARDINAL: |
| case TYPE_CLASS_REAL: |
| case TYPE_CLASS_COMPLEX: |
| case TYPE_CLASS_ENUMERATION: |
| return 1; |
| } |
| return 0; |
| } |
| |
| static int is_real_number(Value * v) { |
| switch (v->type_class) { |
| case TYPE_CLASS_INTEGER: |
| case TYPE_CLASS_CARDINAL: |
| if (v->binary_scale != 0) return 1; |
| if (v->decimal_scale != 0) return 1; |
| break; |
| case TYPE_CLASS_REAL: |
| return 1; |
| } |
| return 0; |
| } |
| |
| static int is_whole_number(Value * v) { |
| switch (v->type_class) { |
| case TYPE_CLASS_INTEGER: |
| case TYPE_CLASS_CARDINAL: |
| if (v->binary_scale != 0) return 0; |
| if (v->decimal_scale != 0) return 0; |
| return 1; |
| case TYPE_CLASS_ENUMERATION: |
| return 1; |
| } |
| return 0; |
| } |
| |
| static void to_host_endianness(Value * v) { |
| assert(v->type_class != TYPE_CLASS_COMPOSITE); |
| assert(v->type_class != TYPE_CLASS_ARRAY); |
| assert(!v->remote); |
| if (v->big_endian != big_endian) { |
| size_t i = 0; |
| size_t n = (size_t)v->size; |
| uint8_t * buf = (uint8_t *)tmp_alloc(n); |
| for (i = 0; i < n; i++) { |
| buf[i] = ((uint8_t *)v->value)[n - i - 1]; |
| } |
| v->value = buf; |
| v->big_endian = big_endian; |
| v->sym_list = NULL; |
| v->sym = NULL; |
| v->reg = NULL; |
| v->loc = NULL; |
| } |
| } |
| |
| static int64_t to_int_fixed_point(int mode, Value * v) { |
| if (v->size == 0 || mode != MODE_NORMAL) { |
| if (v->remote) { |
| v->value = tmp_alloc_zero((size_t)v->size); |
| v->remote = 0; |
| } |
| return 0; |
| } |
| |
| if (v->type_class == TYPE_CLASS_POINTER) { |
| load_value(v); |
| to_host_endianness(v); |
| switch (v->size) { |
| case 1: return *(uint8_t *)v->value; |
| case 2: return *(uint16_t *)v->value; |
| case 4: return *(uint32_t *)v->value; |
| case 8: return *(uint64_t *)v->value; |
| } |
| } |
| if (is_number(v)) { |
| load_value(v); |
| to_host_endianness(v); |
| |
| if (v->type_class == TYPE_CLASS_REAL) { |
| switch (v->size) { |
| case 4: return (int64_t)*(float *)v->value; |
| case 8: return (int64_t)*(double *)v->value; |
| } |
| } |
| else if (v->type_class == TYPE_CLASS_COMPLEX) { |
| /* Not supported */ |
| } |
| else if (v->type_class == TYPE_CLASS_CARDINAL) { |
| switch (v->size) { |
| case 1: return (int64_t)*(uint8_t *)v->value; |
| case 2: return (int64_t)*(uint16_t *)v->value; |
| case 4: return (int64_t)*(uint32_t *)v->value; |
| case 8: return (int64_t)*(uint64_t *)v->value; |
| } |
| } |
| else { |
| switch (v->size) { |
| case 1: return *(int8_t *)v->value; |
| case 2: return *(int16_t *)v->value; |
| case 4: return *(int32_t *)v->value; |
| case 8: return *(int64_t *)v->value; |
| } |
| } |
| } |
| |
| error(ERR_INV_EXPRESSION, "Operation is not applicable for the value type"); |
| return 0; |
| } |
| |
| static uint64_t to_uns_fixed_point(int mode, Value * v) { |
| if (v->size == 0 || mode != MODE_NORMAL) { |
| if (v->remote) { |
| v->value = tmp_alloc_zero((size_t)v->size); |
| v->remote = 0; |
| } |
| return 0; |
| } |
| |
| if (v->type_class == TYPE_CLASS_ARRAY && v->remote) { |
| return (uint64_t)v->address; |
| } |
| if (v->type_class == TYPE_CLASS_POINTER || v->type_class == TYPE_CLASS_MEMBER_PTR) { |
| load_value(v); |
| to_host_endianness(v); |
| switch (v->size) { |
| case 1: return *(uint8_t *)v->value; |
| case 2: return *(uint16_t *)v->value; |
| case 4: return *(uint32_t *)v->value; |
| case 8: return *(uint64_t *)v->value; |
| } |
| } |
| if (is_number(v)) { |
| load_value(v); |
| to_host_endianness(v); |
| |
| if (v->type_class == TYPE_CLASS_REAL) { |
| switch (v->size) { |
| case 4: return (uint64_t)*(float *)v->value; |
| case 8: return (uint64_t)*(double *)v->value; |
| } |
| } |
| else if (v->type_class == TYPE_CLASS_COMPLEX) { |
| /* Not supported */ |
| } |
| else if (v->type_class == TYPE_CLASS_CARDINAL) { |
| switch (v->size) { |
| case 1: return *(uint8_t *)v->value; |
| case 2: return *(uint16_t *)v->value; |
| case 4: return *(uint32_t *)v->value; |
| case 8: return *(uint64_t *)v->value; |
| } |
| } |
| else { |
| switch (v->size) { |
| case 1: return (uint64_t)*(int8_t *)v->value; |
| case 2: return (uint64_t)*(int16_t *)v->value; |
| case 4: return (uint64_t)*(int32_t *)v->value; |
| case 8: return (uint64_t)*(int64_t *)v->value; |
| } |
| } |
| } |
| |
| error(ERR_INV_EXPRESSION, "Operation is not applicable for the value type"); |
| return 0; |
| } |
| |
| static int64_t to_int(int mode, Value * v) { |
| if (v->type_class == TYPE_CLASS_INTEGER) { |
| int64_t n = to_int_fixed_point(mode, v); |
| int decimal_scale = v->decimal_scale; |
| while (decimal_scale > 0) { |
| decimal_scale--; |
| n = n * 10; |
| } |
| while (decimal_scale < 0) { |
| decimal_scale++; |
| n = n / 10; |
| } |
| if (v->binary_scale > 0) n = n << +v->binary_scale; |
| if (v->binary_scale < 0) n = n >> -v->binary_scale; |
| return n; |
| } |
| if (v->type_class == TYPE_CLASS_CARDINAL) { |
| uint64_t n = to_uns_fixed_point(mode, v); |
| int decimal_scale = v->decimal_scale; |
| while (decimal_scale > 0) { |
| decimal_scale--; |
| n = n * 10; |
| } |
| while (decimal_scale < 0) { |
| decimal_scale++; |
| n = n / 10; |
| } |
| if (v->binary_scale > 0) n = n << +v->binary_scale; |
| if (v->binary_scale < 0) n = n >> -v->binary_scale; |
| return n; |
| } |
| assert(v->binary_scale == 0); |
| assert(v->decimal_scale == 0); |
| return to_int_fixed_point(mode, v); |
| } |
| |
| static uint64_t to_uns(int mode, Value * v) { |
| if (v->type_class == TYPE_CLASS_INTEGER) { |
| int64_t n = to_int_fixed_point(mode, v); |
| int decimal_scale = v->decimal_scale; |
| while (decimal_scale > 0) { |
| decimal_scale--; |
| n = n * 10; |
| } |
| while (decimal_scale < 0) { |
| decimal_scale++; |
| n = n / 10; |
| } |
| if (v->binary_scale > 0) n = n << +v->binary_scale; |
| if (v->binary_scale < 0) n = n >> -v->binary_scale; |
| return n; |
| } |
| if (v->type_class == TYPE_CLASS_CARDINAL) { |
| uint64_t n = to_uns_fixed_point(mode, v); |
| int decimal_scale = v->decimal_scale; |
| while (decimal_scale > 0) { |
| decimal_scale--; |
| n = n * 10; |
| } |
| while (decimal_scale < 0) { |
| decimal_scale++; |
| n = n / 10; |
| } |
| if (v->binary_scale > 0) n = n << +v->binary_scale; |
| if (v->binary_scale < 0) n = n >> -v->binary_scale; |
| return n; |
| } |
| assert(v->binary_scale == 0); |
| assert(v->decimal_scale == 0); |
| return to_uns_fixed_point(mode, v); |
| } |
| |
| static double to_double(int mode, Value * v) { |
| if (v->size == 0 || mode != MODE_NORMAL) { |
| if (v->remote) { |
| v->value = tmp_alloc_zero((size_t)v->size); |
| v->remote = 0; |
| } |
| return 0; |
| } |
| |
| if (is_number(v)) { |
| load_value(v); |
| to_host_endianness(v); |
| |
| if (v->type_class == TYPE_CLASS_REAL) { |
| switch (v->size) { |
| case 4: return *(float *)v->value; |
| case 8: return *(double *)v->value; |
| } |
| } |
| else if (v->type_class == TYPE_CLASS_COMPLEX) { |
| /* Not supported */ |
| } |
| else if (v->type_class == TYPE_CLASS_CARDINAL) { |
| double n = (double)to_uns_fixed_point(mode, v); |
| int binary_scale = v->binary_scale; |
| int decimal_scale = v->decimal_scale; |
| while (binary_scale > 0) { |
| binary_scale--; |
| n = n * 2; |
| } |
| while (n != 0 && binary_scale < 0) { |
| binary_scale++; |
| n = n / 2; |
| } |
| while (decimal_scale > 0) { |
| decimal_scale--; |
| n = n * 10; |
| } |
| while (n != 0 && decimal_scale < 0) { |
| decimal_scale++; |
| n = n / 10; |
| } |
| return n; |
| } |
| else { |
| double n = (double)to_int_fixed_point(mode, v); |
| int binary_scale = v->binary_scale; |
| int decimal_scale = v->decimal_scale; |
| while (binary_scale > 0) { |
| binary_scale--; |
| n = n * 2; |
| } |
| while (n != 0 && binary_scale < 0) { |
| binary_scale++; |
| n = n / 2; |
| } |
| while (decimal_scale > 0) { |
| decimal_scale--; |
| n = n * 10; |
| } |
| while (n != 0 && decimal_scale < 0) { |
| decimal_scale++; |
| n = n / 10; |
| } |
| return n; |
| } |
| } |
| |
| error(ERR_INV_EXPRESSION, "Operation is not applicable for the value type"); |
| return 0; |
| } |
| |
| static double to_i_double(int mode, Value * v) { |
| if (v->size == 0 || mode != MODE_NORMAL) { |
| if (v->remote) { |
| v->value = tmp_alloc_zero((size_t)v->size); |
| v->remote = 0; |
| } |
| return 0; |
| } |
| |
| if (is_number(v)) { |
| load_value(v); |
| to_host_endianness(v); |
| |
| if (v->type_class == TYPE_CLASS_COMPLEX) { |
| switch (v->size) { |
| case 8: return *(float *)((char *)v->value + 4); |
| case 16: return *(double *)((char *)v->value + 8); |
| } |
| } |
| else { |
| return 0.0; |
| } |
| } |
| |
| error(ERR_INV_EXPRESSION, "Operation is not applicable for the value type"); |
| return 0; |
| } |
| |
| static double to_r_double(int mode, Value * v) { |
| if (v->size == 0 || mode != MODE_NORMAL) { |
| if (v->remote) { |
| v->value = tmp_alloc_zero((size_t)v->size); |
| v->remote = 0; |
| } |
| return 0; |
| } |
| |
| if (is_number(v)) { |
| load_value(v); |
| to_host_endianness(v); |
| |
| if (v->type_class == TYPE_CLASS_COMPLEX) { |
| switch (v->size) { |
| case 8: return *(float *)v->value; |
| case 16: return *(double *)v->value; |
| } |
| } |
| else { |
| return to_double(mode, v); |
| } |
| } |
| |
| error(ERR_INV_EXPRESSION, "Operation is not applicable for the value type"); |
| return 0; |
| } |
| |
| static int to_boolean(int mode, Value * v) { |
| return to_int(mode, v) != 0; |
| } |
| |
| static void qualified_name_expression(int mode, Value * scope, Value * v) { |
| if (qualified_name(mode, scope, 0, v) != SYM_CLASS_TYPE) return; |
| error(ERR_INV_EXPRESSION, "Illegal usage of a type in expression"); |
| } |
| |
| #if ENABLE_Symbols |
| static int get_std_type(const char * name, int type_class, Symbol ** type, size_t * size) { |
| Symbol * sym = NULL; |
| int sym_class = 0; |
| ContextAddress sym_size = 0; |
| if (find_symbol_by_name(expression_context, |
| expression_frame, expression_addr, name, &sym) < 0) return 0; |
| if (sym == NULL) return 0; |
| if (get_symbol_class(sym, &sym_class) < 0 || sym_class != SYM_CLASS_TYPE) return 0; |
| if (type_class != TYPE_CLASS_UNKNOWN) { |
| int sym_type_class = TYPE_CLASS_UNKNOWN; |
| if (get_symbol_type_class(sym, &sym_type_class) < 0 || sym_type_class != type_class) return 0; |
| } |
| if (get_symbol_size(sym, &sym_size) < 0 || sym_size == 0) return 0; |
| *type = sym; |
| *size = (size_t)sym_size; |
| return 1; |
| } |
| #endif |
| |
| static void set_fp_type(Value * v) { |
| #if ENABLE_Symbols |
| Symbol * type = NULL; |
| size_t size = 0; |
| if (get_std_type("float", TYPE_CLASS_REAL, &type, &size) && v->size == size) { |
| v->type = type; |
| return; |
| } |
| if (get_std_type("double", TYPE_CLASS_REAL, &type, &size) && v->size == size) { |
| v->type = type; |
| return; |
| } |
| #endif |
| } |
| |
| static void set_complex_type(Value * v) { |
| #if ENABLE_Symbols |
| Symbol * type = NULL; |
| size_t size = 0; |
| if (get_std_type("complex float", TYPE_CLASS_COMPLEX, &type, &size) && v->size == size) { |
| v->type = type; |
| return; |
| } |
| if (get_std_type("complex double", TYPE_CLASS_COMPLEX, &type, &size) && v->size == size) { |
| v->type = type; |
| return; |
| } |
| #endif |
| } |
| |
| static void expression(int mode, Value * v); |
| |
| static void primary_expression(int mode, Value * v) { |
| if (text_sy == '(') { |
| next_sy(); |
| for (;;) { |
| expression(mode, v); |
| if (text_sy != ',') break; |
| next_sy(); |
| } |
| if (text_sy != ')') error(ERR_INV_EXPRESSION, "Missing ')'"); |
| next_sy(); |
| } |
| else if (text_sy == SY_VAL) { |
| int flags = text_val_flags; |
| *v = text_val; |
| next_sy(); |
| #if ENABLE_Symbols |
| if (v->type_class == TYPE_CLASS_INTEGER || v->type_class == TYPE_CLASS_CARDINAL) { |
| size_t size = 0; |
| uint64_t n = to_uns(MODE_NORMAL, v); |
| if (flags & VAL_FLAG_C) { |
| Symbol * type = NULL; |
| if (get_std_type(flags & VAL_FLAG_L ? "wchar_t" : "char", TYPE_CLASS_UNKNOWN, &type, &size)) { |
| uint64_t m = ((uint64_t)1 << (size * 8 - 1)) - 1; |
| if (n <= m) { |
| v->type = type; |
| get_symbol_type_class(type, &v->type_class); |
| } |
| } |
| } |
| else { |
| if ((flags & (VAL_FLAG_L | VAL_FLAG_U)) == 0) { |
| Symbol * type = NULL; |
| if (get_std_type("int", TYPE_CLASS_INTEGER, &type, &size)) { |
| uint64_t m = ((uint64_t)1 << (size * 8 - 1)) - 1; |
| if (n <= m) { |
| v->type = type; |
| v->type_class = TYPE_CLASS_INTEGER; |
| } |
| } |
| } |
| if (v->type == NULL && (flags & VAL_FLAG_L) == 0 && |
| (flags & (VAL_FLAG_X | VAL_FLAG_U)) != 0) { |
| Symbol * type = NULL; |
| if (get_std_type("unsigned int", TYPE_CLASS_CARDINAL, &type, &size)) { |
| uint64_t m = ((uint64_t)1 << (size * 8)) - 1; |
| if (n <= m) { |
| v->type = type; |
| v->type_class = TYPE_CLASS_CARDINAL; |
| } |
| } |
| } |
| if (v->type == NULL && (flags & VAL_FLAG_U) == 0) { |
| Symbol * type = NULL; |
| if (get_std_type("long int", TYPE_CLASS_INTEGER, &type, &size)) { |
| uint64_t m = ((uint64_t)1 << (size * 8 - 1)) - 1; |
| if (n <= m) { |
| v->type = type; |
| v->type_class = TYPE_CLASS_INTEGER; |
| } |
| } |
| } |
| if (v->type == NULL) { |
| Symbol * type = NULL; |
| if (get_std_type("long unsigned int", TYPE_CLASS_CARDINAL, &type, &size)) { |
| uint64_t m = ((uint64_t)1 << (size * 8)) - 1; |
| if (n <= m) { |
| v->type = type; |
| v->type_class = TYPE_CLASS_CARDINAL; |
| } |
| } |
| } |
| } |
| if (v->type != NULL && size != v->size) set_int_value(v, size, n); |
| } |
| else if (v->type_class == TYPE_CLASS_REAL) { |
| size_t size = 0; |
| Symbol * type = NULL; |
| const char * name = flags & VAL_FLAG_F ? "float" : "double"; |
| if (get_std_type(name, TYPE_CLASS_REAL, &type, &size)) { |
| v->type = type; |
| if (size != v->size) set_fp_value(v, size, to_double(MODE_NORMAL, v)); |
| } |
| } |
| else if (v->type_class == TYPE_CLASS_ARRAY && (flags & VAL_FLAG_S) != 0) { |
| if (text_sy == SY_SCOPE) { |
| Value x; |
| char * name = NULL; |
| if (flags & VAL_FLAG_L) { |
| unsigned i = 0; |
| unsigned j = 0; |
| unsigned size = (unsigned)v->size; |
| name = (char *)tmp_alloc_zero(size + 1); |
| for (i = 0; i + 3 < size; i += 4) { |
| unsigned ch = ((uint8_t *)v->value)[i + 0]; |
| ch |= (unsigned)((uint8_t *)v->value)[i + 1] << 8; |
| ch |= (unsigned)((uint8_t *)v->value)[i + 2] << 16; |
| ch |= (unsigned)((uint8_t *)v->value)[i + 3] << 24; |
| if (ch < 0x80) { |
| name[j++] = (char)ch; |
| } |
| else if (ch < 0x800) { |
| name[j++] = (char)((ch >> 6) | 0xc0); |
| name[j++] = (char)((ch & 0x3f) | 0x80); |
| } |
| else { |
| name[j++] = (char)((ch >> 12) | 0xe0); |
| name[j++] = (char)(((ch >> 6) & 0x3f) | 0x80); |
| name[j++] = (char)((ch & 0x3f) | 0x80); |
| } |
| } |
| } |
| else { |
| name = (char *)v->value; |
| } |
| if (identifier(mode, NULL, name, 0, &x) < 0) |
| error(ERR_INV_EXPRESSION, "Undefined identifier '%s'", name); |
| next_sy(); |
| qualified_name_expression(mode, &x, v); |
| } |
| else { |
| size_t size = 0; |
| Symbol * type = NULL; |
| if (get_std_type(flags & VAL_FLAG_L ? "wchar_t" : "char", TYPE_CLASS_UNKNOWN, &type, &size)) { |
| size_t def_size = flags & VAL_FLAG_L ? 4 : 1; |
| unsigned n = (unsigned)(v->size / def_size); |
| if (n > 0 && size > 0 && get_array_symbol(type, n, &type) >= 0) { |
| if (size != def_size) { |
| unsigned i; |
| uint8_t * buf = (uint8_t *)tmp_alloc_zero(size * n); |
| size_t min_size = size > def_size ? def_size : size; |
| for (i = 0; i < n; i++) { |
| memcpy(buf + i * size, (uint8_t *)v->value + i * def_size, min_size); |
| } |
| v->value = buf; |
| v->size = size * n; |
| } |
| v->type = type; |
| } |
| } |
| } |
| } |
| #endif |
| } |
| else if (text_sy == SY_SCOPE) { |
| Value x; |
| next_sy(); |
| memset(&x, 0, sizeof(x)); |
| qualified_name_expression(mode, &x, v); |
| } |
| else if (text_sy == SY_NAME || text_sy == SY_ID) { |
| qualified_name_expression(mode, NULL, v); |
| } |
| else { |
| error(ERR_INV_EXPRESSION, "Syntax error"); |
| } |
| } |
| |
| static void op_deref(int mode, Value * v) { |
| #if ENABLE_Symbols |
| Symbol * type = NULL; |
| if (mode == MODE_SKIP) return; |
| if (v->type_class != TYPE_CLASS_ARRAY && v->type_class != TYPE_CLASS_POINTER) { |
| error(ERR_INV_EXPRESSION, "Array or pointer type expected"); |
| } |
| if (v->type != NULL && get_symbol_base_type(v->type, &type) < 0) { |
| error(errno, "Cannot retrieve symbol type"); |
| } |
| if (type == NULL) { |
| error(ERR_OTHER, "Array or pointer base type is unknown"); |
| } |
| if (v->type_class == TYPE_CLASS_POINTER) { |
| if (v->loc && v->loc->pieces_cnt == 1 && v->loc->pieces->implicit_pointer) { |
| v->loc->pieces->implicit_pointer--; |
| assert(v->remote == 0); |
| assert(v->value == NULL); |
| } |
| else { |
| if (v->sym != NULL && v->size == 0 && get_symbol_size(v->sym, &v->size) < 0) { |
| error(errno, "Cannot retrieve symbol size"); |
| } |
| v->address = (ContextAddress)to_uns(mode, v); |
| v->remote = 1; |
| v->loc = NULL; |
| v->value = NULL; |
| v->constant = 0; |
| set_value_endianness(v, NULL, type); |
| } |
| v->sym_list = NULL; |
| v->sym = NULL; |
| v->reg = NULL; |
| } |
| v->type = type; |
| if (get_symbol_type_class(v->type, &v->type_class) < 0) { |
| error(errno, "Cannot retrieve symbol type class"); |
| } |
| if (get_symbol_size(v->type, &v->size) < 0) { |
| error(errno, "Cannot retrieve symbol size"); |
| } |
| set_value_props(v); |
| #else |
| error(ERR_UNSUPPORTED, "Symbols service not available"); |
| #endif |
| } |
| |
| #if ENABLE_Symbols |
| static void evaluate_symbol_location(Symbol * sym, ContextAddress obj_addr, |
| ContextAddress index, LocationExpressionState ** loc, int * big_endian) { |
| LocationInfo * loc_info = NULL; |
| StackFrame * frame_info = NULL; |
| uint64_t args[2]; |
| args[0] = obj_addr; |
| args[1] = index; |
| if (get_location_info(sym, &loc_info) < 0) { |
| error(errno, "Cannot get symbol location information"); |
| } |
| if (expression_frame != STACK_NO_FRAME && get_frame_info(expression_context, expression_frame, &frame_info) < 0) { |
| error(errno, "Cannot get stack frame info"); |
| } |
| *loc = evaluate_location_expression(expression_context, frame_info, |
| loc_info->value_cmds.cmds, loc_info->value_cmds.cnt, args, 2); |
| if (big_endian != NULL) *big_endian = loc_info->big_endian; |
| } |
| |
| static void find_field(int mode, |
| Symbol * class_sym, ContextAddress obj_addr, const char * name, const char * id, |
| Symbol ** field_sym, LocationExpressionState ** field_loc, int * big_endian) { |
| Symbol ** children = NULL; |
| Symbol ** inheritance = NULL; |
| int count = 0; |
| int h = 0; |
| int i; |
| |
| if (get_symbol_children(class_sym, &children, &count) < 0) { |
| error(errno, "Cannot retrieve field list"); |
| } |
| for (i = 0; i < count; i++) { |
| char * s = NULL; |
| int sym_class = 0; |
| if (get_symbol_class(children[i], &sym_class) < 0) { |
| error(errno, "Cannot retrieve field symbol class"); |
| } |
| if (get_symbol_name(children[i], &s) < 0) { |
| error(errno, "Cannot retrieve field name"); |
| } |
| if (s == NULL && sym_class == SYM_CLASS_REFERENCE) { |
| if (inheritance == NULL) inheritance = (Symbol **)tmp_alloc(sizeof(Symbol *) * count); |
| inheritance[h++] = children[i]; |
| } |
| if ((name != NULL && s != NULL && strcmp(s, name) == 0) || |
| (id != NULL && strcmp(symbol2id(children[i]), id) == 0)) { |
| if (mode == MODE_NORMAL) evaluate_symbol_location(children[i], obj_addr, 0, field_loc, big_endian); |
| *field_sym = children[i]; |
| return; |
| } |
| if (sym_class == SYM_CLASS_VARIANT_PART || sym_class == SYM_CLASS_VARIANT) { |
| find_field(mode, children[i], obj_addr, name, id, field_sym, field_loc, big_endian); |
| if (*field_sym != NULL) return; |
| } |
| } |
| for (i = 0; i < h; i++) { |
| ContextAddress addr = obj_addr; |
| if (mode == MODE_NORMAL) { |
| LocationExpressionState * x = NULL; |
| evaluate_symbol_location(inheritance[i], obj_addr, 0, &x, NULL); |
| if (x->stk_pos != 1) error(ERR_INV_EXPRESSION, "Cannot evaluate symbol address"); |
| addr = (ContextAddress)x->stk[0]; |
| } |
| find_field(mode, inheritance[i], addr, name, id, field_sym, field_loc, big_endian); |
| if (*field_sym != NULL) return; |
| } |
| } |
| #endif |
| |
| static void op_field(int mode, Value * v) { |
| char * id = NULL; |
| char * name = NULL; |
| if (text_sy == SY_ID) id = (char *)text_val.value; |
| else if (text_sy == SY_NAME) name = (char *)text_val.value; |
| else error(ERR_INV_EXPRESSION, "Field name expected"); |
| next_sy(); |
| if (mode == MODE_SKIP) return; |
| if (v->field_cb && name != NULL) { |
| v->field_cb(mode, v, name); |
| } |
| else if (v->type_class == TYPE_CLASS_COMPOSITE) { |
| #if ENABLE_Symbols |
| Symbol * sym = NULL; |
| int sym_class = 0; |
| LocationExpressionState * loc = NULL; |
| int big_endian = 0; |
| void * struct_value = NULL; |
| ContextAddress struct_size = 0; |
| |
| if (v->remote) { |
| find_field(mode, v->type, v->address, name, id, &sym, &loc, &big_endian); |
| } |
| else { |
| load_value(v); |
| struct_value = v->value; |
| struct_size = v->size; |
| find_field(mode, v->type, 0, name, id, &sym, &loc, &big_endian); |
| } |
| if (sym == NULL) { |
| error(ERR_SYM_NOT_FOUND, "Invalid field name or ID"); |
| } |
| if (get_symbol_class(sym, &sym_class) < 0) { |
| error(errno, "Cannot retrieve symbol class"); |
| } |
| if (get_symbol_type(sym, &v->type) < 0) { |
| error(errno, "Cannot retrieve symbol type"); |
| } |
| v->reg = NULL; |
| v->sym = NULL; |
| v->sym_list = NULL; |
| if (sym_class == SYM_CLASS_FUNCTION) { |
| ContextAddress addr = 0; |
| if (mode == MODE_NORMAL) { |
| if (loc->stk_pos != 1) error(ERR_INV_EXPRESSION, "Invalid symbol location expression"); |
| addr = (ContextAddress)loc->stk[0]; |
| } |
| v->type_class = TYPE_CLASS_POINTER; |
| if (v->type != NULL) get_array_symbol(v->type, 0, &v->type); |
| set_ctx_word_value(v, addr); |
| v->function = 1; |
| v->sym = sym; |
| } |
| else { |
| if (get_symbol_type_class(sym, &v->type_class) < 0) { |
| error(errno, "Cannot retrieve symbol type class"); |
| } |
| if (get_symbol_size(sym, &v->size) < 0) { |
| error(errno, "Cannot retrieve field size"); |
| } |
| if (mode == MODE_NORMAL) { |
| if (struct_value == NULL && loc->pieces_cnt > 0) { |
| size_t size = 0; |
| void * value = NULL; |
| StackFrame * frame_info = NULL; |
| if (expression_frame != STACK_NO_FRAME && get_frame_info(expression_context, expression_frame, &frame_info) < 0) { |
| error(errno, "Cannot get stack frame info"); |
| } |
| read_location_pieces(expression_context, frame_info, |
| loc->pieces, loc->pieces_cnt, big_endian, &value, &size); |
| if (size > v->size) size = (size_t)v->size; |
| set_value(v, value, size, big_endian); |
| sign_extend(v, loc); |
| } |
| else { |
| if (loc->stk_pos != 1) error(ERR_OTHER, "Invalid location expression"); |
| if (struct_value != NULL) { |
| if (loc->stk[0] + v->size > struct_size) error(ERR_OTHER, "Invalid location expression"); |
| v->value = (uint8_t *)struct_value + (size_t)loc->stk[0]; |
| assert(!v->remote); |
| } |
| else { |
| v->address = (ContextAddress)loc->stk[0]; |
| assert(v->remote); |
| } |
| set_value_endianness(v, sym, v->type); |
| } |
| } |
| } |
| v->loc = loc; |
| if (sym_class == SYM_CLASS_REFERENCE && mode == MODE_NORMAL) { |
| check_hidden_redirection(v); |
| } |
| set_value_props(v); |
| #else |
| error(ERR_UNSUPPORTED, "Symbols service not available"); |
| #endif |
| } |
| else if (v->reg != NULL) { |
| if (id != NULL) { |
| Context * ctx = NULL; |
| int frame = STACK_NO_FRAME; |
| RegisterDefinition * def = NULL; |
| if (id2register(id, &ctx, &frame, &def) < 0) exception(errno); |
| if (frame == STACK_TOP_FRAME) frame = expression_frame; |
| reg2value(mode, ctx, frame, def, v); |
| } |
| else { |
| RegisterDefinition * def = get_reg_definitions(v->loc->ctx); |
| if (def != NULL) { |
| while (def->name != NULL) { |
| if (def->parent == v->reg && strcmp(name, def->name) == 0) { |
| int frame = STACK_NO_FRAME; |
| if (v->loc->ctx == expression_context) frame = expression_frame; |
| reg2value(mode, v->loc->ctx, frame, def, v); |
| return; |
| } |
| def++; |
| } |
| } |
| error(ERR_INV_EXPRESSION, "Unknown register: %s", name); |
| } |
| } |
| else { |
| error(ERR_INV_EXPRESSION, "Composite type expected"); |
| } |
| } |
| |
| static void op_index(int mode, Value * v) { |
| #if ENABLE_Symbols |
| Value i; |
| ContextAddress size = 0; |
| Symbol * type = NULL; |
| int type_class = 0; |
| |
| expression(mode, &i); |
| if (mode == MODE_SKIP) return; |
| |
| if (v->type_class != TYPE_CLASS_ARRAY && v->type_class != TYPE_CLASS_POINTER) { |
| error(ERR_INV_EXPRESSION, "Array or pointer expected"); |
| } |
| if (v->type == NULL) { |
| error(ERR_INV_EXPRESSION, "Value type is unknown"); |
| } |
| if (get_symbol_base_type(v->type, &type) < 0) { |
| error(errno, "Cannot get array element type"); |
| } |
| if (type == NULL) { |
| error(ERR_INV_EXPRESSION, "Array element type is unknown"); |
| } |
| if (get_symbol_type_class(type, &type_class) < 0) { |
| error(errno, "Cannot get type class of the array element"); |
| } |
| if (v->type_class == TYPE_CLASS_POINTER) { |
| if (v->loc && v->loc->pieces_cnt == 1 && v->loc->pieces->implicit_pointer) { |
| v->loc->pieces->implicit_pointer--; |
| } |
| else { |
| /* Note: v->size is not set yet if v->sym != NULL */ |
| if (v->sym != NULL && v->size == 0 && get_symbol_size(v->sym, &v->size) < 0) { |
| error(errno, "Cannot retrieve symbol size"); |
| } |
| v->address = (ContextAddress)to_uns(mode, v); |
| v->remote = 1; |
| v->sym_list = NULL; |
| v->sym = NULL; |
| v->reg = NULL; |
| v->loc = NULL; |
| v->value = NULL; |
| v->constant = 0; |
| set_value_endianness(v, NULL, type); |
| } |
| } |
| if (get_symbol_size(type, &size) < 0) { |
| error(errno, "Cannot get array element size"); |
| } |
| |
| if (mode == MODE_NORMAL) { |
| int64_t index = to_int(mode, &i); |
| ContextAddress byte_offs = 0; |
| ContextAddress bit_offs = 0; |
| int64_t lower_bound = 0; |
| if (v->type_class == TYPE_CLASS_ARRAY) { |
| if (get_symbol_lower_bound(v->type, &lower_bound) < 0) { |
| error(errno, "Cannot get array lower bound"); |
| } |
| if (index < lower_bound) { |
| error(ERR_INV_EXPRESSION, "Invalid index"); |
| } |
| } |
| if (v->bit_stride > 0) { |
| bit_offs = (ContextAddress)(index - lower_bound) * v->bit_stride; |
| } |
| else { |
| byte_offs = (ContextAddress)(index - lower_bound) * size; |
| } |
| if (v->remote && v->bit_stride == 0) { |
| assert(bit_offs == 0); |
| v->address += byte_offs; |
| } |
| else { |
| if (v->sym != NULL && v->size == 0 && get_symbol_size(v->sym, &v->size) < 0) { |
| error(errno, "Cannot retrieve symbol size"); |
| } |
| load_value(v); |
| v->value = (char *)v->value + byte_offs; |
| if (v->bit_stride > 0) { |
| unsigned x; |
| uint8_t * buf = (uint8_t *)tmp_alloc_zero((size_t)size); |
| uint8_t * val = (uint8_t *)v->value; |
| unsigned buf_offs = v->big_endian ? (unsigned)(size * 8 - v->bit_stride) : 0; |
| v->value = buf; |
| for (x = 0; x < v->bit_stride; x++) { |
| unsigned y = (unsigned)(x + bit_offs); |
| unsigned z = (unsigned)(x + buf_offs); |
| if (val[y / 8] & bit_mask(v, y)) buf[z / 8] |= bit_mask(v, z); |
| } |
| if (type_class == TYPE_CLASS_INTEGER) { |
| /* Sign extension */ |
| bit_sign_extend(v, v->bit_stride); |
| } |
| } |
| } |
| } |
| v->sym_list = NULL; |
| v->sym = NULL; |
| v->reg = NULL; |
| v->loc = NULL; |
| v->size = size; |
| v->type = type; |
| v->type_class = type_class; |
| set_value_props(v); |
| #else |
| error(ERR_UNSUPPORTED, "Symbols service not available"); |
| #endif |
| } |
| |
| static void op_addr(int mode, Value * v) { |
| if (mode == MODE_SKIP) return; |
| if (v->function) { |
| assert(v->type_class == TYPE_CLASS_POINTER); |
| } |
| else if (v->remote) { |
| set_ctx_word_value(v, v->address); |
| v->type_class = TYPE_CLASS_POINTER; |
| v->constant = 0; |
| #if ENABLE_Symbols |
| if (v->type != NULL) { |
| if (get_array_symbol(v->type, 0, &v->type)) { |
| error(errno, "Cannot get pointer type"); |
| } |
| } |
| #else |
| v->type = NULL; |
| #endif |
| } |
| else if (v->reg != NULL && v->reg->memory_context != NULL) { |
| Context * ctx = id2ctx(v->reg->memory_context); |
| set_ctx_word_value(v, v->reg->memory_address); |
| v->type_class = TYPE_CLASS_POINTER; |
| v->constant = 1; |
| if (ctx != expression_context) { |
| /* The address is in another address space */ |
| v->loc = (LocationExpressionState *)tmp_alloc_zero(sizeof(LocationExpressionState)); |
| v->loc->ctx = ctx; |
| v->loc->pieces = (LocationPiece *)tmp_alloc_zero(sizeof(LocationPiece)); |
| v->loc->pieces_cnt = v->loc->pieces_max = 1; |
| v->loc->pieces->value = v->value; |
| v->loc->pieces->size = (size_t)v->size; |
| } |
| } |
| else if (v->loc != NULL && v->loc->pieces_cnt == 1 && |
| v->loc->pieces->implicit_pointer == 0 && v->loc->pieces->optimized_away == 0 && |
| v->loc->pieces->reg == NULL && v->loc->pieces->value == NULL && v->loc->pieces->bit_offs == 0) { |
| set_ctx_word_value(v, v->loc->pieces->addr); |
| v->type_class = TYPE_CLASS_POINTER; |
| v->constant = 0; |
| v->type = NULL; |
| } |
| else { |
| error(ERR_INV_EXPRESSION, "Invalid '&': the value has no address"); |
| } |
| } |
| |
| static void unary_expression(int mode, Value * v); |
| |
| static void op_sizeof(int mode, Value * v) { |
| Symbol * type = NULL; |
| int pos = 0; |
| int p = text_sy == '('; |
| |
| if (p) next_sy(); |
| pos = sy_pos; |
| if (type_name(mode, &type) && (!p || text_sy == ')')) { |
| if (mode != MODE_SKIP) { |
| ContextAddress type_size = 0; |
| Symbol * s_type = NULL; |
| size_t s_size = 0; |
| #if ENABLE_Symbols |
| if (get_symbol_size(type, &type_size) < 0) { |
| error(errno, "Cannot retrieve symbol size"); |
| } |
| get_std_type("size_t", TYPE_CLASS_CARDINAL, &s_type, &s_size); |
| #endif |
| v->type = s_type; |
| v->type_class = TYPE_CLASS_CARDINAL; |
| if (s_size == 0) s_size = context_word_size(expression_context); |
| set_int_value(v, s_size, type_size); |
| v->constant = 1; |
| } |
| } |
| else { |
| text_pos = pos; |
| next_ch(); |
| next_sy(); |
| unary_expression(mode == MODE_NORMAL ? MODE_TYPE : mode, v); |
| if (mode != MODE_SKIP) { |
| Symbol * s_type = NULL; |
| size_t s_size = 0; |
| #if ENABLE_Symbols |
| get_std_type("size_t", TYPE_CLASS_CARDINAL, &s_type, &s_size); |
| #endif |
| v->type = s_type; |
| v->type_class = TYPE_CLASS_CARDINAL; |
| if (s_size == 0) s_size = context_word_size(expression_context); |
| set_int_value(v, s_size, v->size); |
| v->constant = 1; |
| } |
| } |
| if (p) { |
| if (text_sy != ')') error(ERR_INV_EXPRESSION, "')' expected"); |
| next_sy(); |
| } |
| } |
| |
| static void funccall_error(const char * msg) { |
| set_errno(ERR_OTHER, msg); |
| set_errno(errno, "Cannot inject a function call"); |
| exception(errno); |
| } |
| |
| #if ENABLE_FuncCallInjection |
| |
| static void free_funccall_state(FuncCallState * state) { |
| assert(!state->started || state->intercepted); |
| assert(state->committed); |
| assert(state->regs_cnt == 0 || state->error); |
| list_remove(&state->link_all); |
| if (state->bp) destroy_eventpoint(state->bp); |
| context_unlock(state->ctx); |
| release_error_report(state->error); |
| cache_dispose(&state->cache); |
| loc_free(state->ret_value); |
| loc_free(state->args); |
| loc_free(state->cmds); |
| loc_free(state->regs); |
| loc_free(state); |
| } |
| |
| static void funcccall_breakpoint(Context * ctx, void * args) { |
| Trap trap; |
| FuncCallState * state = (FuncCallState *)args; |
| assert(state->ctx == ctx); |
| assert(state->started); |
| assert(!state->finished); |
| ctx->stopped_by_funccall = 1; |
| if (set_trap(&trap)) { |
| if (!state->intercepted && state->cmds_cnt > 0) { |
| /* Execute after call commands */ |
| StackFrame * frame_info = NULL; |
| LocationExpressionState * vm = NULL; |
| if (get_frame_info(ctx, STACK_TOP_FRAME, &frame_info) < 0) exception(errno); |
| vm = evaluate_location_expression(ctx, frame_info, |
| state->cmds, state->cmds_cnt, NULL, 0); |
| state->cmds_cnt = 0; |
| |
| /* Read function call returned value */ |
| if (vm->pieces_cnt > 0) { |
| void * value = NULL; |
| read_location_pieces(ctx, frame_info, vm->pieces, vm->pieces_cnt, |
| state->ret_big_endian, &value, &state->ret_size); |
| state->ret_value = loc_alloc_zero(state->ret_size); |
| memcpy(state->ret_value, value, state->ret_size); |
| } |
| else if (vm->stk_pos > 0) { |
| state->ret_size = sizeof(uint64_t); |
| state->ret_value = loc_alloc_zero(state->ret_size); |
| memcpy(state->ret_value, vm->stk + vm->stk_pos - 1, state->ret_size); |
| } |
| } |
| if (state->regs_cnt > 0) { |
| /* Restore registers */ |
| unsigned i; |
| unsigned offs = 0; |
| for (i = 0; i < state->regs_cnt; i++) { |
| RegisterDefinition * r = state->regs[i]; |
| if (context_write_reg(ctx, r, 0, r->size, state->regs_data + offs) < 0) exception(errno); |
| #if SERVICE_Registers |
| send_event_register_changed(register2id(ctx, STACK_TOP_FRAME, r)); |
| #endif |
| offs += r->size; |
| } |
| state->regs_cnt = 0; |
| } |
| clear_trap(&trap); |
| } |
| else { |
| release_error_report(state->error); |
| state->error = get_error_report(trap.error); |
| } |
| if (!state->intercepted) { |
| assert(!state->finished); |
| state->finished = 1; |
| suspend_debug_context(ctx); |
| } |
| else if (state->committed) { |
| if (state->error) trace(LOG_ALWAYS, "Cannot restore state", |
| errno_to_str(set_error_report_errno(state->error))); |
| free_funccall_state(state); |
| } |
| } |
| |
| static void funccall_check_recursion(uint64_t ret_addr) { |
| LINK * l = func_call_state.next; |
| while (l != &func_call_state) { |
| FuncCallState * state = link_all2fc(l); |
| if (state->started && !state->finished && |
| state->ctx == expression_context && state->ret_addr == ret_addr) { |
| funccall_error("Recursive invocation"); |
| } |
| l = l->next; |
| } |
| } |
| |
| static void op_funccall(int mode, Value * v) { |
| unsigned id = cache_transaction_id(); |
| FuncCallState * state = NULL; |
| Symbol * func = NULL; |
| int type_class = 0; |
| LINK * l; |
| |
| if (!context_has_state(expression_context)) funccall_error("Context is not a thread"); |
| if (is_safe_event()) funccall_error("Called from safe event handler"); |
| |
| for (l = func_call_state.next; l != &func_call_state; l = l->next) { |
| FuncCallState * s = link_all2fc(l); |
| if (s->id == id && s->pos == text_pos) { |
| state = s; |
| break; |
| } |
| } |
| |
| if (state != NULL && state->started && !state->intercepted) cache_wait(&state->cache); |
| if (state == NULL) { |
| state = (FuncCallState *)loc_alloc_zero(sizeof(FuncCallState)); |
| state->id = id; |
| state->pos = text_pos; |
| context_lock(state->ctx = expression_context); |
| list_add_first(&state->link_all, &func_call_state); |
| } |
| if (v->function) { |
| func = v->sym; |
| } |
| else if (v->type != NULL && v->type_class == TYPE_CLASS_POINTER) { |
| if (get_symbol_base_type(v->type, &func) < 0) { |
| error(errno, "Cannot retrieve symbol base type"); |
| } |
| } |
| if (func != NULL && get_symbol_type_class(func, &type_class) < 0) { |
| error(errno, "Cannot retrieve symbol type class"); |
| } |
| if (type_class != TYPE_CLASS_FUNCTION) { |
| error(ERR_INV_EXPRESSION, "Invalid '()': not a function"); |
| } |
| if (get_symbol_address(func, &state->func_addr) < 0) { |
| error(errno, "Cannot retrieve function address"); |
| } |
| state->args_cnt = 0; |
| if (text_sy != ')') { |
| int args_mode = mode; |
| if (state->started) args_mode = MODE_SKIP; |
| for (;;) { |
| if (state->args_cnt >= state->args_max) { |
| state->args_max += 8; |
| state->args = (Value *)loc_realloc(state->args, sizeof(Value) * state->args_max); |
| } |
| ini_value(state->args + state->args_cnt); |
| expression(args_mode, state->args + state->args_cnt++); |
| if (text_sy != ',') break; |
| next_sy(); |
| } |
| } |
| if (get_symbol_base_type(func, &v->type) < 0) { |
| error(errno, "Cannot retrieve function return type"); |
| } |
| if (get_symbol_type_class(v->type, &v->type_class) < 0) { |
| error(errno, "Cannot retrieve function return type class"); |
| } |
| if (get_symbol_size(v->type, &v->size) < 0) { |
| error(errno, "Cannot retrieve function return value size"); |
| } |
| expression_has_func_call = 1; |
| if (mode == MODE_NORMAL) { |
| if (!state->started) { |
| unsigned i; |
| StackFrame * frame_info = NULL; |
| FunctionCallInfo * call_info = NULL; |
| LocationExpressionState * vm = NULL; |
| RegisterDefinition * reg_pc = get_PC_definition(state->ctx); |
| const Symbol ** arg_types = (const Symbol **)tmp_alloc_zero(sizeof(Symbol *) * state->args_cnt); |
| uint64_t * arg_vals = (uint64_t *)tmp_alloc_zero(sizeof(uint64_t) * (FUNCCALL_ARG_ARGS + state->args_cnt)); |
| uint64_t sp = 0; |
| |
| if (get_frame_info(state->ctx, STACK_TOP_FRAME, &frame_info) < 0) exception(errno); |
| if (read_reg_value(frame_info, reg_pc, &state->ret_addr) < 0) exception(errno); |
| funccall_check_recursion(state->ret_addr); |
| for (i = 0; i < state->args_cnt; i++) arg_types[i] = state->args[i].type; |
| if (get_funccall_info(func, arg_types, state->args_cnt, &call_info) < 0) exception(errno); |
| |
| /* Save registers */ |
| if (call_info->saveregs_cnt > 0) { |
| unsigned offs = 0; |
| for (i = 0; i < call_info->saveregs_cnt; i++) offs += call_info->saveregs[i]->size; |
| state->regs = (RegisterDefinition **)loc_alloc(sizeof(RegisterDefinition *) * call_info->saveregs_cnt); |
| state->regs_data = (uint8_t *)loc_alloc_zero(offs); |
| state->regs_cnt = call_info->saveregs_cnt; |
| offs = 0; |
| for (i = 0; i < call_info->saveregs_cnt; i++) { |
| RegisterDefinition * r = call_info->saveregs[i]; |
| state->regs[i] = r; |
| if (context_read_reg(state->ctx, r, 0, r->size, state->regs_data + offs) < 0) exception(errno); |
| offs += r->size; |
| } |
| } |
| |
| /* get values of actual arguments */ |
| arg_vals[FUNCCALL_ARG_ADDR] = state->func_addr; |
| arg_vals[FUNCCALL_ARG_RET] = state->ret_addr; |
| if (read_reg_value(frame_info, call_info->stak_pointer, &sp) < 0) exception(errno); |
| sp -= call_info->red_zone_size; |
| for (i = 0; i < state->args_cnt; i++) { |
| Value * v = state->args + i; |
| switch (v->type_class) { |
| case TYPE_CLASS_CARDINAL: |
| case TYPE_CLASS_INTEGER: |
| case TYPE_CLASS_POINTER: |
| case TYPE_CLASS_ENUMERATION: |
| arg_vals[FUNCCALL_ARG_ARGS + i] = to_uns(MODE_NORMAL, v); |
| break; |
| default: |
| if (v->remote) { |
| arg_vals[FUNCCALL_ARG_ARGS + i] = v->address; |
| } |
| else { |
| sp -= v->size; |
| while (sp % 8) sp--; |
| if (context_write_mem(state->ctx, (ContextAddress)sp, |
| v->value, (size_t)v->size) < 0) exception(errno); |
| arg_vals[FUNCCALL_ARG_ARGS + i] = sp; |
| } |
| break; |
| } |
| } |
| if (write_reg_value(frame_info, call_info->stak_pointer, sp) < 0) exception(errno); |
| |
| /* Execute call injection commands */ |
| state->started = 1; |
| vm = evaluate_location_expression(state->ctx, frame_info, |
| call_info->cmds, call_info->cmds_cnt, arg_vals, FUNCCALL_ARG_ARGS + state->args_cnt); |
| state->ret_big_endian = call_info->scope.big_endian; |
| if (vm->sft_cmd != NULL) { |
| char ret_addr[64]; |
| if (vm->sft_cmd->cmd != SFT_CMD_FCALL || vm->stk_pos != 0) { |
| funccall_error("Invalid SFT instruction"); |
| } |
| |
| /* Create breakpoint at the function return address */ |
| assert(state->bp == NULL); |
| snprintf(ret_addr, sizeof(ret_addr), "0x%" PRIX64, state->ret_addr); |
| state->bp = create_eventpoint(ret_addr, state->ctx, funcccall_breakpoint, state); |
| |
| /* Set PC to the function address */ |
| if (write_reg_value(frame_info, reg_pc, state->func_addr) < 0) exception(errno); |
| state->ctx->stopped_by_bp = 0; |
| |
| /* Save rest of func call commands to be executed after the function returns */ |
| state->cmds_cnt = call_info->cmds_cnt - (vm->sft_cmd - call_info->cmds) - 1; |
| state->cmds = (LocationExpressionCommand *)loc_alloc(sizeof(LocationExpressionCommand) * state->cmds_cnt); |
| memcpy(state->cmds, vm->sft_cmd + 1, sizeof(LocationExpressionCommand) * state->cmds_cnt); |
| |
| /* Resume debug context */ |
| if (continue_debug_context(state->ctx, cache_channel(), RM_RESUME, 1, 0, 0) < 0) exception(errno); |
| |
| /* Wait until the function returns */ |
| cache_wait(&state->cache); |
| } |
| else { |
| state->finished = 1; |
| } |
| } |
| assert(state->started); |
| assert(state->intercepted); |
| if (state->error) exception(set_error_report_errno(state->error)); |
| set_value(v, state->ret_value, state->ret_size, state->ret_big_endian); |
| } |
| else { |
| set_value(v, NULL, (size_t)v->size, 0); |
| } |
| set_value_props(v); |
| } |
| |
| #else |
| |
| static void op_funccall(int mode, Value * v) { |
| funccall_error("Symbols service not available"); |
| } |
| |
| #endif /* ENABLE_FuncCallInjection */ |
| |
| static void op_call(int mode, Value * v) { |
| if (v->func_cb || mode == MODE_SKIP) { |
| Value * args = NULL; |
| unsigned args_cnt = 0; |
| unsigned args_max = 0; |
| |
| if (text_sy != ')') { |
| for (;;) { |
| if (args_cnt == args_max) { |
| args_max += 32; |
| args = (Value *)tmp_realloc(args, sizeof(Value) * args_max); |
| } |
| ini_value(args + args_cnt); |
| expression(mode, args + args_cnt++); |
| if (text_sy != ',') break; |
| next_sy(); |
| } |
| } |
| if (mode == MODE_SKIP) { |
| ini_value(v); |
| return; |
| } |
| if (mode == MODE_NORMAL) { |
| unsigned i; |
| for (i = 0; i < args_cnt; i++) load_value(args + i); |
| } |
| v->func_cb(mode, v, args, args_cnt); |
| } |
| else { |
| op_funccall(mode, v); |
| } |
| } |
| |
| static void resolve_ref_type(int mode, Value * v) { |
| #if ENABLE_Symbols |
| Symbol * type = v->type; |
| if (type == NULL || v->type_class != TYPE_CLASS_POINTER) return; |
| for (;;) { |
| SYM_FLAGS flags = 0; |
| Symbol * next = NULL; |
| if (get_symbol_flags(type, &flags) < 0) { |
| error(errno, "Cannot retrieve symbol flags"); |
| } |
| if (flags & SYM_FLAG_REFERENCE) { |
| op_deref(mode, v); |
| break; |
| } |
| if (get_symbol_type(type, &next) < 0) { |
| error(errno, "Cannot retrieve symbol type"); |
| } |
| if (next == type) break; |
| type = next; |
| } |
| #endif |
| } |
| |
| static void postfix_expression(int mode, Value * v) { |
| primary_expression(mode, v); |
| for (;;) { |
| resolve_ref_type(mode, v); |
| if (text_sy == '.') { |
| next_sy(); |
| op_field(mode, v); |
| } |
| else if (text_sy == '[') { |
| next_sy(); |
| op_index(mode, v); |
| if (text_sy != ']') { |
| error(ERR_INV_EXPRESSION, "']' expected"); |
| } |
| next_sy(); |
| } |
| else if (text_sy == SY_REF) { |
| next_sy(); |
| op_deref(mode, v); |
| resolve_ref_type(mode, v); |
| op_field(mode, v); |
| } |
| else if (text_sy == '(') { |
| next_sy(); |
| op_call(mode, v); |
| if (text_sy != ')') { |
| error(ERR_INV_EXPRESSION, "')' expected"); |
| } |
| next_sy(); |
| } |
| else { |
| if (v->func_cb) error(ERR_INV_EXPRESSION, "'(' expected"); |
| break; |
| } |
| } |
| } |
| |
| static void set_bool_value(Value * v, uint64_t n) { |
| Symbol * type = NULL; |
| int type_class = TYPE_CLASS_ENUMERATION; |
| size_t size = 0; |
| #if ENABLE_Symbols |
| if (!get_std_type("bool", TYPE_CLASS_ENUMERATION, &type, &size)) { |
| get_std_type("int", TYPE_CLASS_INTEGER, &type, &size); |
| type_class = TYPE_CLASS_INTEGER; |
| } |
| #endif |
| if (size == 0) size = context_word_size(expression_context); |
| v->type = type; |
| v->type_class = type_class; |
| set_int_value(v, size, n); |
| } |
| |
| /* Note: lazy_unary_expression() does not set v->size if v->sym != NULL */ |
| static void lazy_unary_expression(int mode, Value * v) { |
| switch (text_sy) { |
| case '*': |
| next_sy(); |
| lazy_unary_expression(mode, v); |
| op_deref(mode, v); |
| break; |
| case '&': |
| next_sy(); |
| lazy_unary_expression(mode, v); |
| op_addr(mode, v); |
| break; |
| case SY_SIZEOF: |
| next_sy(); |
| op_sizeof(mode, v); |
| break; |
| case '+': |
| next_sy(); |
| lazy_unary_expression(mode, v); |
| break; |
| case '-': |
| next_sy(); |
| unary_expression(mode, v); |
| if (mode != MODE_SKIP) { |
| if (!is_number(v)) { |
| error(ERR_INV_EXPRESSION, "Numeric types expected"); |
| } |
| else if (v->type_class == TYPE_CLASS_REAL) { |
| set_fp_value(v, (size_t)v->size, -to_double(mode, v)); |
| } |
| else if (is_real_number(v)) { |
| double n = -to_double(mode, v); |
| v->type = NULL; |
| v->type_class = TYPE_CLASS_REAL; |
| set_fp_value(v, sizeof(double), n); |
| set_fp_type(v); |
| } |
| else if (v->type_class == TYPE_CLASS_COMPLEX) { |
| set_complex_value(v, (size_t)v->size, -to_r_double(mode, v), -to_i_double(mode, v)); |
| } |
| else if (v->type_class != TYPE_CLASS_CARDINAL) { |
| int64_t value = -to_int(mode, v); |
| if (v->type_class == TYPE_CLASS_INTEGER) { |
| set_int_value(v, (size_t)v->size, value); |
| } |
| else { |
| v->type_class = TYPE_CLASS_INTEGER; |
| set_int_value(v, context_word_size(expression_context), value); |
| v->type = NULL; |
| } |
| } |
| assert(!v->remote); |
| } |
| break; |
| case '!': |
| next_sy(); |
| unary_expression(mode, v); |
| if (mode != MODE_SKIP) { |
| if (!is_whole_number(v)) { |
| error(ERR_INV_EXPRESSION, "Integral types expected"); |
| } |
| else { |
| set_bool_value(v, !to_int(mode, v)); |
| } |
| assert(!v->remote); |
| } |
| break; |
| case '~': |
| next_sy(); |
| #if ENABLE_Symbols |
| /* Check for C++ destructor */ |
| if (text_sy == SY_NAME) { |
| Value type; |
| int sym_class = identifier(mode, NULL, (char *)text_val.value, SYM_FLAG_TYPE, &type); |
| if (sym_class == SYM_CLASS_TYPE && type.type_class == TYPE_CLASS_COMPOSITE) { |
| char * name = tmp_strdup2("~", (char *)text_val.value); |
| sym_class = identifier(mode, &type, name, 0, v); |
| if (sym_class < 0) error(ERR_INV_EXPRESSION, "Undefined identifier '%s'", name); |
| next_sy(); |
| break; |
| } |
| } |
| #endif |
| unary_expression(mode, v); |
| if (mode != MODE_SKIP) { |
| if (!is_whole_number(v)) { |
| error(ERR_INV_EXPRESSION, "Integral types expected"); |
| } |
| else { |
| int64_t value = ~to_int(mode, v); |
| set_int_value(v, (size_t)v->size, value); |
| } |
| assert(!v->remote); |
| } |
| break; |
| #if ENABLE_Symbols |
| case '(': |
| { |
| Symbol * type = NULL; |
| int type_class = TYPE_CLASS_UNKNOWN; |
| ContextAddress type_size = 0; |
| int pos = sy_pos; |
| |
| assert(text[pos] == '('); |
| next_sy(); |
| if (!type_name(mode, &type)) { |
| text_pos = pos; |
| next_ch(); |
| next_sy(); |
| assert(text_sy == '('); |
| postfix_expression(mode, v); |
| break; |
| } |
| if (text_sy != ')') error(ERR_INV_EXPRESSION, "')' expected"); |
| next_sy(); |
| unary_expression(mode, v); |
| if (mode == MODE_SKIP) break; |
| if (get_symbol_type_class(type, &type_class) < 0) { |
| error(errno, "Cannot retrieve symbol type class"); |
| } |
| if (get_symbol_size(type, &type_size) < 0) { |
| error(errno, "Cannot retrieve symbol size"); |
| } |
| if (v->remote && v->size == type_size) { |
| /* A type cast can be an l-value expression as long as the size does not change */ |
| int ok = 0; |
| switch (type_class) { |
| case TYPE_CLASS_CARDINAL: |
| case TYPE_CLASS_POINTER: |
| case TYPE_CLASS_INTEGER: |
| case TYPE_CLASS_ENUMERATION: |
| case TYPE_CLASS_COMPOSITE: |
| switch (v->type_class) { |
| case TYPE_CLASS_CARDINAL: |
| case TYPE_CLASS_POINTER: |
| case TYPE_CLASS_INTEGER: |
| case TYPE_CLASS_ENUMERATION: |
| case TYPE_CLASS_UNKNOWN: |
| case TYPE_CLASS_COMPOSITE: |
| ok = 1; |
| break; |
| } |
| break; |
| case TYPE_CLASS_REAL: |
| ok = v->type_class == TYPE_CLASS_REAL; |
| break; |
| } |
| if (ok) { |
| v->type = type; |
| v->type_class = type_class; |
| break; |
| } |
| } |
| switch (type_class) { |
| case TYPE_CLASS_UNKNOWN: |
| error(ERR_INV_EXPRESSION, "Unknown type class"); |
| break; |
| case TYPE_CLASS_CARDINAL: |
| case TYPE_CLASS_POINTER: |
| { |
| uint64_t value = to_uns(mode, v); |
| v->type = type; |
| v->type_class = type_class; |
| set_int_value(v, (size_t)type_size, value); |
| } |
| break; |
| case TYPE_CLASS_INTEGER: |
| case TYPE_CLASS_ENUMERATION: |
| { |
| int64_t value = to_int(mode, v); |
| v->type = type; |
| v->type_class = type_class; |
| set_int_value(v, (size_t)type_size, value); |
| } |
| break; |
| case TYPE_CLASS_REAL: |
| { |
| double value = to_double(mode, v); |
| v->type = type; |
| v->type_class = type_class; |
| set_fp_value(v, (size_t)type_size, value); |
| set_fp_type(v); |
| } |
| break; |
| case TYPE_CLASS_ARRAY: |
| if (v->type_class == TYPE_CLASS_POINTER) { |
| if (v->loc && v->loc->pieces_cnt == 1 && v->loc->pieces->implicit_pointer) { |
| v->loc->pieces->implicit_pointer--; |
| } |
| else { |
| v->address = (ContextAddress)to_uns(mode, v); |
| v->remote = 1; |
| v->sym_list = NULL; |
| v->sym = NULL; |
| v->reg = NULL; |
| v->loc = NULL; |
| v->value = NULL; |
| v->size = type_size; |
| v->big_endian = expression_context->big_endian; |
| v->constant = 0; |
| } |
| v->type = type; |
| v->type_class = type_class; |
| } |
| else if (v->remote) { |
| v->sym_list = NULL; |
| v->sym = NULL; |
| v->type = type; |
| v->size = type_size; |
| v->type_class = type_class; |
| } |
| else { |
| error(ERR_INV_EXPRESSION, "Invalid type cast: illegal source type"); |
| } |
| break; |
| default: |
| error(ERR_INV_EXPRESSION, "Invalid type cast: illegal destination type"); |
| break; |
| } |
| break; |
| } |
| #endif |
| default: |
| postfix_expression(mode, v); |
| break; |
| } |
| } |
| |
| static void unary_expression(int mode, Value * v) { |
| lazy_unary_expression(mode, v); |
| #if ENABLE_Symbols |
| if (mode != MODE_SKIP && v->sym != NULL && v->size == 0 && get_symbol_size(v->sym, &v->size) < 0) { |
| error(errno, "Cannot retrieve symbol size"); |
| } |
| #endif |
| } |
| |
| static void pm_expression(int mode, Value * v) { |
| unary_expression(mode, v); |
| #if ENABLE_Symbols |
| while (text_sy == SY_PM_D || text_sy == SY_PM_R) { |
| Value x; |
| int sy = text_sy; |
| next_sy(); |
| unary_expression(mode, &x); |
| if (x.type == NULL || x.type_class != TYPE_CLASS_MEMBER_PTR) { |
| error(ERR_INV_EXPRESSION, "Invalid type: pointer to member expected"); |
| } |
| if (mode != MODE_SKIP) { |
| if (mode == MODE_NORMAL) { |
| ContextAddress obj = 0; |
| ContextAddress ptr = 0; |
| LocationExpressionState * loc = NULL; |
| if (sy == SY_PM_D) { |
| if (!v->remote) error(ERR_INV_EXPRESSION, "L-value expected"); |
| obj = v->address; |
| } |
| else { |
| obj = (ContextAddress)to_uns(mode, v); |
| } |
| ptr = (ContextAddress)to_uns(mode, &x); |
| evaluate_symbol_location(x.type, obj, ptr, &loc, NULL); |
| if (loc->stk_pos != 1) error(ERR_INV_EXPRESSION, "Cannot evaluate symbol address"); |
| v->address = (ContextAddress)loc->stk[0]; |
| } |
| else { |
| v->address = 0; |
| } |
| v->sym_list = NULL; |
| v->sym = NULL; |
| v->reg = NULL; |
| v->loc = NULL; |
| v->remote = 1; |
| v->function = 0; |
| v->value = NULL; |
| v->constant = 0; |
| if (get_symbol_base_type(x.type, &v->type) < 0) { |
| error(ERR_INV_EXPRESSION, "Cannot get pointed type"); |
| } |
| if (get_symbol_type_class(v->type, &v->type_class) < 0) { |
| error(ERR_INV_EXPRESSION, "Cannot get pointed type class"); |
| } |
| if (get_symbol_size(v->type, &v->size) < 0) { |
| error(errno, "Cannot retrieve field size"); |
| } |
| set_value_endianness(v, x.type, v->type); |
| } |
| } |
| #endif |
| } |
| |
| static void multiplicative_expression(int mode, Value * v) { |
| pm_expression(mode, v); |
| while (text_sy == '*' || text_sy == '/' || text_sy == '%') { |
| Value x; |
| int sy = text_sy; |
| next_sy(); |
| pm_expression(mode, &x); |
| if (mode != MODE_SKIP) { |
| if (!is_number(v) || !is_number(&x)) { |
| error(ERR_INV_EXPRESSION, "Numeric types expected"); |
| } |
| else if (v->type_class == TYPE_CLASS_COMPLEX || x.type_class == TYPE_CLASS_COMPLEX) { |
| double r_value = 0; |
| double i_value = 0; |
| if (mode == MODE_NORMAL) { |
| double d = 0; |
| switch (sy) { |
| case '*': |
| r_value = |
| to_r_double(mode, v) * to_r_double(mode, &x) - |
| to_i_double(mode, v) * to_i_double(mode, &x); |
| i_value = |
| to_r_double(mode, v) * to_i_double(mode, &x) + |
| to_i_double(mode, v) * to_r_double(mode, &x); |
| break; |
| case '/': |
| d = |
| to_r_double(mode, &x) * to_r_double(mode, &x) + |
| to_i_double(mode, &x) * to_i_double(mode, &x); |
| r_value = |
| (to_r_double(mode, v) * to_r_double(mode, &x) + |
| to_i_double(mode, v) * to_i_double(mode, &x)) / d; |
| i_value = |
| (to_i_double(mode, v) * to_r_double(mode, &x) - |
| to_r_double(mode, v) * to_i_double(mode, &x)) / d; |
| break; |
| default: |
| error(ERR_INV_EXPRESSION, "Invalid type"); |
| } |
| } |
| v->type = NULL; |
| v->type_class = TYPE_CLASS_COMPLEX; |
| set_complex_value(v, sizeof(double) * 2, r_value, i_value); |
| set_complex_type(v); |
| } |
| else if (is_real_number(v) || is_real_number(&x)) { |
| double value = 0; |
| if (mode == MODE_NORMAL) { |
| switch (sy) { |
| case '*': value = to_double(mode, v) * to_double(mode, &x); break; |
| case '/': value = to_double(mode, v) / to_double(mode, &x); break; |
| default: error(ERR_INV_EXPRESSION, "Invalid type"); |
| } |
| } |
| v->type = NULL; |
| v->type_class = TYPE_CLASS_REAL; |
| set_fp_value(v, sizeof(double), value); |
| set_fp_type(v); |
| } |
| else if (v->type_class == TYPE_CLASS_CARDINAL || x.type_class == TYPE_CLASS_CARDINAL) { |
| uint64_t value = 0; |
| if (mode == MODE_NORMAL) { |
| uint64_t a = to_uns(mode, v); |
| uint64_t b = to_uns(mode, &x); |
| if (sy != '*' && b == 0) error(ERR_INV_EXPRESSION, "Dividing by zero"); |
| switch (sy) { |
| case '*': value = a * b; break; |
| case '/': value = a / b; break; |
| case '%': value = a % b; break; |
| } |
| } |
| v->type = NULL; |
| v->type_class = TYPE_CLASS_CARDINAL; |
| set_int_value(v, sizeof(uint64_t), value); |
| } |
| else { |
| int64_t value = 0; |
| if (mode == MODE_NORMAL) { |
| int64_t a = to_int(mode, v); |
| int64_t b = to_int(mode, &x); |
| if (sy != '*' && b == 0) error(ERR_INV_EXPRESSION, "Dividing by zero"); |
| switch (sy) { |
| case '*': value = a * b; break; |
| case '/': value = a / b; break; |
| case '%': value = a % b; break; |
| } |
| } |
| v->type = NULL; |
| v->type_class = TYPE_CLASS_INTEGER; |
| set_int_value(v, sizeof(int64_t), value); |
| } |
| v->constant = v->constant && x.constant; |
| } |
| } |
| } |
| |
| static void additive_expression(int mode, Value * v) { |
| multiplicative_expression(mode, v); |
| while (text_sy == '+' || text_sy == '-') { |
| Value x; |
| int sy = text_sy; |
| next_sy(); |
| multiplicative_expression(mode, &x); |
| if (mode != MODE_SKIP) { |
| if (v->function) { |
| v->type_class = TYPE_CLASS_CARDINAL; |
| v->type = NULL; |
| } |
| if (x.function) { |
| x.type_class = TYPE_CLASS_CARDINAL; |
| x.type = NULL; |
| } |
| if (sy == '+' && v->type_class == TYPE_CLASS_ARRAY && x.type_class == TYPE_CLASS_ARRAY) { |
| if (mode == MODE_TYPE) { |
| v->remote = 0; |
| v->size = 0; |
| v->value = tmp_alloc_zero((size_t)v->size); |
| } |
| else { |
| char * value; |
| load_value(v); |
| load_value(&x); |
| v->size = strlen((char *)v->value) + strlen((char *)x.value) + 1; |
| value = (char *)tmp_alloc((size_t)v->size); |
| strcpy(value, (const char *)(v->value)); |
| strcat(value, (const char *)(x.value)); |
| v->value = value; |
| } |
| v->type = NULL; |
| } |
| #if ENABLE_Symbols |
| else if ((v->type_class == TYPE_CLASS_POINTER || v->type_class == TYPE_CLASS_ARRAY) && is_number(&x)) { |
| uint64_t value = 0; |
| Symbol * base = NULL; |
| ContextAddress size = 0; |
| if (v->type == NULL || get_symbol_base_type(v->type, &base) < 0 || |
| base == NULL || get_symbol_size(base, &size) < 0 || size == 0) { |
| error(ERR_INV_EXPRESSION, "Unknown pointer base type size"); |
| } |
| switch (sy) { |
| case '+': value = to_uns(mode, v) + to_uns(mode, &x) * size; break; |
| case '-': value = to_uns(mode, v) - to_uns(mode, &x) * size; break; |
| } |
| if (v->type_class == TYPE_CLASS_ARRAY) { |
| if (get_array_symbol(base, 0, &v->type) < 0 || |
| get_symbol_size(v->type, &v->size) < 0) { |
| error(errno, "Cannot cast to pointer"); |
| } |
| v->type_class = TYPE_CLASS_POINTER; |
| } |
| set_int_value(v, (size_t)v->size, value); |
| } |
| else if (is_number(v) && (x.type_class == TYPE_CLASS_POINTER || x.type_class == TYPE_CLASS_ARRAY) && sy == '+') { |
| uint64_t value = 0; |
| Symbol * base = NULL; |
| ContextAddress size = 0; |
| if (x.type == NULL || get_symbol_base_type(x.type, &base) < 0 || |
| base == NULL || get_symbol_size(base, &size) < 0 || size == 0) { |
| error(ERR_INV_EXPRESSION, "Unknown pointer base type size"); |
| } |
| value = to_uns(mode, &x) + to_uns(mode, v) * size; |
| v->type = x.type; |
| if (x.type_class == TYPE_CLASS_ARRAY) { |
| if (get_array_symbol(base, 0, &v->type) < 0 || |
| get_symbol_size(v->type, &v->size) < 0) { |
| error(errno, "Cannot cast to pointer"); |
| } |
| } |
| v->type_class = TYPE_CLASS_POINTER; |
| set_int_value(v, (size_t)x.size, value); |
| } |
| #endif |
| else if (!is_number(v) || !is_number(&x)) { |
| error(ERR_INV_EXPRESSION, "Numeric types expected"); |
| } |
| else if (v->type_class == TYPE_CLASS_COMPLEX || x.type_class == TYPE_CLASS_COMPLEX) { |
| double r_value = 0; |
| double i_value = 0; |
| switch (sy) { |
| case '+': |
| r_value = to_r_double(mode, v) + to_r_double(mode, &x); |
| i_value = to_i_double(mode, v) + to_i_double(mode, &x); |
| break; |
| case '-': |
| r_value = to_r_double(mode, v) - to_r_double(mode, &x); |
| i_value = to_i_double(mode, v) - to_i_double(mode, &x); |
| break; |
| } |
| v->type = NULL; |
| v->type_class = TYPE_CLASS_COMPLEX; |
| set_complex_value(v, sizeof(double) * 2, r_value, i_value); |
| set_complex_type(v); |
| } |
| else if (is_real_number(v) || is_real_number(&x)) { |
| double value = 0; |
| switch (sy) { |
| case '+': value = to_double(mode, v) + to_double(mode, &x); break; |
| case '-': value = to_double(mode, v) - to_double(mode, &x); break; |
| } |
| v->type = NULL; |
| v->type_class = TYPE_CLASS_REAL; |
| set_fp_value(v, sizeof(double), value); |
| set_fp_type(v); |
| } |
| else if (v->type_class == TYPE_CLASS_CARDINAL || x.type_class == TYPE_CLASS_CARDINAL) { |
| uint64_t value = 0; |
| switch (sy) { |
| case '+': value = to_uns(mode, v) + to_uns(mode, &x); break; |
| case '-': value = to_uns(mode, v) - to_uns(mode, &x); break; |
| } |
| v->type = NULL; |
| v->type_class = TYPE_CLASS_CARDINAL; |
| set_int_value(v, sizeof(uint64_t), value); |
| } |
| else { |
| int64_t value = 0; |
| switch (sy) { |
| case '+': value = to_int(mode, v) + to_int(mode, &x); break; |
| case '-': value = to_int(mode, v) - to_int(mode, &x); break; |
| } |
| v->type = NULL; |
| v->type_class = TYPE_CLASS_INTEGER; |
| set_int_value(v, sizeof(int64_t), value); |
| } |
| v->constant = v->constant && x.constant; |
| } |
| } |
| } |
| |
| static void shift_expression(int mode, Value * v) { |
| additive_expression(mode, v); |
| while (text_sy == SY_SHL || text_sy == SY_SHR) { |
| Value x; |
| int sy = text_sy; |
| next_sy(); |
| additive_expression(mode, &x); |
| if (mode != MODE_SKIP) { |
| uint64_t value = 0; |
| if (!is_whole_number(v) || !is_whole_number(&x)) { |
| error(ERR_INV_EXPRESSION, "Integral types expected"); |
| } |
| if (x.type_class != TYPE_CLASS_CARDINAL && to_int(mode, &x) < 0) { |
| if (v->type_class == TYPE_CLASS_CARDINAL) { |
| switch (sy) { |
| case SY_SHL: value = to_uns(mode, v) >> -to_int(mode, &x); break; |
| case SY_SHR: value = to_uns(mode, v) << -to_int(mode, &x); break; |
| } |
| } |
| else { |
| switch (sy) { |
| case SY_SHL: value = to_int(mode, v) >> -to_int(mode, &x); break; |
| case SY_SHR: value = to_int(mode, v) << -to_int(mode, &x); break; |
| } |
| v->type_class = TYPE_CLASS_INTEGER; |
| } |
| } |
| else { |
| if (v->type_class == TYPE_CLASS_CARDINAL) { |
| switch (sy) { |
| case SY_SHL: value = to_uns(mode, v) << to_uns(mode, &x); break; |
| case SY_SHR: value = to_uns(mode, v) >> to_uns(mode, &x); break; |
| } |
| } |
| else { |
| switch (sy) { |
| case SY_SHL: value = to_int(mode, v) << to_uns(mode, &x); break; |
| case SY_SHR: value = to_int(mode, v) >> to_uns(mode, &x); break; |
| } |
| v->type_class = TYPE_CLASS_INTEGER; |
| } |
| } |
| v->type = NULL; |
| v->constant = v->constant && x.constant; |
| set_int_value(v, sizeof(uint64_t), value); |
| } |
| } |
| } |
| |
| static void relational_expression(int mode, Value * v) { |
| shift_expression(mode, v); |
| while (text_sy == '<' || text_sy == '>' || text_sy == SY_LEQ || text_sy == SY_GEQ) { |
| Value x; |
| int sy = text_sy; |
| next_sy(); |
| shift_expression(mode, &x); |
| if (mode != MODE_SKIP) { |
| uint32_t value = 0; |
| if (v->type_class == TYPE_CLASS_ARRAY && x.type_class == TYPE_CLASS_ARRAY) { |
| int n = 0; |
| load_value(v); |
| load_value(&x); |
| n = strcmp((char *)v->value, (char *)x.value); |
| switch (sy) { |
| case '<': value = n < 0; break; |
| case '>': value = n > 0; break; |
| case SY_LEQ: value = n <= 0; break; |
| case SY_GEQ: value = n >= 0; break; |
| } |
| } |
| else if (is_real_number(v) || is_real_number(&x)) { |
| switch (sy) { |
| case '<': value = to_double(mode, v) < to_double(mode, &x); break; |
| case '>': value = to_double(mode, v) > to_double(mode, &x); break; |
| case SY_LEQ: value = to_double(mode, v) <= to_double(mode, &x); break; |
| case SY_GEQ: value = to_double(mode, v) >= to_double(mode, &x); break; |
| } |
| } |
| else if (v->type_class == TYPE_CLASS_CARDINAL || x.type_class == TYPE_CLASS_CARDINAL) { |
| switch (sy) { |
| case '<': value = to_uns(mode, v) < to_uns(mode, &x); break; |
| case '>': value = to_uns(mode, v) > to_uns(mode, &x); break; |
| case SY_LEQ: value = to_uns(mode, v) <= to_uns(mode, &x); break; |
| case SY_GEQ: value = to_uns(mode, v) >= to_uns(mode, &x); break; |
| } |
| } |
| else { |
| switch (sy) { |
| case '<': value = to_int(mode, v) < to_int(mode, &x); break; |
| case '>': value = to_int(mode, v) > to_int(mode, &x); break; |
| case SY_LEQ: value = to_int(mode, v) <= to_int(mode, &x); break; |
| case SY_GEQ: value = to_int(mode, v) >= to_int(mode, &x); break; |
| } |
| } |
| if (mode != MODE_NORMAL) value = 0; |
| v->constant = v->constant && x.constant; |
| set_bool_value(v, value); |
| } |
| } |
| } |
| |
| static void equality_expression(int mode, Value * v) { |
| relational_expression(mode, v); |
| while (text_sy == SY_EQU || text_sy == SY_NEQ) { |
| Value x; |
| int sy = text_sy; |
| next_sy(); |
| relational_expression(mode, &x); |
| if (mode != MODE_SKIP) { |
| uint32_t value = 0; |
| if (v->type_class == TYPE_CLASS_ARRAY && x.type_class == TYPE_CLASS_ARRAY) { |
| load_value(v); |
| load_value(&x); |
| value = strcmp((char *)v->value, (char *)x.value) == 0; |
| } |
| else if (v->type_class == TYPE_CLASS_COMPLEX || x.type_class == TYPE_CLASS_COMPLEX) { |
| value = |
| to_r_double(mode, v) == to_r_double(mode, &x) && |
| to_i_double(mode, v) == to_i_double(mode, &x); |
| } |
| else if (is_real_number(v) || is_real_number(&x)) { |
| value = to_double(mode, v) == to_double(mode, &x); |
| } |
| else { |
| value = to_int(mode, v) == to_int(mode, &x); |
| } |
| if (sy == SY_NEQ) value = !value; |
| if (mode != MODE_NORMAL) value = 0; |
| v->constant = v->constant && x.constant; |
| set_bool_value(v, value); |
| } |
| } |
| } |
| |
| static void and_expression(int mode, Value * v) { |
| equality_expression(mode, v); |
| while (text_sy == '&') { |
| Value x; |
| next_sy(); |
| equality_expression(mode, &x); |
| if (mode != MODE_SKIP) { |
| int64_t value = 0; |
| if (!is_whole_number(v) || !is_whole_number(&x)) { |
| error(ERR_INV_EXPRESSION, "Integral types expected"); |
| } |
| if (v->type_class == TYPE_CLASS_CARDINAL || x.type_class == TYPE_CLASS_CARDINAL) { |
| v->type_class = TYPE_CLASS_CARDINAL; |
| value = to_uns(mode, v) & to_uns(mode, &x); |
| } |
| else { |
| v->type_class = TYPE_CLASS_INTEGER; |
| value = to_int(mode, v) & to_int(mode, &x); |
| } |
| if (mode != MODE_NORMAL) value = 0; |
| v->type = NULL; |
| v->constant = v->constant && x.constant; |
| set_int_value(v, sizeof(int64_t), value); |
| } |
| } |
| } |
| |
| static void exclusive_or_expression(int mode, Value * v) { |
| and_expression(mode, v); |
| while (text_sy == '^') { |
| Value x; |
| next_sy(); |
| and_expression(mode, &x); |
| if (mode != MODE_SKIP) { |
| int64_t value = 0; |
| if (!is_whole_number(v) || !is_whole_number(&x)) { |
| error(ERR_INV_EXPRESSION, "Integral types expected"); |
| } |
| if (v->type_class == TYPE_CLASS_CARDINAL || x.type_class == TYPE_CLASS_CARDINAL) { |
| v->type_class = TYPE_CLASS_CARDINAL; |
| value = to_uns(mode, v) ^ to_uns(mode, &x); |
| } |
| else { |
| v->type_class = TYPE_CLASS_INTEGER; |
| value = to_int(mode, v) ^ to_int(mode, &x); |
| } |
| if (mode != MODE_NORMAL) value = 0; |
| v->type = NULL; |
| v->constant = v->constant && x.constant; |
| set_int_value(v, sizeof(int64_t), value); |
| } |
| } |
| } |
| |
| static void inclusive_or_expression(int mode, Value * v) { |
| exclusive_or_expression(mode, v); |
| while (text_sy == '|') { |
| Value x; |
| next_sy(); |
| exclusive_or_expression(mode, &x); |
| if (mode != MODE_SKIP) { |
| int64_t value = 0; |
| if (!is_whole_number(v) || !is_whole_number(&x)) { |
| error(ERR_INV_EXPRESSION, "Integral types expected"); |
| } |
| if (v->type_class == TYPE_CLASS_CARDINAL || x.type_class == TYPE_CLASS_CARDINAL) { |
| v->type_class = TYPE_CLASS_CARDINAL; |
| value = to_uns(mode, v) | to_uns(mode, &x); |
| } |
| else { |
| v->type_class = TYPE_CLASS_INTEGER; |
| value = to_int(mode, v) | to_int(mode, &x); |
| } |
| if (mode != MODE_NORMAL) value = 0; |
| v->type = NULL; |
| v->constant = v->constant && x.constant; |
| set_int_value(v, sizeof(int64_t), value); |
| } |
| } |
| } |
| |
| static void logical_and_expression(int mode, Value * v) { |
| inclusive_or_expression(mode, v); |
| while (text_sy == SY_AND) { |
| Value x; |
| int b = to_boolean(mode, v); |
| next_sy(); |
| inclusive_or_expression(b ? mode : MODE_SKIP, &x); |
| if (b) { |
| if (!v->constant) x.constant = 0; |
| *v = x; |
| } |
| } |
| } |
| |
| static void logical_or_expression(int mode, Value * v) { |
| logical_and_expression(mode, v); |
| while (text_sy == SY_OR) { |
| Value x; |
| int b = to_boolean(mode, v); |
| next_sy(); |
| logical_and_expression(!b ? mode : MODE_SKIP, &x); |
| if (!b) { |
| if (!v->constant) x.constant = 0; |
| *v = x; |
| } |
| } |
| } |
| |
| static void conditional_expression(int mode, Value * v) { |
| logical_or_expression(mode, v); |
| if (text_sy == '?') { |
| Value x; |
| Value y; |
| int b = to_boolean(mode, v); |
| next_sy(); |
| expression(b ? mode : MODE_SKIP, &x); |
| if (text_sy != ':') error(ERR_INV_EXPRESSION, "Missing ':'"); |
| next_sy(); |
| conditional_expression(!b ? mode : MODE_SKIP, &y); |
| if (!v->constant) x.constant = y.constant = 0; |
| *v = b ? x : y; |
| } |
| } |
| |
| static void expression(int mode, Value * v) { |
| /* TODO: assignments in expressions */ |
| conditional_expression(mode, v); |
| } |
| |
| static int evaluate_script(int mode, char * s, int load, Value * v) { |
| Trap trap; |
| |
| expression_has_func_call = 0; |
| if (set_trap(&trap)) { |
| if (s == NULL || *s == 0) str_exception(ERR_INV_EXPRESSION, "Empty expression"); |
| text = s; |
| text_pos = 0; |
| text_len = strlen(s) + 1; |
| next_ch(); |
| next_sy(); |
| for (;;) { |
| expression(mode, v); |
| if (text_sy != ',') break; |
| next_sy(); |
| } |
| if (text_sy != 0) error(ERR_INV_EXPRESSION, "Illegal characters at the end of expression"); |
| if (load) load_value(v); |
| clear_trap(&trap); |
| } |
| |
| #if ENABLE_FuncCallInjection |
| if (get_error_code(trap.error) != ERR_CACHE_MISS) { |
| unsigned id = cache_transaction_id(); |
| LINK * l = func_call_state.next; |
| while (l != &func_call_state) { |
| FuncCallState * state = link_all2fc(l); |
| l = l->next; |
| if (state->id == id) { |
| state->committed = 1; |
| if (state->regs_cnt == 0) free_funccall_state(state); |
| } |
| } |
| } |
| #endif /* ENABLE_FuncCallInjection */ |
| |
| if (trap.error) { |
| errno = trap.error; |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| int evaluate_expression(Context * ctx, int frame, ContextAddress addr, char * s, int load, Value * v) { |
| #if !defined(SERVICE_Expressions) |
| big_endian = big_endian_host(); |
| #endif |
| expression_context = ctx; |
| expression_frame = frame; |
| expression_addr = addr; |
| return evaluate_script(MODE_NORMAL, s, load, v); |
| } |
| |
| int value_to_boolean(Value * v, int * res) { |
| Trap trap; |
| if (!set_trap(&trap)) return -1; |
| *res = to_boolean(MODE_NORMAL, v); |
| clear_trap(&trap); |
| return 0; |
| } |
| |
| int value_to_address(Value * v, ContextAddress * res) { |
| Trap trap; |
| if (!set_trap(&trap)) return -1; |
| *res = (ContextAddress)to_uns(MODE_NORMAL, v); |
| clear_trap(&trap); |
| return 0; |
| } |
| |
| int value_to_signed(Value * v, int64_t *res) { |
| Trap trap; |
| if (!set_trap(&trap)) return -1; |
| *res = to_int(MODE_NORMAL, v); |
| clear_trap(&trap); |
| return 0; |
| } |
| |
| int value_to_unsigned(Value * v, uint64_t *res) { |
| Trap trap; |
| if (!set_trap(&trap)) return -1; |
| *res = to_uns(MODE_NORMAL, v); |
| clear_trap(&trap); |
| return 0; |
| } |
| |
| int value_to_double(Value * v, double *res) { |
| Trap trap; |
| if (!set_trap(&trap)) return -1; |
| *res = to_double(MODE_NORMAL, v); |
| clear_trap(&trap); |
| return 0; |
| } |
| |
| #if SERVICE_Expressions |
| |
| /********************** Commands **************************/ |
| |
| typedef struct CommandArgs { |
| char token[256]; |
| char id[256]; |
| } CommandArgs; |
| |
| typedef struct CommandCreateArgs { |
| char token[256]; |
| char id[256]; |
| int use_state; |
| ContextAddress addr; |
| char language[256]; |
| char * script; |
| } CommandCreateArgs; |
| |
| typedef struct CommandAssignArgs { |
| char token[256]; |
| char id[256]; |
| char * value_buf; |
| size_t value_size; |
| } CommandAssignArgs; |
| |
| typedef struct Expression { |
| LINK link_all; |
| LINK link_id; |
| char id[256]; |
| char var_id[256]; |
| char parent[256]; |
| int use_state; |
| ContextAddress addr; |
| char language[256]; |
| Channel * channel; |
| char * script; |
| int can_assign; |
| int has_func_call; |
| ContextAddress size; |
| int type_class; |
| char type[256]; |
| } Expression; |
| |
| #define link_all2exp(A) ((Expression *)((char *)(A) - offsetof(Expression, link_all))) |
| #define link_id2exp(A) ((Expression *)((char *)(A) - offsetof(Expression, link_id))) |
| |
| #define ID2EXP_HASH_SIZE (32 * MEM_USAGE_FACTOR - 1) |
| |
| static LINK expressions = TCF_LIST_INIT(expressions); |
| static LINK id2exp[ID2EXP_HASH_SIZE]; |
| |
| static const char * EXPRESSIONS = "Expressions"; |
| static unsigned expr_id_cnt = 0; |
| |
| #define expression_hash(id) ((unsigned)atoi(id + 4) % ID2EXP_HASH_SIZE) |
| |
| #if ENABLE_ExpressionSerialization |
| |
| typedef struct PendingCommand { |
| LINK link; |
| CacheClient * client; |
| Channel * channel; |
| char args[sizeof(CommandCreateArgs)]; |
| size_t args_size; |
| } PendingCommand; |
| |
| #define link_cmds2cmd(A) ((PendingCommand *)((char *)(A) - offsetof(PendingCommand, link))) |
| |
| static PendingCommand * pending_cmd = NULL; |
| static LINK cmd_queue; |
| |
| static void command_start(CacheClient * client, Channel * channel, void * args, size_t args_size) { |
| PendingCommand * cmd = (PendingCommand *)loc_alloc_zero(sizeof(PendingCommand)); |
| assert(args_size <= sizeof(cmd->args)); |
| channel_lock_with_msg(cmd->channel = channel, EXPRESSIONS); |
| memcpy(cmd->args, args, args_size); |
| cmd->args_size = args_size; |
| cmd->client = client; |
| list_add_last(&cmd->link, &cmd_queue); |
| if (cmd_queue.next == &cmd->link) { |
| assert(pending_cmd == NULL); |
| pending_cmd = cmd; |
| cache_enter(cmd->client, cmd->channel, cmd->args, cmd->args_size); |
| } |
| } |
| |
| static void command_start_next(void * args) { |
| PendingCommand * cmd = NULL; |
| assert(pending_cmd == NULL); |
| pending_cmd = cmd = link_cmds2cmd(cmd_queue.next); |
| cache_enter(cmd->client, cmd->channel, cmd->args, cmd->args_size); |
| } |
| |
| static void command_done(void) { |
| PendingCommand * cmd = pending_cmd; |
| pending_cmd = NULL; |
| assert(cmd != NULL); |
| assert(&cmd->link == cmd_queue.next); |
| channel_unlock_with_msg(cmd->channel, EXPRESSIONS); |
| list_remove(cmd_queue.next); |
| loc_free(cmd); |
| if (list_is_empty(&cmd_queue)) return; |
| post_event(command_start_next, NULL); |
| } |
| |
| #else |
| |
| #define command_start(client, channel, args, args_size) cache_enter(client, channel, args, args_size) |
| #define command_done() |
| |
| #endif |
| |
| static Expression * find_expression(char * id) { |
| if (id[0] == 'E' && id[1] == 'X' && id[2] == 'P' && id[3] == 'R') { |
| unsigned hash = expression_hash(id); |
| LINK * l = id2exp[hash].next; |
| while (l != &id2exp[hash]) { |
| Expression * e = link_id2exp(l); |
| l = l->next; |
| if (strcmp(e->id, id) == 0) return e; |
| } |
| } |
| return NULL; |
| } |
| |
| static int symbol_to_expression(char * expr_id, char * parent, char * sym_id, Expression ** res) { |
| #if ENABLE_Symbols |
| Symbol * sym = NULL; |
| Symbol * type = NULL; |
| int sym_class = 0; |
| size_t script_len = strlen(sym_id) + 8; |
| char * script = (char *)tmp_alloc(script_len); |
| Expression * expr = (Expression *)tmp_alloc_zero(sizeof(Expression)); |
| |
| strlcpy(expr->id, expr_id, sizeof(expr->id)); |
| strlcpy(expr->var_id, sym_id, sizeof(expr->var_id)); |
| strlcpy(expr->parent, parent, sizeof(expr->parent)); |
| expr->use_state = 1; |
| |
| if (id2symbol(sym_id, &sym) < 0) return -1; |
| |
| snprintf(script, script_len, "${%s}", sym_id); |
| expr->script = script; |
| |
| if (get_symbol_type_class(sym, &expr->type_class) < 0) return -1; |
| if (get_symbol_size(sym, &expr->size) < 0) return -1; |
| if (get_symbol_class(sym, &sym_class) < 0) return -1; |
| if (get_symbol_type(sym, &type) < 0) return -1; |
| |
| expr->can_assign = sym_class == SYM_CLASS_REFERENCE; |
| if (type != NULL) strlcpy(expr->type, symbol2id(type), sizeof(expr->type)); |
| |
| *res = expr; |
| return 0; |
| #else |
| errno = ERR_UNSUPPORTED; |
| return -1; |
| #endif |
| } |
| |
| static int expression_context_id(char * id, Context ** ctx, int * frame, Expression ** expr) { |
| int err = 0; |
| Expression * e = NULL; |
| |
| if (id[0] == 'S') { |
| char parent[256]; |
| char * s = id + 1; |
| size_t i = 0; |
| while (*s && i < sizeof(parent) - 1) { |
| char ch = *s++; |
| if (ch == '.') { |
| if (*s == '.') { |
| parent[i++] = *s++; |
| continue; |
| } |
| break; |
| } |
| parent[i++] = ch; |
| } |
| parent[i] = 0; |
| if (symbol_to_expression(id, parent, s, &e) < 0) err = errno; |
| } |
| else if ((e = find_expression(id)) == NULL) { |
| err = ERR_INV_CONTEXT; |
| } |
| |
| if (!err) { |
| if ((*ctx = id2ctx(e->parent)) != NULL) { |
| *frame = e->use_state && context_has_state(*ctx) ? STACK_TOP_FRAME : STACK_NO_FRAME; |
| } |
| else if (id2frame(e->parent, ctx, frame) < 0) { |
| err = errno; |
| } |
| } |
| |
| if (err) { |
| errno = err; |
| return -1; |
| } |
| |
| *expr = e; |
| return 0; |
| } |
| |
| static void write_context(OutputStream * out, Expression * expr) { |
| write_stream(out, '{'); |
| json_write_string(out, "ID"); |
| write_stream(out, ':'); |
| json_write_string(out, expr->id); |
| |
| write_stream(out, ','); |
| |
| json_write_string(out, "ParentID"); |
| write_stream(out, ':'); |
| json_write_string(out, expr->parent); |
| |
| if (expr->var_id[0]) { |
| write_stream(out, ','); |
| |
| json_write_string(out, "SymbolID"); |
| write_stream(out, ':'); |
| json_write_string(out, expr->var_id); |
| } |
| |
| write_stream(out, ','); |
| |
| json_write_string(out, "Expression"); |
| write_stream(out, ':'); |
| json_write_string(out, expr->script); |
| |
| if (expr->can_assign) { |
| write_stream(out, ','); |
| |
| json_write_string(out, "CanAssign"); |
| write_stream(out, ':'); |
| json_write_boolean(out, expr->can_assign); |
| } |
| |
| if (expr->has_func_call) { |
| write_stream(out, ','); |
| |
| json_write_string(out, "HasFuncCall"); |
| write_stream(out, ':'); |
| json_write_boolean(out, expr->has_func_call); |
| } |
| |
| if (expr->type_class != TYPE_CLASS_UNKNOWN) { |
| write_stream(out, ','); |
| |
| json_write_string(out, "Class"); |
| write_stream(out, ':'); |
| json_write_long(out, expr->type_class); |
| } |
| |
| if (expr->type[0]) { |
| write_stream(out, ','); |
| |
| json_write_string(out, "Type"); |
| write_stream(out, ':'); |
| json_write_string(out, expr->type); |
| } |
| |
| write_stream(out, ','); |
| |
| json_write_string(out, "Size"); |
| write_stream(out, ':'); |
| json_write_uint64(out, expr->size); |
| |
| write_stream(out, '}'); |
| } |
| |
| static void get_context_cache_client(void * x) { |
| CommandArgs * args = (CommandArgs *)x; |
| Channel * c = cache_channel(); |
| Context * ctx = NULL; |
| int frame = STACK_NO_FRAME; |
| Expression * expr = NULL; |
| int err = 0; |
| |
| if (expression_context_id(args->id, &ctx, &frame, &expr) < 0) err = errno; |
| |
| cache_exit(); |
| |
| write_stringz(&c->out, "R"); |
| write_stringz(&c->out, args->token); |
| write_errno(&c->out, err); |
| |
| if (err) { |
| write_stringz(&c->out, "null"); |
| } |
| else { |
| write_context(&c->out, expr); |
| write_stream(&c->out, 0); |
| } |
| |
| write_stream(&c->out, MARKER_EOM); |
| } |
| |
| static void command_get_context(char * token, Channel * c) { |
| CommandArgs args; |
| |
| json_read_string(&c->inp, args.id, sizeof(args.id)); |
| json_test_char(&c->inp, MARKER_EOA); |
| json_test_char(&c->inp, MARKER_EOM); |
| |
| strlcpy(args.token, token, sizeof(args.token)); |
| cache_enter(get_context_cache_client, c, &args, sizeof(args)); |
| } |
| |
| #if ENABLE_Symbols && (SERVICE_StackTrace || ENABLE_ContextProxy) |
| |
| static int sym_cnt = 0; |
| static int sym_max = 0; |
| static Symbol ** sym_buf = NULL; |
| |
| static void get_children_callback(void * x, Symbol * symbol) { |
| if (sym_cnt >= sym_max) { |
| sym_max += 8; |
| sym_buf = (Symbol **)loc_realloc(sym_buf, sizeof(Symbol *) * sym_max); |
| } |
| sym_buf[sym_cnt++] = symbol; |
| } |
| |
| #endif |
| |
| static void get_children_cache_client(void * x) { |
| CommandArgs * args = (CommandArgs *)x; |
| Channel * c = cache_channel(); |
| int err = 0; |
| |
| /* TODO: Expressions.getChildren - structures */ |
| #if ENABLE_Symbols && (SERVICE_StackTrace || ENABLE_ContextProxy) |
| char parent_id[256]; |
| { |
| Context * ctx; |
| int frame = STACK_NO_FRAME; |
| |
| sym_cnt = 0; |
| |
| if ((ctx = id2ctx(args->id)) != NULL && context_has_state(ctx)) { |
| frame = get_top_frame(ctx); |
| strlcpy(parent_id, frame2id(ctx, frame), sizeof(parent_id)); |
| } |
| else if (id2frame(args->id, &ctx, &frame) == 0) { |
| strlcpy(parent_id, args->id, sizeof(parent_id)); |
| } |
| else { |
| ctx = NULL; |
| } |
| |
| if (ctx != NULL && err == 0 && enumerate_symbols( |
| ctx, frame, get_children_callback, &args) < 0) err = errno; |
| } |
| #else |
| err = ERR_UNSUPPORTED; |
| #endif |
| |
| cache_exit(); |
| |
| write_stringz(&c->out, "R"); |
| write_stringz(&c->out, args->token); |
| |
| write_errno(&c->out, err); |
| |
| write_stream(&c->out, '['); |
| #if ENABLE_Symbols && (SERVICE_StackTrace || ENABLE_ContextProxy) |
| { |
| int i; |
| for (i = 0; i < sym_cnt; i++) { |
| const char * s = parent_id; |
| if (i > 0) write_stream(&c->out, ','); |
| write_stream(&c->out, '"'); |
| write_stream(&c->out, 'S'); |
| while (*s) { |
| if (*s == '.') write_stream(&c->out, '.'); |
| json_write_char(&c->out, *s++); |
| } |
| write_stream(&c->out, '.'); |
| s = symbol2id(sym_buf[i]); |
| while (*s) json_write_char(&c->out, *s++); |
| write_stream(&c->out, '"'); |
| } |
| } |
| #endif |
| write_stream(&c->out, ']'); |
| write_stream(&c->out, 0); |
| |
| write_stream(&c->out, MARKER_EOM); |
| } |
| |
| static void command_get_children(char * token, Channel * c) { |
| CommandArgs args; |
| |
| json_read_string(&c->inp, args.id, sizeof(args.id)); |
| json_test_char(&c->inp, MARKER_EOA); |
| json_test_char(&c->inp, MARKER_EOM); |
| |
| strlcpy(args.token, token, sizeof(args.token)); |
| cache_enter(get_children_cache_client, c, &args, sizeof(args)); |
| } |
| |
| static void command_create_cache_client(void * x) { |
| CommandCreateArgs * args = (CommandCreateArgs *)x; |
| Expression * e; |
| Expression buf; |
| Channel * c = cache_channel(); |
| int frame = STACK_NO_FRAME; |
| int err = 0; |
| |
| memset(e = &buf, 0, sizeof(buf)); |
| |
| if (is_channel_closed(c)) err = ERR_CHANNEL_CLOSED; |
| |
| if (!err) { |
| do snprintf(e->id, sizeof(e->id), "EXPR%d", expr_id_cnt++); |
| while (find_expression(e->id) != NULL); |
| strlcpy(e->parent, args->id, sizeof(e->parent)); |
| strlcpy(e->language, args->language, sizeof(e->language)); |
| e->channel = c; |
| e->script = args->script; |
| e->use_state = args->use_state; |
| e->addr = args->addr; |
| } |
| |
| if (!err) { |
| Value value; |
| Context * ctx = NULL; |
| memset(&value, 0, sizeof(value)); |
| if ((ctx = id2ctx(e->parent)) != NULL) { |
| frame = args->use_state && context_has_state(ctx) ? STACK_TOP_FRAME : STACK_NO_FRAME; |
| } |
| else if (id2frame(e->parent, &ctx, &frame) < 0) { |
| err = errno; |
| } |
| if (!err) { |
| check_all_stopped(ctx); |
| expression_context = ctx; |
| expression_frame = frame; |
| expression_addr = e->addr; |
| if (evaluate_script(MODE_TYPE, e->script, 0, &value) < 0) err = errno; |
| } |
| if (!err) { |
| e->can_assign = value.remote || (value.loc != NULL && value.loc->pieces_cnt > 0); |
| e->has_func_call = expression_has_func_call; |
| e->type_class = value.type_class; |
| e->size = value.size; |
| #if ENABLE_Symbols |
| if (value.type != NULL) strlcpy(e->type, symbol2id(value.type), sizeof(e->type)); |
| #endif |
| } |
| } |
| |
| cache_exit(); |
| |
| write_stringz(&c->out, "R"); |
| write_stringz(&c->out, args->token); |
| write_errno(&c->out, err); |
| |
| if (err) { |
| write_stringz(&c->out, "null"); |
| loc_free(args->script); |
| } |
| else { |
| *(e = (Expression *)loc_alloc(sizeof(Expression))) = buf; |
| list_add_last(&e->link_all, &expressions); |
| list_add_last(&e->link_id, id2exp + expression_hash(e->id)); |
| write_context(&c->out, e); |
| write_stream(&c->out, 0); |
| } |
| |
| write_stream(&c->out, MARKER_EOM); |
| command_done(); |
| } |
| |
| static void command_create(char * token, Channel * c) { |
| CommandCreateArgs args; |
| |
| memset(&args, 0, sizeof(args)); |
| json_read_string(&c->inp, args.id, sizeof(args.id)); |
| json_test_char(&c->inp, MARKER_EOA); |
| json_read_string(&c->inp, args.language, sizeof(args.language)); |
| json_test_char(&c->inp, MARKER_EOA); |
| args.script = json_read_alloc_string(&c->inp); |
| json_test_char(&c->inp, MARKER_EOA); |
| json_test_char(&c->inp, MARKER_EOM); |
| |
| args.use_state = 1; |
| strlcpy(args.token, token, sizeof(args.token)); |
| command_start(command_create_cache_client, c, &args, sizeof(args)); |
| } |
| |
| static void read_expression_scope(InputStream * inp, const char * name, void * x) { |
| CommandCreateArgs * args = (CommandCreateArgs *)x; |
| if (strcmp(name, "ContextID") == 0) json_read_string(inp, args->id, sizeof(args->id)); |
| else if (strcmp(name, "Address") == 0) args->addr = (ContextAddress)json_read_uint64(inp); |
| else if (strcmp(name, "Language") == 0) json_read_string(inp, args->language, sizeof(args->language)); |
| else json_skip_object(inp); |
| } |
| |
| static void command_create_in_scope(char * token, Channel * c) { |
| CommandCreateArgs args; |
| |
| memset(&args, 0, sizeof(args)); |
| json_read_struct(&c->inp, read_expression_scope, &args); |
| json_test_char(&c->inp, MARKER_EOA); |
| args.script = json_read_alloc_string(&c->inp); |
| json_test_char(&c->inp, MARKER_EOA); |
| json_test_char(&c->inp, MARKER_EOM); |
| |
| strlcpy(args.token, token, sizeof(args.token)); |
| command_start(command_create_cache_client, c, &args, sizeof(args)); |
| } |
| |
| static void command_evaluate_cache_client(void * x) { |
| CommandArgs * args = (CommandArgs *)x; |
| Channel * c = cache_channel(); |
| Context * ctx = NULL; |
| int frame = STACK_NO_FRAME; |
| Expression * e = NULL; |
| Value value; |
| int value_ok = 0; |
| void * buf = NULL; |
| int implicit_pointer = 0; |
| int err = 0; |
| |
| memset(&value, 0, sizeof(value)); |
| if (expression_context_id(args->id, &ctx, &frame, &e) < 0) err = errno; |
| if (!err) { |
| check_all_stopped(ctx); |
| expression_context = ctx; |
| expression_frame = frame; |
| expression_addr = e->addr; |
| if (evaluate_script(MODE_NORMAL, e->script, 0, &value) < 0) err = errno; |
| else value_ok = 1; |
| } |
| if (!err && value.remote && value.size <= 0x10000) { |
| buf = tmp_alloc_zero((size_t)value.size); |
| if (!err && context_read_mem(ctx, value.address, buf, (size_t)value.size) < 0) |
| err = set_errno(errno, "Cannot read target memory"); |
| } |
| if (!err && value.loc) { |
| unsigned n; |
| for (n = 0; n < value.loc->pieces_cnt; n++) { |
| if (value.loc->pieces[n].implicit_pointer) { |
| implicit_pointer = 1; |
| break; |
| } |
| } |
| } |
| if (!err && !value.remote && value.value == NULL && !implicit_pointer) { |
| Trap trap; |
| if (set_trap(&trap)) { |
| load_value(&value); |
| clear_trap(&trap); |
| } |
| err = trap.error; |
| } |
| |
| cache_exit(); |
| |
| write_stringz(&c->out, "R"); |
| write_stringz(&c->out, args->token); |
| if (err) { |
| write_stringz(&c->out, "null"); |
| } |
| else if (!value.remote) { |
| json_write_binary(&c->out, value.value, (size_t)value.size); |
| write_stream(&c->out, 0); |
| } |
| else if (buf != NULL) { |
| json_write_binary(&c->out, buf, (size_t)value.size); |
| write_stream(&c->out, 0); |
| } |
| else { |
| write_stringz(&c->out, "null"); |
| } |
| write_errno(&c->out, err); |
| if (!value_ok) { |
| write_stringz(&c->out, "null"); |
| } |
| else { |
| int cnt = 0; |
| write_stream(&c->out, '{'); |
| |
| if (value.type_class != TYPE_CLASS_UNKNOWN) { |
| json_write_string(&c->out, "Class"); |
| write_stream(&c->out, ':'); |
| json_write_long(&c->out, value.type_class); |
| cnt++; |
| } |
| |
| #if ENABLE_Symbols |
| if (value.type != NULL) { |
| if (cnt > 0) write_stream(&c->out, ','); |
| json_write_string(&c->out, "Type"); |
| write_stream(&c->out, ':'); |
| json_write_string(&c->out, symbol2id(value.type)); |
| cnt++; |
| } |
| |
| if (value.sym != NULL) { |
| if (cnt > 0) write_stream(&c->out, ','); |
| json_write_string(&c->out, "Symbol"); |
| write_stream(&c->out, ':'); |
| json_write_string(&c->out, symbol2id(value.sym)); |
| cnt++; |
| } |
| #endif |
| if (value.bit_stride != 0) { |
| if (cnt > 0) write_stream(&c->out, ','); |
| json_write_string(&c->out, "BitStride"); |
| write_stream(&c->out, ':'); |
| json_write_ulong(&c->out, value.bit_stride); |
| cnt++; |
| } |
| |
| if (value.binary_scale != 0) { |
| if (cnt > 0) write_stream(&c->out, ','); |
| json_write_string(&c->out, "BinaryScale"); |
| write_stream(&c->out, ':'); |
| json_write_long(&c->out, value.binary_scale); |
| cnt++; |
| } |
| |
| if (value.decimal_scale != 0) { |
| if (cnt > 0) write_stream(&c->out, ','); |
| json_write_string(&c->out, "DecimalScale"); |
| write_stream(&c->out, ':'); |
| json_write_long(&c->out, value.decimal_scale); |
| cnt++; |
| } |
| |
| if (implicit_pointer) { |
| if (cnt > 0) write_stream(&c->out, ','); |
| json_write_string(&c->out, "ImplicitPointer"); |
| write_stream(&c->out, ':'); |
| json_write_boolean(&c->out, 1); |
| cnt++; |
| } |
| else { |
| if (value.reg != NULL) { |
| int reg_frame = value.loc->ctx == ctx ? frame : STACK_NO_FRAME; |
| if (cnt > 0) write_stream(&c->out, ','); |
| json_write_string(&c->out, "Register"); |
| write_stream(&c->out, ':'); |
| json_write_string(&c->out, register2id(value.loc->ctx, reg_frame, value.reg)); |
| cnt++; |
| } |
| |
| if (value.remote) { |
| if (cnt > 0) write_stream(&c->out, ','); |
| json_write_string(&c->out, "Address"); |
| write_stream(&c->out, ':'); |
| json_write_uint64(&c->out, value.address); |
| cnt++; |
| } |
| |
| if (value.loc != NULL && value.loc->pieces_cnt > 0 && value.reg == NULL) { |
| unsigned i; |
| if (cnt > 0) write_stream(&c->out, ','); |
| json_write_string(&c->out, "Pieces"); |
| write_stream(&c->out, ':'); |
| write_stream(&c->out, '['); |
| for (i = 0; i < value.loc->pieces_cnt; i++) { |
| LocationPiece * piece = value.loc->pieces + i; |
| if (i > 0) write_stream(&c->out, ','); |
| write_stream(&c->out, '{'); |
| if (piece->size) { |
| json_write_string(&c->out, "Size"); |
| write_stream(&c->out, ':'); |
| json_write_ulong(&c->out, piece->size); |
| } |
| else { |
| json_write_string(&c->out, "BitSize"); |
| write_stream(&c->out, ':'); |
| json_write_ulong(&c->out, piece->bit_size); |
| } |
| if (piece->bit_offs) { |
| write_stream(&c->out, ','); |
| json_write_string(&c->out, "BitOffs"); |
| write_stream(&c->out, ':'); |
| json_write_ulong(&c->out, piece->bit_offs); |
| } |
| if (!piece->optimized_away) { |
| write_stream(&c->out, ','); |
| if (piece->reg) { |
| Context * reg_ctx = value.loc->ctx; |
| int reg_frame = get_info_frame(value.loc->ctx, value.loc->stack_frame); |
| json_write_string(&c->out, "Register"); |
| write_stream(&c->out, ':'); |
| json_write_string(&c->out, register2id(reg_ctx, reg_frame, piece->reg)); |
| } |
| else if (piece->value) { |
| json_write_string(&c->out, "Value"); |
| write_stream(&c->out, ':'); |
| json_write_binary(&c->out, piece->value, piece->size); |
| } |
| else { |
| json_write_string(&c->out, "Address"); |
| write_stream(&c->out, ':'); |
| json_write_uint64(&c->out, piece->addr); |
| } |
| } |
| write_stream(&c->out, '}'); |
| } |
| write_stream(&c->out, ']'); |
| cnt++; |
| } |
| |
| if (value.big_endian) { |
| if (cnt > 0) write_stream(&c->out, ','); |
| json_write_string(&c->out, "BigEndian"); |
| write_stream(&c->out, ':'); |
| json_write_boolean(&c->out, 1); |
| cnt++; |
| } |
| } |
| |
| write_stream(&c->out, '}'); |
| write_stream(&c->out, 0); |
| } |
| write_stream(&c->out, MARKER_EOM); |
| command_done(); |
| } |
| |
| static void command_evaluate(char * token, Channel * c) { |
| CommandArgs args; |
| |
| json_read_string(&c->inp, args.id, sizeof(args.id)); |
| json_test_char(&c->inp, MARKER_EOA); |
| json_test_char(&c->inp, MARKER_EOM); |
| |
| strlcpy(args.token, token, sizeof(args.token)); |
| command_start(command_evaluate_cache_client, c, &args, sizeof(args)); |
| } |
| |
| static void command_assign_cache_client(void * x) { |
| CommandAssignArgs * args = (CommandAssignArgs *)x; |
| Channel * c = cache_channel(); |
| Context * ctx = NULL; |
| int frame = STACK_NO_FRAME; |
| Expression * e = NULL; |
| Value value; |
| int err = 0; |
| |
| memset(&value, 0, sizeof(value)); |
| if (expression_context_id(args->id, &ctx, &frame, &e) < 0) err = errno; |
| if (!err) { |
| check_all_stopped(ctx); |
| expression_context = ctx; |
| expression_frame = frame; |
| expression_addr = e->addr; |
| if (evaluate_script(MODE_NORMAL, e->script, 0, &value) < 0) err = errno; |
| } |
| if (!err) { |
| if (value.remote) { |
| if (context_write_mem(ctx, value.address, args->value_buf, args->value_size) < 0) err = errno; |
| #if SERVICE_Memory |
| if (!err) send_event_memory_changed(ctx, value.address, args->value_size); |
| #endif |
| } |
| else if (value.loc != NULL && value.loc->pieces_cnt > 0) { |
| Trap trap; |
| if (set_trap(&trap)) { |
| write_location_pieces(value.loc->ctx, value.loc->stack_frame, |
| value.loc->pieces, value.loc->pieces_cnt, |
| value.big_endian, args->value_buf, args->value_size); |
| #if SERVICE_Registers || SERVICE_Memory |
| { |
| unsigned i; |
| for (i = 0; i < value.loc->pieces_cnt; i++) { |
| LocationPiece * piece = value.loc->pieces + i; |
| assert(piece->optimized_away == 0); |
| #if SERVICE_Registers |
| if (piece->reg != NULL) { |
| send_event_register_changed(register2id(value.loc->ctx, |
| get_info_frame(value.loc->ctx, value.loc->stack_frame), piece->reg)); |
| } |
| #endif |
| #if SERVICE_Memory |
| if (piece->reg == NULL && piece->value == NULL) { |
| unsigned piece_size = piece->size ? piece->size : (piece->bit_offs + piece->bit_size + 7) / 8; |
| send_event_memory_changed(value.loc->ctx, piece->addr, piece_size); |
| } |
| #endif |
| } |
| } |
| #endif |
| clear_trap(&trap); |
| } |
| else { |
| err = trap.error; |
| } |
| } |
| else { |
| err = ERR_INV_EXPRESSION; |
| } |
| } |
| |
| cache_exit(); |
| |
| write_stringz(&c->out, "R"); |
| write_stringz(&c->out, args->token); |
| write_errno(&c->out, err); |
| write_stream(&c->out, MARKER_EOM); |
| loc_free(args->value_buf); |
| command_done(); |
| } |
| |
| static void command_assign(char * token, Channel * c) { |
| CommandAssignArgs args; |
| |
| json_read_string(&c->inp, args.id, sizeof(args.id)); |
| json_test_char(&c->inp, MARKER_EOA); |
| args.value_buf = json_read_alloc_binary(&c->inp, &args.value_size); |
| json_test_char(&c->inp, MARKER_EOA); |
| json_test_char(&c->inp, MARKER_EOM); |
| |
| strlcpy(args.token, token, sizeof(args.token)); |
| command_start(command_assign_cache_client, c, &args, sizeof(args)); |
| } |
| |
| static void command_dispose_cache_client(void * x) { |
| CommandAssignArgs * args = (CommandAssignArgs *)x; |
| Channel * c = cache_channel(); |
| Expression * e; |
| int err = 0; |
| |
| e = find_expression(args->id); |
| |
| cache_exit(); |
| |
| if (e != NULL) { |
| list_remove(&e->link_all); |
| list_remove(&e->link_id); |
| loc_free(e->script); |
| loc_free(e); |
| } |
| else { |
| err = ERR_INV_CONTEXT; |
| } |
| |
| write_stringz(&c->out, "R"); |
| write_stringz(&c->out, args->token); |
| write_errno(&c->out, err); |
| write_stream(&c->out, MARKER_EOM); |
| command_done(); |
| } |
| |
| static void command_dispose(char * token, Channel * c) { |
| CommandArgs args; |
| |
| json_read_string(&c->inp, args.id, sizeof(args.id)); |
| json_test_char(&c->inp, MARKER_EOA); |
| json_test_char(&c->inp, MARKER_EOM); |
| |
| strlcpy(args.token, token, sizeof(args.token)); |
| command_start(command_dispose_cache_client, c, &args, sizeof(args)); |
| } |
| |
| static void on_channel_close(Channel * c) { |
| LINK * l = expressions.next; |
| while (l != &expressions) { |
| Expression * e = link_all2exp(l); |
| l = l->next; |
| if (e->channel == c) { |
| list_remove(&e->link_all); |
| list_remove(&e->link_id); |
| loc_free(e->script); |
| loc_free(e); |
| } |
| } |
| } |
| |
| #endif /* SERVICE_Expressions */ |
| |
| void add_identifier_callback(ExpressionIdentifierCallBack * callback) { |
| if (id_callback_cnt >= id_callback_max) { |
| id_callback_max += 8; |
| id_callbacks = (ExpressionIdentifierCallBack **)loc_realloc( |
| id_callbacks, id_callback_max * sizeof(ExpressionIdentifierCallBack *)); |
| } |
| id_callbacks[id_callback_cnt++] = callback; |
| } |
| |
| #if SERVICE_Expressions |
| |
| #if ENABLE_FuncCallInjection |
| static void context_intercepted(Context * ctx, void * args) { |
| LINK * l = func_call_state.next; |
| while (l != &func_call_state) { |
| FuncCallState * state = link_all2fc(l); |
| l = l->next; |
| if (state->ctx == ctx && !state->intercepted) { |
| state->intercepted = 1; |
| if (!state->finished && state->error == NULL) { |
| state->error = get_error_report(set_errno(ERR_OTHER, |
| "Intercepted while executing injected function call")); |
| } |
| cache_notify(&state->cache); |
| } |
| } |
| } |
| #endif |
| |
| void ini_expressions_service(Protocol * proto) { |
| static int init = 0; |
| if (init == 0) { |
| unsigned i; |
| #if ENABLE_FuncCallInjection |
| static RunControlEventListener rc_listener = { context_intercepted, NULL }; |
| add_run_control_event_listener(&rc_listener, NULL); |
| #endif |
| #if ENABLE_ExpressionSerialization |
| list_init(&cmd_queue); |
| #endif |
| for (i = 0; i < ID2EXP_HASH_SIZE; i++) list_init(id2exp + i); |
| add_channel_close_listener(on_channel_close); |
| big_endian = big_endian_host(); |
| init = 1; |
| } |
| add_command_handler(proto, EXPRESSIONS, "getContext", command_get_context); |
| add_command_handler(proto, EXPRESSIONS, "getChildren", command_get_children); |
| add_command_handler(proto, EXPRESSIONS, "create", command_create); |
| add_command_handler(proto, EXPRESSIONS, "createInScope", command_create_in_scope); |
| add_command_handler(proto, EXPRESSIONS, "evaluate", command_evaluate); |
| add_command_handler(proto, EXPRESSIONS, "assign", command_assign); |
| add_command_handler(proto, EXPRESSIONS, "dispose", command_dispose); |
| } |
| |
| #endif /* if SERVICE_Expressions */ |
| #endif /* if ENABLE_Expressions */ |