/*************************************************************************** gbx_exec.c (c) 2000-2017 BenoƮt Minisini This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ***************************************************************************/ #define __GBX_EXEC_C #include "gb_common.h" #include "gb_error.h" #include "gbx_type.h" #include #include #include "gb_limit.h" #include "gbx_subr.h" #include "gbx_stack.h" #include "gbx_debug.h" #include "gbx_event.h" #include "gbx_string.h" #include "gbx_date.h" #include "gbx_c_collection.h" #include "gbx_api.h" #include "gbx_jit.h" #include "gbx_exec.h" //#define DEBUG_STACK 1 //#define SHOW_FUNCTION 1 STACK_CONTEXT EXEC_current = { 0 }; // Current virtual machine state VALUE *SP = NULL; // Stack pointer VALUE TEMP; // Temporary storage or return value of a native function VALUE RET; // Return value of a gambas function VALUE *EXEC_super = NULL; // SUPER was used for this stack pointer bool EXEC_big_endian; // CPU endianness CENUM *EXEC_enum; // Current iterator const char *EXEC_profile_path = NULL; // profile file path const char *EXEC_fifo_name = NULL; // fifo name EXEC_HOOK EXEC_Hook = { NULL }; EXEC_GLOBAL EXEC; uint64_t EXEC_byref = 0; unsigned char EXEC_quit_value = 0; // interpreter return value bool EXEC_debug = FALSE; // debugging mode bool EXEC_task = FALSE; // I am a background task bool EXEC_profile = FALSE; // profiling mode bool EXEC_profile_instr = FALSE; // profiling mode at instruction level bool EXEC_trace = FALSE; // tracing mode bool EXEC_arch = FALSE; // executing an archive bool EXEC_fifo = FALSE; // debugging through a fifo bool EXEC_keep_library = FALSE; // do not unload libraries bool EXEC_string_add = FALSE; // next '&' operator is done for a '&=' bool EXEC_main_hook_done = FALSE; bool EXEC_got_error = FALSE; bool EXEC_break_on_error = FALSE; // if we must break into the debugger as soon as there is an error. bool EXEC_in_event_loop = FALSE; // if we are in the event loop const char EXEC_should_borrow[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, 0, 0, 1 }; const char *EXEC_unknown_name; bool EXEC_unknown_property; char EXEC_unknown_nparam; void EXEC_init(void) { union { char _string[4]; uint _int; } test; PC = NULL; BP = NULL; OP = NULL; CP = NULL; RP->type = T_VOID; test._string[0] = 0xAA; test._string[1] = 0xBB; test._string[2] = 0xCC; test._string[3] = 0xDD; EXEC_big_endian = test._int == 0xAABBCCDDL; if (EXEC_big_endian) ERROR_warning("CPU is big endian"); DATE_init(); } void EXEC_borrow(TYPE type, VALUE *value) { static const void *jump[16] = { &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__STRING, &&__NONE, &&__NONE, &&__VARIANT, &&__FUNCTION, &&__NONE, &&__NONE }; goto *jump[type]; __VARIANT: if (value->_variant.vtype == T_STRING) STRING_ref(value->_variant.value._string); else if (TYPE_is_object(value->_variant.vtype)) OBJECT_REF(value->_variant.value._object); return; __FUNCTION: OBJECT_REF(value->_function.object); return; __STRING: STRING_ref(value->_string.addr); __NONE: return; } void UNBORROW(VALUE *value) { static const void *jump[16] = { &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__STRING, &&__NONE, &&__NONE, &&__VARIANT, &&__FUNCTION, &&__NONE, &&__NONE }; TYPE type = value->type; if (UNLIKELY(TYPE_is_object(type))) { OBJECT_UNREF_KEEP(value->_object.object); return; } goto *jump[type]; __VARIANT: if (value->_variant.vtype == T_STRING) STRING_unref_keep(&value->_variant.value._string); else if (TYPE_is_object(value->_variant.vtype)) OBJECT_UNREF_KEEP(value->_variant.value._object); return; __FUNCTION: OBJECT_UNREF_KEEP(value->_function.object); return; __STRING: STRING_unref_keep(&value->_string.addr); __NONE: return; } void EXEC_release(TYPE type, VALUE *value) { static const void *jump[16] = { &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__STRING, &&__NONE, &&__NONE, &&__VARIANT, &&__FUNCTION, &&__NONE, &&__NONE }; goto *jump[type]; __VARIANT: if (value->_variant.vtype == T_STRING) STRING_unref(&value->_variant.value._string); else if (TYPE_is_object(value->_variant.vtype)) OBJECT_UNREF(value->_variant.value._object); return; __FUNCTION: OBJECT_UNREF(value->_function.object); return; __STRING: STRING_unref(&value->_string.addr); __NONE: return; } void RELEASE_many(VALUE *value, int n) { static const void *jump[16] = { &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__STRING, &&__NONE, &&__NONE, &&__VARIANT, &&__FUNCTION, &&__NONE, &&__NONE }; TYPE type; while (n) { n--; value--; type = value->type; if (UNLIKELY(TYPE_is_object(type))) { OBJECT_UNREF(value->_object.object); continue; } goto *jump[type]; __VARIANT: if (value->_variant.vtype == T_STRING) STRING_unref(&value->_variant.value._string); else if (TYPE_is_object(value->_variant.vtype)) OBJECT_UNREF(value->_variant.value._object); continue; __FUNCTION: OBJECT_UNREF(value->_function.object); continue; __STRING: STRING_unref(&value->_string.addr); __NONE: continue; } } #if 0 void DUMP(VALUE *value) { static void *jump[16] = { &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__NONE, &&__STRING, &&__NONE, &&__VARIANT, &&__ARRAY, &&__NONE, &&__FUNCTION, &&__NONE, &&__NONE }; TYPE type = value->type; printf("type = %p / ", (void *)type); if (TYPE_is_object(type)) goto __OBJECT; else goto *jump[type]; __STRING: printf("STRING %p\n", value->_string.addr); return; __OBJECT: if (value->_object.object) { printf("OBJECT (%p)\n", value->_object.object); printf("-> %s\n", OBJECT_class(value->_object.object)->name); } else printf("OBJECT (NULL)\n"); return; __VARIANT: if (value->_variant.vtype == T_STRING) printf("STRING %p\n", *((char **)value->_variant.value)); else if (TYPE_is_object(value->_variant.vtype)) printf("OBJECT (%s %p)\n", OBJECT_class(*((void **)value->_variant.value))->name, *((void **)value->_variant.value)); return; __FUNCTION: printf("FUNCTION %s (%s %p)\n", value->_function.class->name, OBJECT_class(value->_function.object)->name, value->_function.object); return; __ARRAY: printf("ARRAY\n"); return; __NONE: printf("\n"); return; } #endif void EXEC_release_return_value(void) { RELEASE(RP); RP->type = T_VOID; } #define print_register() \ fprintf(stderr, "| SP = %d BP = %d FP = %p PC = %p EC = %p\n", (int)(SP - (VALUE *)STACK_base), (int)(BP - (VALUE *)STACK_base), FP, PC, EC) static ushort exec_enter_can_quick(void) { int i; FUNCTION *func; int nparam = EXEC.nparam; func = &EXEC.class->load->func[EXEC.index]; /* check number of arguments */ if (func->npmin < func->n_param || nparam != func->n_param || func->vararg) return C_CALL_SLOW; /* check arguments type */ for (i = 0; i < nparam; i++) { if (SP[i - nparam].type != func->param[i].type) return C_CALL_SLOW; } return C_CALL_QUICK; } static void init_local_var(CLASS *class, FUNCTION *func) { static const void *jump[] = { &&__VOID, &&__BOOLEAN, &&__BYTE, &&__SHORT, &&__INTEGER, &&__LONG, &&__SINGLE, &&__FLOAT, &&__DATE, &&__STRING, &&__STRING, &&__POINTER, &&__VARIANT, &&__FUNCTION, &&__CLASS, &&__NULL, &&__OBJECT }; CLASS_LOCAL *local; int n; CTYPE ctype; VALUE *value; local = func->local; value = SP; for (n = func->n_local; n; n--, value++, local++) { ctype = local->type; value->type = ctype.id; goto *jump[ctype.id]; __BOOLEAN: __BYTE: __SHORT: __INTEGER: value->_integer.value = 0; continue; __LONG: value->_long.value = 0; continue; __SINGLE: value->_single.value = 0; continue; __FLOAT: value->_float.value = 0; continue; __STRING: value->_string.addr = NULL; value->_string.start = 0; value->_string.len = 0; continue; __VARIANT: value->_variant.vtype = T_NULL; continue; __POINTER: value->_pointer.value = NULL; continue; __DATE: value->_date.date = 0; value->_date.time = 0; continue; __VOID: continue; __OBJECT: if (ctype.value >= 0) value->type = (TYPE)class->load->class_ref[ctype.value]; value->_object.object = NULL; continue; __FUNCTION: __CLASS: __NULL: ERROR_panic("VALUE_default: Unknown default type"); } SP = value; } // EXEC.nparam must be set to the amount of stack that must be freed if an exception is raised during EXEC_enter() void EXEC_enter(void) { int i; FUNCTION *func; // = EXEC.func; bool optional; int nparam = EXEC.nparam; void *object = EXEC.object; CLASS *class = EXEC.class; int64_t optargs; #if DEBUG_STACK fprintf(stderr, "\n| >> EXEC_enter(%s, %d, %d)\n", EXEC.class->name, EXEC.index, nparam); print_register(); #endif func = &class->load->func[EXEC.index]; #if DEBUG_STACK if (func->debug) fprintf(stderr, " | >> %s\n", func->debug->name); #endif #if SHOW_FUNCTION if (func->debug) fprintf(stderr, "%s.%s\n", EXEC.class->name, func->debug->name); #endif optional = func->optional; // Check number of arguments if (UNLIKELY(nparam < func->npmin)) THROW(E_NEPARAM); else if (UNLIKELY(nparam > func->n_param && !func->vararg)) THROW(E_TMPARAM); // Mandatory arguments for (i = 0; i < func->npmin; i++) VALUE_conv(SP - nparam + i, func->param[i].type); if (optional) { optargs = 0; // Optional arguments for (i = func->npmin; i < Min(func->n_param, nparam); i++) { if (SP[- nparam + i].type == T_VOID) { optargs |= (1 << i); SP[- nparam + i]._void.ptype = func->param[i].type; } else VALUE_conv(SP - nparam + i, func->param[i].type); } // Missing optional arguments if (nparam < func->n_param) { STACK_check(func->n_param - nparam); for (i = nparam; i < func->n_param; i++) { optargs |= (1 << i); SP->type = T_VOID; SP->_void.ptype = func->param[i].type; SP++; } //EXEC.nparam = func->n_param; nparam = func->n_param; } } // Save context & check stack STACK_push_frame(&EXEC_current, func->stack_usage); // Enter function BP = SP; if (func->vararg) PP = SP - (nparam - func->n_param); else PP = SP; FP = func; PC = func->code; OP = object; CP = class; EP = NULL; GP = NULL; PROFILE_ENTER_FUNCTION(); if (func->error) { #if DEBUG_ERROR printf("EXEC_enter: EC = PC + %d\n", func->error); #endif EC = PC + func->error; } else EC = NULL; // Reference the object so that it is not destroyed during the function call OBJECT_REF(OP); // Local variables initialization if (func->n_local > 0) init_local_var(class, func); // Control variables initialization if (func->n_ctrl > 0) { for (i = 0; i < func->n_ctrl; i++) { SP->type = T_VOID; SP++; } // Optional argument map if (optional && func->use_is_missing) { SP--; SP->type = T_LONG; SP->_long.value = optargs; SP++; } } RP->type = T_VOID; #if DEBUG_STACK fprintf(stderr, "| << EXEC_enter()\n"); print_register(); #endif } void EXEC_enter_check(bool defined) { ushort mode = defined ? exec_enter_can_quick() : C_CALL_SLOW; *PC = (*PC & 0xFF) | mode; switch(mode) { case C_CALL_QUICK: EXEC_enter_quick(); break; //case C_CALL_EASY: EXEC_enter_easy(); break; default: EXEC_enter(); break; } } void EXEC_enter_quick(void) { int i; FUNCTION *func;; void *object = EXEC.object; CLASS *class = EXEC.class; #if DEBUG_STACK fprintf(stderr, "\n| >> EXEC_enter_quick(%s, %d, %d)\n", EXEC.class->name, EXEC.index, EXEC.nparam); print_register(); #endif func = &class->load->func[EXEC.index]; #if DEBUG_STACK if (func->debug) fprintf(stderr, " | >> %s\n", func->debug->name); #endif #if SHOW_FUNCTION if (func->debug) fprintf(stderr, "%s.%s\n", EXEC.class->name, func->debug->name); #endif /* save context & check stack */ STACK_push_frame(&EXEC_current, func->stack_usage); /* enter function */ BP = SP; PP = SP; FP = func; PC = func->code; OP = object; CP = class; EP = NULL; GP = NULL; if (func->error) EC = PC + func->error; else EC = NULL; PROFILE_ENTER_FUNCTION(); /* reference the object so that it is not destroyed during the function call */ OBJECT_REF(OP); /* local variables initialization */ if (LIKELY(func->n_local != 0)) init_local_var(class, func); /* control variables initialization */ if (LIKELY(func->n_ctrl != 0)) { for (i = 0; i < func->n_ctrl; i++) { SP->type = T_VOID; SP++; } } RP->type = T_VOID; #if DEBUG_STACK fprintf(stderr, "| << EXEC_enter()\n"); print_register(); #endif } static int exec_leave_byref(ushort *pc, int nparam) { int nbyref; ushort *pc_func; int nbyref_func; VALUE *xp, *pp; int i, n, bit; int nb; pc++; nbyref = 1 + (*pc & 0xF); pc_func = FP->code; if (!PCODE_is(*pc_func, C_BYREF)) return 0; nbyref_func = 1 + (*pc_func & 0xF); if (nbyref_func < nbyref) return 0; for (i = 1; i <= nbyref; i++) { if (pc[i] & ~pc_func[i]) return 0; } xp = PP - nparam; pp = xp; for (i = 0, n = 0, nb = 0; i < nparam; i++) { bit = i & 15; if (bit == 0) n++; if (n <= nbyref && (pc[n] & (1 << bit))) { xp[nb] = *pp; nb++; } else { RELEASE(pp); } pp++; } pc--; n = SP - PP; RELEASE_MANY(SP, n); SP -= nparam; SP += nb; OBJECT_UNREF(OP); SP -= nb; PROFILE_LEAVE_FUNCTION(); STACK_pop_frame(&EXEC_current); PC += nbyref + 1; return nb; } #if 0 #define EXEC_LEAVE_BYREF() \ pc++; \ nbyref = 1 + (*pc & 0xF); \ pc_func = FP->code; \ \ if (LIKELY(!PCODE_is(*pc_func, C_BYREF))) \ goto __LEAVE_NORMAL; \ \ nbyref_func = 1 + (*pc_func & 0xF); \ if (nbyref_func < nbyref) \ goto __LEAVE_NORMAL; \ \ for (i = 1; i <= nbyref; i++) \ { \ if (pc[i] & ~pc_func[i]) \ goto __LEAVE_NORMAL; \ } \ \ xp = PP - nparam; \ pp = xp; \ \ for (i = 0, n = 0; i < nparam; i++) \ { \ bit = i & 15; \ if (bit == 0) \ n++; \ \ if (n <= nbyref && (pc[n] & (1 << bit))) \ { \ xp[nb] = *pp; \ nb++; \ } \ else \ { \ RELEASE(pp); \ } \ pp++; \ } \ \ pc--; \ n = SP - PP; \ RELEASE_MANY(SP, n); \ SP -= nparam; \ SP += nb; \ OBJECT_UNREF(OP); \ SP -= nb; \ PROFILE_LEAVE_FUNCTION(); \ STACK_pop_frame(&EXEC_current); \ PC += nbyref + 1; \ goto __RETURN_VALUE; #endif void EXEC_leave_drop() { int nparam; //VALUE ret; ushort *pc; int n, nb; #if DEBUG_STACK fprintf(stderr, "| >> EXEC_leave\n"); print_register(); #endif /* Save the return value. It can be erased by OBJECT_UNREF() */ //ret = *RP; EXEC_release_return_value(); //VALUE_copy(&ret, RP); pc = STACK_get_previous_pc(); nparam = FP->n_param; /* ByRef arguments management */ nb = (pc && PCODE_is(pc[1], C_BYREF)) ? exec_leave_byref(pc, nparam) : 0; if (nb == 0) { n = nparam + (SP - PP); RELEASE_MANY(SP, n); OBJECT_UNREF(OP); PROFILE_LEAVE_FUNCTION(); STACK_pop_frame(&EXEC_current); } SP += nb; #if DEBUG_STACK fprintf(stderr, "| << EXEC_leave()\n"); print_register(); fprintf(stderr, "\n"); #endif return; } void EXEC_leave_keep() { VALUE ret; int nparam; ushort *pc; int n, nb; #if DEBUG_STACK fprintf(stderr, "| >> EXEC_leave\n"); print_register(); #endif // RP may be indirectly freed by OBJECT_UNREF() VALUE_copy(&ret, RP); pc = STACK_get_previous_pc(); nparam = FP->n_param; // ByRef arguments management nb = (pc && PCODE_is(pc[1], C_BYREF)) ? exec_leave_byref(pc, nparam) : 0; if (nb == 0) { n = nparam + (SP - PP); RELEASE_MANY(SP, n); OBJECT_UNREF(OP); PROFILE_LEAVE_FUNCTION(); STACK_pop_frame(&EXEC_current); } if (pc) { if (SP[-1].type == T_FUNCTION) { SP--; OBJECT_UNREF(SP->_function.object); } COPY_VALUE(SP, &ret); RP->type = T_VOID; if (PCODE_is_variant(*PC) && SP->type != T_VOID) VALUE_conv_variant(SP); SP++; } else { VALUE_copy(RP, &ret); } SP += nb; #if DEBUG_STACK fprintf(stderr, "| << EXEC_leave()\n"); print_register(); fprintf(stderr, "\n"); #endif return; } static void error_EXEC_function_real(void) { RELEASE_MANY(SP, EXEC.nparam); STACK_pop_frame(&EXEC_current); } void EXEC_function_real() { EXEC.func = &EXEC.class->load->func[EXEC.index]; if (EXEC.func->fast && !JIT_exec(FALSE)) return; // We need to push a void frame, because EXEC_leave looks at *PC to know if a return value is expected STACK_push_frame(&EXEC_current, 0); PC = NULL; GP = NULL; ON_ERROR(error_EXEC_function_real) { EXEC_enter(); } END_ERROR EXEC_function_loop(); } void EXEC_function_loop() { bool retry = FALSE; if (LIKELY(PC != NULL)) { do { TRY { EXEC_loop(); retry = FALSE; } CATCH { // QUIT was called if (ERROR->info.code == E_ABORT) { #if DEBUG_ERROR fprintf(stderr, "#0 QUIT\n"); #endif ERROR_lock(); while (PC != NULL) EXEC_leave_drop(); ERROR_unlock(); //STACK_pop_frame(&EXEC_current); PROPAGATE(); } if (EXEC_break_on_error) DEBUG.Main(TRUE); if (ERROR->info.code == E_ASSERT) { EP = NULL; EC = NULL; goto __IGNORE_TRY_CATCH; } // Are we in a TRY? if (EP && EC) { #if DEBUG_ERROR fprintf(stderr, "#1 EP = %d SP = %d\n", EP - (VALUE *)STACK_base, SP - (VALUE *)STACK_base); fprintf(stderr, "TRY\n"); #endif ERROR_set_last(FALSE); // No need to unwind the Gosub stack until the TRY stack position, because TRY GOSUB is forbidden /*while (GP > EP) { ... }*/ // The stack is popped until reaching the stack position before the TRY while (SP > EP) POP(); // We go directly to the END TRY PC = EC; EP = NULL; retry = TRUE; goto __CONTINUE; } // Is there a CATCH in the function? if (EC != NULL) { #if DEBUG_ERROR fprintf(stderr, "#2 EC = %p\n", EC); fprintf(stderr, "CATCH\n"); #endif ERROR_set_last(TRUE); // The stack is popped until reaching the stack position at the function start while (SP > (BP + FP->n_local + FP->n_ctrl)) POP(); // Reset the Gosub stack pointer as all Gosub control variables saved have been released GP = NULL; PC = EC; EC = NULL; retry = TRUE; goto __CONTINUE; } __IGNORE_TRY_CATCH: // There is no error handler in the function #if DEBUG_ERROR fprintf(stderr, "#3\n"); fprintf(stderr, "NOTHING\n"); #endif //ERROR_INFO save = { 0 }; //ERROR_save(&save); ERROR_set_last(TRUE); if (EXEC_debug && !FP->fast && !STACK_has_error_handler()) { ERROR_hook(); for(;;) DEBUG.Main(TRUE); } else { // We leave stack frames until we find either: // - A stack frame that has an error handler. // - A void stack frame created by EXEC_enter_real() // First we leave stack frames for JIT functions // on top of the stack. We have just propagated // past these some lines below. /*ERROR_lock(); while (PC != NULL && EC == NULL && FP->fast) EXEC_leave_drop(); ERROR_unlock();*/ // We can only leave stack frames for non-JIT functions. ERROR_lock(); while (PC != NULL && EC == NULL && !FP->fast) EXEC_leave_drop(); ERROR_unlock(); if (FP && FP->fast) PROPAGATE(); // If we got the void stack frame, then we remove it and raise the error again if (PC == NULL) { STACK_pop_frame(&EXEC_current); PROPAGATE(); } // We have a TRY too, handle it. if (EP != NULL) { #if DEBUG_ERROR fprintf(stderr, "#4 EP = %d SP = %d\n", EP - (VALUE *)STACK_base, SP - (VALUE *)STACK_base); #endif ERROR_lock(); while (SP > EP) POP(); ERROR_unlock(); EP = NULL; /* On va directement sur le END TRY */ } // Now we can handle the CATCH // The stack is popped until reaching the stack position at the function start ERROR_lock(); #if DEBUG_ERROR DEBUG_where(); fprintf(stderr, "#5 BP + local + ctrl = %d SP = %d\n", BP + FP->n_local + FP->n_ctrl - (VALUE *)STACK_base, SP - (VALUE *)STACK_base); #endif while (SP > (BP + FP->n_local + FP->n_ctrl)) POP(); ERROR_unlock(); PC = EC; EC = NULL; retry = TRUE; } //ERROR_restore(&save); //ERROR_set_last(); __CONTINUE: while (SP < EXEC_super) EXEC_super = ((VALUE *)EXEC_super)->_object.super; } END_TRY #if DEBUG_ERROR if (retry) fprintf(stderr, "RETRY %p\n", PC); #endif } while (retry); } if (PC == NULL) STACK_pop_frame(&EXEC_current); } static ushort exec_native_can_quick(void) { CLASS_DESC_METHOD *desc = EXEC.desc; int i; int nparam = EXEC.nparam; /* check number of arguments */ //fprintf(stderr, "exec_native_can_quick: %s.%s(%d) npmin:%d npmax:%d npvar:%d\n", desc->class->name, desc->name, nparam, desc->npmin, desc->npmax, desc->npvar); if (desc->npmin < desc->npmax || nparam != desc->npmin || desc->npvar) return C_CALL_SLOW; /* check arguments type */ for (i = 0; i < nparam; i++) { if (SP[i - nparam].type != desc->signature[i]) return C_CALL_SLOW; } return C_CALL_QUICK; } void EXEC_native_check(bool defined) { ushort mode = defined ? exec_native_can_quick() : C_CALL_SLOW; *PC = (*PC & 0xFF) | mode; switch(mode) { case C_CALL_QUICK: EXEC_native_quick(); break; //case C_CALL_EASY: EXEC_native_easy(); break; default: EXEC_native(); break; } } #define EXEC_call_native_inline(_exec, _object, _type, _param) \ ({ \ EXEC_set_native_error(FALSE); \ (*(_exec))((_object), (void *)(_param)); \ \ if (EXEC_has_native_error()) \ { \ EXEC_set_native_error(FALSE); \ error = TRUE; \ } \ else \ { \ TYPE __type = (_type); \ if (TYPE_is_pure_object(__type) && TEMP.type != T_NULL && TEMP.type != T_VOID) \ { \ if (TEMP.type == T_CLASS) \ TEMP._class.class = (CLASS *)__type; \ else \ TEMP.type = __type; \ \ error = FALSE; \ } \ else \ error = FALSE; \ } \ }) bool EXEC_call_native(void (*exec)(), void *object, TYPE type, VALUE *param) { bool error; EXEC_call_native_inline(exec, object, type, param); return error; } void EXEC_native_quick(void) { CLASS_DESC_METHOD *desc = EXEC.desc; int nparam = EXEC.nparam; bool error; //void *free_later; VALUE ret; EXEC_call_native_inline(desc->exec, EXEC.object, desc->type, &SP[-nparam]); COPY_VALUE(&ret, &TEMP); if (UNLIKELY(error)) { RELEASE_MANY(SP, nparam); POP(); PROPAGATE(); } if (desc->type == T_VOID) { RELEASE_MANY(SP, nparam); SP--; OBJECT_UNREF(SP->_function.object); SP->type = T_VOID; SP->_void.ptype = T_NULL; SP++; } else { BORROW(&ret); RELEASE_MANY(SP, nparam); SP--; OBJECT_UNREF(SP->_function.object); COPY_VALUE(SP, &ret); SP++; } #if DEBUG_STACK fprintf(stderr, "| << EXEC_native: %s (%p)\n", desc->name, &desc); #endif } static void error_EXEC_native(intptr_t nparam) { RELEASE_MANY(SP, (int)nparam); } void EXEC_native(void) { CLASS_DESC_METHOD *desc = EXEC.desc; int nparam = EXEC.nparam; void *object = EXEC.object; bool use_stack = EXEC.use_stack; int i, n, nm; VALUE *value; TYPE *sign; bool error; VALUE ret; #if DEBUG_STACK fprintf(stderr, "| >> EXEC_native: %s.%s (%p)\n", EXEC.class->name, desc->name, &desc); #endif ON_ERROR_1(error_EXEC_native, nparam) { n = desc->npmin; nm = desc->npmax; if (UNLIKELY(nparam < n)) THROW(E_NEPARAM); if (LIKELY(!desc->npvar)) { if (UNLIKELY(nparam > nm)) THROW(E_TMPARAM); value = &SP[-nparam]; sign = desc->signature; for (i = 0; i < n; i++, value++, sign++) VALUE_conv(value, *sign); if (UNLIKELY(n < nm)) { for (; i < nparam; i++, value++, sign++) { if (value->type != T_VOID) VALUE_conv(value, *sign); } n = nm - nparam; /*if (UNLIKELY(STACK_check(n))) { STACK_RELOCATE(value); }*/ SP += n; nparam = nm; for (; i < nparam; i++, value++) value->type = T_VOID; } } else { value = &SP[-nparam]; sign = desc->signature; for (i = 0; i < n; i++, value++, sign++) VALUE_conv(value, *sign); nm = desc->npmax; if (UNLIKELY(n < nm)) { if (UNLIKELY(nparam < nm)) { for (; i < nparam; i++, value++, sign++) { if (value->type != T_VOID) VALUE_conv(value, *sign); } n = nm - nparam; /*if (UNLIKELY(STACK_check(n))) { STACK_RELOCATE(value); }*/ SP += n; nparam = nm; for (; i < nparam; i++, value++) value->type = T_VOID; } else { n = desc->npmax; for (; i < n; i++, value++, sign++) { if (value->type != T_VOID) VALUE_conv(value, *sign); } } } EXEC_unknown_nparam = nparam <= nm ? 0 : nparam - nm; for (; i < nparam; i++, value++) VARIANT_undo(value); //printf("EXEC_native: nparvar = %d\n", EXEC.nparvar); } } END_ERROR EXEC_call_native_inline(desc->exec, object, desc->type, &SP[-nparam]); COPY_VALUE(&ret, &TEMP); if (UNLIKELY(error)) { RELEASE_MANY(SP, nparam); if (use_stack) { SP--; OBJECT_UNREF(SP->_function.object); } PROPAGATE(); } // If the function description is on the stack if (desc->type == T_VOID) { RELEASE_MANY(SP, nparam); if (use_stack) { SP--; OBJECT_UNREF(SP->_function.object); } SP->type = T_VOID; SP->_void.ptype = T_NULL; SP++; //UNBORROW(&ret); } else { BORROW(&ret); RELEASE_MANY(SP, nparam); if (use_stack) { SP--; OBJECT_UNREF(SP->_function.object); if (PCODE_is_variant(*PC) && ret.type != T_VOID) VALUE_conv_variant(&ret); } COPY_VALUE(SP, &ret); SP++; } #if DEBUG_STACK fprintf(stderr, "| << EXEC_native: %s (%p)\n", desc->name, &desc); #endif } CLASS *EXEC_object_real(VALUE *val) { CLASS *class; OBJECT *object; object = val->_object.object; class = val->_object.class; if (!object) { /* A null object and a virtual class means that we want to pass a static class */ if (!class->is_virtual) THROW_NULL(); CLASS_load(class); goto __RETURN; } if (val == EXEC_super) EXEC_super = val->_object.super; else if (!class->is_virtual) class = object->class; //CLASS_load(class); If we have an object, the class is necessarily loaded. if (UNLIKELY(class->must_check && (*(class->check))(object))) THROW(E_IOBJECT); __RETURN: return class; } CLASS *EXEC_object_variant(VALUE *val, OBJECT **pobject) { CLASS *class; OBJECT *object; if (TYPE_is_pure_object(val->_variant.vtype)) { object = val->_variant.value._object; if (!object) goto __NULL; class = (CLASS *)val->_variant.vtype; if (!class->is_virtual) class = object->class; /* Virtual dispatching */ goto __CHECK; } else if (val->_variant.vtype == T_OBJECT) { object = val->_variant.value._object; if (!object) goto __NULL; class = object->class; goto __CHECK; } else if (val->_variant.vtype == T_STRING || val->_variant.vtype == T_CSTRING) { *pobject = NULL; return CLASS_BoxedString; } else goto __ERROR; __ERROR: THROW(E_NOBJECT); __NULL: THROW_NULL(); __CHECK: //CLASS_load(class); //If we have an object, the class is not necessarily loaded? if (UNLIKELY(class->must_check && (*(class->check))(object))) THROW(E_IOBJECT); *pobject = object; return class; } bool EXEC_object_other(VALUE *val, CLASS **pclass, OBJECT **pobject) { static const void *jump[] = { &&__ERROR, &&__ERROR, &&__ERROR, &&__ERROR, &&__ERROR, &&__ERROR, &&__ERROR, &&__ERROR, &&__ERROR, &&__STRING, &&__CSTRING, &&__ERROR, &&__ERROR, &&__FUNCTION, &&__CLASS, &&__NULL, &&__OBJECT, }; CLASS *class; OBJECT *object; bool defined; goto *jump[val->type]; __FUNCTION: if (LIKELY(val->_function.kind == FUNCTION_UNKNOWN)) { EXEC_unknown_property = TRUE; EXEC_unknown_name = CP->load->unknown[val->_function.index]; EXEC_special(SPEC_UNKNOWN, val->_function.class, val->_function.object, 0, FALSE); object = val->_function.object; OBJECT_UNREF(object); SP--; //*val = *SP; COPY_VALUE(val, SP); EXEC_object(val, pclass, pobject); return FALSE; // Could be TRUE, but always returning FALSE allows optimizations in quick array management } else goto __ERROR; __CLASS: class = val->_class.class; object = NULL; defined = TRUE; if (val == EXEC_super) { EXEC_super = val->_class.super; //*class = (*class)->parent; if (UNLIKELY(class == NULL)) THROW(E_PARENT); } CLASS_load(class); goto __RETURN; __OBJECT: object = val->_object.object; if (!object) goto __NULL; class = object->class; defined = FALSE; goto __CHECK; __STRING: __CSTRING: *pclass = CLASS_BoxedString; *pobject = NULL; return TRUE; __ERROR: THROW(E_NOBJECT); __NULL: THROW_NULL(); __CHECK: //CLASS_load(class); If we have an object, the class is necessarily loaded. if (UNLIKELY(class->must_check && (*(class->check))(object))) THROW(E_IOBJECT); __RETURN: *pclass = class; *pobject = object; return defined; } void EXEC_public_desc(CLASS *class, void *object, CLASS_DESC_METHOD *desc, int nparam) { EXEC.object = object; EXEC.nparam = nparam; /*desc->npmin;*/ if (FUNCTION_is_native(desc)) { EXEC.class = class; // EXEC_native() does not need the real class, except the GB.GetClass(NULL) API used by Form.Main. EXEC.native = TRUE; EXEC.use_stack = FALSE; EXEC.desc = desc; EXEC_native(); SP--; *RP = *SP; SP->type = T_VOID; } else { EXEC.class = desc->class; // EXEC_function_real() needs the effective class, because the method can be an inherited one! EXEC.native = FALSE; EXEC.index = (int)(intptr_t)desc->exec; EXEC_function_keep(); } } void EXEC_public(CLASS *class, void *object, const char *name, int nparam) { CLASS_DESC *desc; desc = CLASS_get_symbol_desc_kind(class, name, (object != NULL) ? CD_METHOD : CD_STATIC_METHOD, 0); if (UNLIKELY(desc == NULL)) return; EXEC_public_desc(class, object, &desc->method, nparam); EXEC_release_return_value(); } bool EXEC_special(int special, CLASS *class, void *object, int nparam, bool drop) { CLASS_DESC *desc; short index = class->special[special]; if (index == NO_SYMBOL) return TRUE; desc = CLASS_get_desc(class, index); if (CLASS_DESC_get_type(desc) == CD_STATIC_METHOD) { if (object != NULL) return TRUE; } else { if (object == NULL) { if (class->auto_create) object = EXEC_auto_create(class, FALSE); if (object == NULL) THROW(E_NOBJECT); } } EXEC.class = desc->method.class; EXEC.object = object; EXEC.nparam = nparam; /*printf("<< EXEC_spec: SP = %d\n", SP - (VALUE *)STACK_base); save_SP = SP;*/ if (FUNCTION_is_native(&desc->method)) { if (desc->method.subr) { ((EXEC_FUNC_CODE)(EXEC.class->table[index].desc->method.exec))(nparam); } else { EXEC.desc = &desc->method; EXEC.use_stack = FALSE; EXEC.native = TRUE; EXEC_native(); } if (drop) POP(); } else { //EXEC.func = &class->load->func[(long)desc->method.exec] EXEC.index = (int)(intptr_t)desc->method.exec; EXEC.native = FALSE; if (drop) EXEC_function(); else { EXEC_function_keep(); //*SP++ = *RP; COPY_VALUE(SP, RP); SP++; RP->type = T_VOID; } } /*printf(">> EXEC_spec: SP = %d\n", SP - (VALUE *)STACK_base); if (SP != save_SP) printf("**** SP should be %d\n", save_SP - (VALUE *)STACK_base);*/ return FALSE; } /* The highest parent method is called first, but get only the parameters not consumed by the child methods. */ void EXEC_special_inheritance(int special, CLASS *class, OBJECT *object, int nparam, bool drop) { CLASS *her[MAX_INHERITANCE]; int nher; int i, np, npopt, nparam_opt, save_nparam; CLASS_DESC *desc; short index; int arg, opt; VALUE *base; if (!class->parent) { if (special == SPEC_NEW && class->init_dynamic) { EXEC.class = class; EXEC.object = object; EXEC.index = FUNC_INIT_DYNAMIC; EXEC.native = FALSE; EXEC.nparam = 0; EXEC_function(); } if (EXEC_special(special, class, object, nparam, drop)) { if (nparam) THROW(E_TMPARAM); } return; } nher = CLASS_get_inheritance(class, her); for(i = 0, np = 0; i < nher; i++) { class = her[i]; index = class->special[special]; if (index == NO_SYMBOL) continue; desc = CLASS_get_desc(class, index); //class->special[special]; np += desc->method.npmin; } if (UNLIKELY(np > nparam)) THROW(E_NEPARAM); save_nparam = nparam; arg = - nparam; opt = arg + np; nparam_opt = nparam - np; nparam = np; // nparam is now the number of mandatory arguments // naram_opt is the number of optional arguments for(;;) { nher--; if (nher < 0) break; class = her[nher]; if (special == SPEC_NEW) { if (class->init_dynamic) { EXEC.class = class; EXEC.object = object; EXEC.index = FUNC_INIT_DYNAMIC; //EXEC.func = &class->load->func[FUNC_INIT_DYNAMIC]; EXEC.native = FALSE; EXEC.nparam = 0; EXEC_function(); } } index = class->special[special]; if (index == NO_SYMBOL) continue; desc = CLASS_get_desc(class, index); // class->special[special]; if (nher) { np = desc->method.npmin; if (np > nparam) np = nparam; nparam -= np; npopt = desc->method.npmax - desc->method.npmin; if (npopt > nparam_opt) npopt = nparam_opt; nparam_opt -= npopt; } else { np = nparam; npopt = nparam_opt; } if (np > 0 || npopt > 0) { STACK_check(np + npopt); base = SP; for (i = 0; i < np; i++) { *SP++ = base[arg]; base[arg].type = T_NULL; arg++; } for (i = 0; i < npopt; i++) { *SP++ = base[opt]; base[opt].type = T_NULL; opt++; } } EXEC_special(special, class, object, np + npopt, drop); } SP -= save_nparam; } void *EXEC_create_object(CLASS *class, int np, char *event) { void *object; CLASS_load(class); if (UNLIKELY(class->no_create)) THROW(E_CSTATIC, CLASS_get_name(class)); object = OBJECT_new(class, event, ((OP == NULL) ? (OBJECT *)CP : (OBJECT *)OP)); TRY { OBJECT_lock(object, TRUE); EXEC_special_inheritance(SPEC_NEW, class, object, np, TRUE); OBJECT_lock(object, FALSE); EXEC_special(SPEC_READY, class, object, 0, TRUE); } CATCH { // _free() methods should not be called, but we must OBJECT_UNREF(object); PROPAGATE(); } END_TRY return object; } void EXEC_new(ushort code) { CLASS *class; int np; bool event; void *object; char *name = NULL; char *cname = NULL; char *save; np = code & 0xFF; event = np & CODE_NEW_EVENT; np &= 0x3F; /* Instanciation */ SP -= np; if (SP->type == T_CLASS) { class = SP->_class.class; //if (UNLIKELY(class->override != NULL)) // class = class->override; } else if (TYPE_is_string(SP->type)) { cname = STRING_copy_from_value_temp(SP); class = CLASS_find(cname); RELEASE_STRING(SP); SP->type = T_NULL; } else THROW_TYPE(T_STRING, SP->type); SP += np; //printf("**** NEW %s\n", class->name); CLASS_load(class); if (UNLIKELY(class->no_create)) THROW(E_CSTATIC, CLASS_get_name(class)); if (event) { SP--; if (!TYPE_is_string(SP->type)) THROW_TYPE(T_STRING, SP->type); name = STRING_copy_from_value_temp(SP); //printf("**** name %s\n", class->name); STRING_ref(name); SP++; object = OBJECT_new(class, name, ((OP == NULL) ? (OBJECT *)CP : (OBJECT *)OP)); SP--; STRING_unref(&name); RELEASE_STRING(SP); np -= 2; } else { object = OBJECT_new(class, name, ((OP == NULL) ? (OBJECT *)CP : (OBJECT *)OP)); np--; } save = EVENT_enter_name(name); TRY { OBJECT_lock(object, TRUE); EXEC_special_inheritance(SPEC_NEW, class, object, np, TRUE); OBJECT_lock(object, FALSE); EVENT_leave_name(save); SP--; /* class */ EXEC_special(SPEC_READY, class, object, 0, TRUE); SP->_object.class = class; SP->_object.object = object; SP++; } CATCH { EVENT_leave_name(save); // _free() methods should not be called, but we must OBJECT_UNREF(object); //(*class->free)(class, object); SP--; /* class */ SP->type = T_NULL; SP++; PROPAGATE(); } END_TRY } void EXEC_do_quit(void) { GAMBAS_DoNotRaiseEvent = TRUE; HOOK(quit)(); THROW(E_ABORT); } void *EXEC_auto_create(CLASS *class, bool ref) { void *object; object = CLASS_auto_create(class, 0); /* object is checked by CLASS_auto_create */ /*if (UNLIKELY(class->must_check && (*(class->check))(object))) THROW(E_IOBJECT);*/ if (ref) OBJECT_REF(object); return object; } void EXEC_dup(int n) { VALUE *src; STACK_check(n); src = SP - n; while (n > 0) { BORROW(src); *SP++ = *src++; n--; } } void EXEC_push_complex(void) { static void *(*func)(double) = NULL; void *ob; SP--; if (SP->type < T_INTEGER || SP->type > T_FLOAT) THROW_ILLEGAL(); SP++; if (!func) { if (COMPONENT_get_info("PUSH_COMPLEX", POINTER(&func))) { COMPONENT_load(COMPONENT_create("gb.complex")); if (COMPONENT_get_info("PUSH_COMPLEX", POINTER(&func))) THROW(E_MATH); } } SP--; VALUE_conv_float(SP); ob = (*func)(SP->_float.value); SP->_object.object = ob; SP->_object.class = OBJECT_class(ob); OBJECT_REF(ob); SP++; } void EXEC_push_vargs(void) { int i; int nargs = (FP && FP->vararg) ? BP - PP : 0; if (nargs == 0) return; STACK_check(nargs); for (i = 0; i < nargs; i++) { *SP = PP[i]; PUSH(); } PC[1] += nargs; } void EXEC_end_vargs(void) { int nargs = (FP && FP->vararg) ? BP - PP : 0; PC[-1] -= nargs; } void EXEC_drop_vargs(void) { int nargs = (FP && FP->vararg) ? BP - PP : 0; RELEASE_MANY(SP, nargs); PC[-1] -= nargs; }