/* * $Id$ * Copyright (c) 2014-2019 Chung, Hyung-Hwan. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "moo-prv.h" #define PROC_STATE_RUNNING 3 #define PROC_STATE_WAITING 2 #define PROC_STATE_RUNNABLE 1 #define PROC_STATE_SUSPENDED 0 #define PROC_STATE_TERMINATED -1 static const char* io_type_str[] = { "input", "output" }; static MOO_INLINE const char* proc_state_to_string (int state) { static const moo_bch_t* str[] = { "TERMINATED", "SUSPENDED", "RUNNABLE", "WAITING", "RUNNING" }; return str[state + 1]; } /* TODO: adjust this process map increment value */ #define PROC_MAP_INC 64 /* TODO: adjust these max semaphore pointer buffer capacity, * proably depending on the object memory size? */ #define SEM_LIST_INC 256 #define SEM_HEAP_INC 256 #define SEM_IO_TUPLE_INC 256 #define SEM_LIST_MAX (SEM_LIST_INC * 1000) #define SEM_HEAP_MAX (SEM_HEAP_INC * 1000) #define SEM_IO_TUPLE_MAX (SEM_IO_TUPLE_INC * 1000) #define SEM_IO_MAP_ALIGN 1024 /* this must a power of 2 */ #define SEM_HEAP_PARENT(x) (((x) - 1) / 2) #define SEM_HEAP_LEFT(x) ((x) * 2 + 1) #define SEM_HEAP_RIGHT(x) ((x) * 2 + 2) #define SEM_HEAP_EARLIER_THAN(stx,x,y) ( \ (MOO_OOP_TO_SMOOI((x)->u.timed.ftime_sec) < MOO_OOP_TO_SMOOI((y)->u.timed.ftime_sec)) || \ (MOO_OOP_TO_SMOOI((x)->u.timed.ftime_sec) == MOO_OOP_TO_SMOOI((y)->u.timed.ftime_sec) && MOO_OOP_TO_SMOOI((x)->u.timed.ftime_nsec) < MOO_OOP_TO_SMOOI((y)->u.timed.ftime_nsec)) \ ) #define LOAD_IP(moo, v_ctx) ((moo)->ip = MOO_OOP_TO_SMOOI((v_ctx)->ip)) #define STORE_IP(moo, v_ctx) ((v_ctx)->ip = MOO_SMOOI_TO_OOP((moo)->ip)) #define LOAD_SP(moo, v_ctx) ((moo)->sp = MOO_OOP_TO_SMOOI((v_ctx)->sp)) #define STORE_SP(moo, v_ctx) ((v_ctx)->sp = MOO_SMOOI_TO_OOP((moo)->sp)) #define LOAD_ACTIVE_IP(moo) LOAD_IP(moo, (moo)->active_context) #define STORE_ACTIVE_IP(moo) STORE_IP(moo, (moo)->active_context) #define LOAD_ACTIVE_SP(moo) LOAD_SP(moo, (moo)->processor->active) #define STORE_ACTIVE_SP(moo) STORE_SP(moo, (moo)->processor->active) #define SWITCH_ACTIVE_CONTEXT(moo,v_ctx) \ do { \ STORE_ACTIVE_IP (moo); \ (moo)->active_context = (v_ctx); \ (moo)->active_method = (moo_oop_method_t)(moo)->active_context->origin->method_or_nargs; \ (moo)->active_code = MOO_METHOD_GET_CODE_BYTE((moo)->active_method); \ LOAD_ACTIVE_IP (moo); \ MOO_STORE_OOP (moo, (moo_oop_t*)&(moo)->processor->active->current_context, (moo_oop_t)(moo)->active_context); \ } while (0) #define FETCH_BYTE_CODE(moo) ((moo)->active_code[(moo)->ip++]) #if (MOO_BCODE_LONG_PARAM_SIZE == 2) # define FETCH_PARAM_CODE_TO(moo, v_ooi) \ do { \ v_ooi = FETCH_BYTE_CODE(moo); \ v_ooi = (v_ooi << 8) | FETCH_BYTE_CODE(moo); \ } while (0) #else # define FETCH_PARAM_CODE_TO(moo, v_ooi) (v_ooi = FETCH_BYTE_CODE(moo)) #endif #if defined(MOO_DEBUG_VM_EXEC) # define LOG_MASK_INST (MOO_LOG_IC | MOO_LOG_MNEMONIC | MOO_LOG_DEBUG) /* TODO: for send_message, display the method name. or include the method name before 'ip' */ # define LOG_INST0(moo,fmt) MOO_LOG1(moo, LOG_MASK_INST, " %06zd " fmt "\n", (moo)->last_inst_pointer) # define LOG_INST1(moo,fmt,a1) MOO_LOG2(moo, LOG_MASK_INST, " %06zd " fmt "\n",(moo)->last_inst_pointer, a1) # define LOG_INST2(moo,fmt,a1,a2) MOO_LOG3(moo, LOG_MASK_INST, " %06zd " fmt "\n", (moo)->last_inst_pointer, a1, a2) # define LOG_INST3(moo,fmt,a1,a2,a3) MOO_LOG4(moo, LOG_MASK_INST, " %06zd " fmt "\n", (moo)->last_inst_pointer, a1, a2, a3) #else # define LOG_INST0(moo,fmt) # define LOG_INST1(moo,fmt,a1) # define LOG_INST2(moo,fmt,a1,a2) # define LOG_INST3(moo,fmt,a1,a2,a3) #endif #if defined(__DOS__) && (defined(_INTELC32_) || (defined(__WATCOMC__) && (__WATCOMC__ <= 1000))) /* the old intel c code builder doesn't support __FUNCTION__ */ # define __PRIMITIVE_NAME__ "<>" #elif defined(_SCO_DS) # define __PRIMITIVE_NAME__ "<>" #elif defined(__BORLANDC__) && (__BORLANDC__ <= 0x520) # define __PRIMITIVE_NAME__ "<>" #else # define __PRIMITIVE_NAME__ (&__FUNCTION__[0]) #endif static int delete_sem_from_sem_io_tuple (moo_t* moo, moo_oop_semaphore_t sem, int force); static void signal_io_semaphore (moo_t* moo, moo_ooi_t io_handle, moo_ooi_t mask); static void terminate_all_processes (moo_t* moo); static int send_message (moo_t* moo, moo_oop_char_t selector, moo_ooi_t nargs, int to_super); /* ------------------------------------------------------------------------- */ static MOO_INLINE int vm_startup (moo_t* moo) { moo_oow_t i; MOO_DEBUG0 (moo, "VM started up\n"); for (i = 0; i < moo->sem_io_map_capa; i++) { moo->sem_io_map[i] = -1; } moo->sem_gcfin = (moo_oop_semaphore_t)moo->_nil; moo->sem_gcfin_sigreq = 0; if (moo->vmprim.vm_startup(moo) <= -1) return -1; moo->vmprim.vm_gettime (moo, &moo->exec_start_time); /* raw time. no adjustment */ return 0; } static MOO_INLINE void vm_cleanup (moo_t* moo) { moo_oow_t i; /* TODO: clean up semaphores being waited on MOO_ASSERT (moo, moo->sem_io_wait_count == 0); */ if (moo->processor->total_count != MOO_SMOOI_TO_OOP(0)) { /* the stock kernel code is supposed to stop all processes. * if the code reaches here, the kernel code must be buggy */ MOO_LOG3 (moo, MOO_LOG_WARN, "Warning - non-zero number of processes upon VM clean-up - total: %zd runnable: %zd suspended: %zd\n", (moo_ooi_t)MOO_OOP_TO_SMOOI(moo->processor->total_count), (moo_ooi_t)MOO_OOP_TO_SMOOI(moo->processor->runnable.count), (moo_ooi_t)MOO_OOP_TO_SMOOI(moo->processor->suspended.count)); MOO_LOG0 (moo, MOO_LOG_WARN, "Warning - terminating all residue processes\n"); terminate_all_processes (moo); } MOO_ASSERT (moo, moo->processor->active == moo->nil_process); MOO_ASSERT (moo, MOO_OOP_TO_SMOOI(moo->processor->total_count) == 0); MOO_ASSERT (moo, MOO_OOP_TO_SMOOI(moo->processor->runnable.count) == 0); MOO_ASSERT (moo, MOO_OOP_TO_SMOOI(moo->processor->suspended.count) == 0); for (i = 0; i < moo->sem_io_map_capa;) { moo_ooi_t sem_io_index; if ((sem_io_index = moo->sem_io_map[i]) >= 0) { MOO_ASSERT (moo, sem_io_index < moo->sem_io_tuple_count); MOO_ASSERT (moo, moo->sem_io_tuple[sem_io_index].sem[MOO_SEMAPHORE_IO_TYPE_INPUT] || moo->sem_io_tuple[sem_io_index].sem[MOO_SEMAPHORE_IO_TYPE_OUTPUT]); if (moo->sem_io_tuple[sem_io_index].sem[MOO_SEMAPHORE_IO_TYPE_INPUT]) { delete_sem_from_sem_io_tuple (moo, moo->sem_io_tuple[sem_io_index].sem[MOO_SEMAPHORE_IO_TYPE_INPUT], 1); } if (moo->sem_io_tuple[sem_io_index].sem[MOO_SEMAPHORE_IO_TYPE_OUTPUT]) { delete_sem_from_sem_io_tuple (moo, moo->sem_io_tuple[sem_io_index].sem[MOO_SEMAPHORE_IO_TYPE_OUTPUT], 1); } MOO_ASSERT (moo, moo->sem_io_map[i] <= -1); } else { i++; } } MOO_ASSERT (moo, moo->sem_io_tuple_count == 0); MOO_ASSERT (moo, moo->sem_io_count == 0); moo->vmprim.vm_gettime (moo, &moo->exec_end_time); /* raw time. no adjustment */ moo->vmprim.vm_cleanup (moo); moo->sem_gcfin = (moo_oop_semaphore_t)moo->_nil; moo->sem_gcfin_sigreq = 0; /* deregister all pending finalizable objects pending just in case these * have not been removed for various reasons. (e.g. sudden VM abortion) */ moo_deregallfinalizables (moo); /* final garbage collection */ moo_gc (moo, 1); MOO_DEBUG0 (moo, "VM cleaned up\n"); } static MOO_INLINE void vm_gettime (moo_t* moo, moo_ntime_t* now) { moo->vmprim.vm_gettime (moo, now); /* in vm_startup(), moo->exec_start_time has been set to the time of * that moment. time returned here get offset by moo->exec_start_time and * thus becomes relative to it. this way, it is kept small such that it * can be represented in a small integer with leaving almost zero chance * of overflow. */ MOO_SUB_NTIME (now, now, &moo->exec_start_time); /* now = now - exec_start_time */ } static MOO_INLINE int vm_sleep (moo_t* moo, const moo_ntime_t* dur) { return moo->vmprim.vm_sleep(moo, dur); } static MOO_INLINE void vm_muxwait (moo_t* moo, const moo_ntime_t* dur) { moo->vmprim.vm_muxwait (moo, dur, signal_io_semaphore); } /* ------------------------------------------------------------------------- */ static MOO_INLINE int prepare_to_alloc_pid (moo_t* moo) { moo_oow_t new_capa; moo_ooi_t i, j; moo_oop_t* tmp; MOO_ASSERT (moo, moo->proc_map_free_first <= -1); MOO_ASSERT (moo, moo->proc_map_free_last <= -1); new_capa = moo->proc_map_capa + PROC_MAP_INC; if (new_capa > MOO_SMOOI_MAX) { if (moo->proc_map_capa >= MOO_SMOOI_MAX) { #if defined(MOO_DEBUG_VM_PROCESSOR) MOO_LOG0 (moo, MOO_LOG_IC | MOO_LOG_FATAL, "Processor - too many processes\n"); #endif moo_seterrbfmt (moo, MOO_EPFULL, "maximum number(%zd) of processes reached", MOO_SMOOI_MAX); return -1; } new_capa = MOO_SMOOI_MAX; } tmp = moo_reallocmem(moo, moo->proc_map, MOO_SIZEOF(moo_oop_t) * new_capa); if (!tmp) return -1; moo->proc_map_free_first = moo->proc_map_capa; for (i = moo->proc_map_capa, j = moo->proc_map_capa + 1; j < new_capa; i++, j++) { tmp[i] = MOO_SMOOI_TO_OOP(j); } tmp[i] = MOO_SMOOI_TO_OOP(-1); moo->proc_map_free_last = i; moo->proc_map = tmp; moo->proc_map_capa = new_capa; return 0; } static MOO_INLINE void alloc_pid (moo_t* moo, moo_oop_process_t proc) { moo_ooi_t pid; pid = moo->proc_map_free_first; proc->id = MOO_SMOOI_TO_OOP(pid); MOO_ASSERT (moo, MOO_OOP_IS_SMOOI(moo->proc_map[pid])); moo->proc_map_free_first = MOO_OOP_TO_SMOOI(moo->proc_map[pid]); if (moo->proc_map_free_first <= -1) moo->proc_map_free_last = -1; moo->proc_map[pid] = (moo_oop_t)proc; moo->proc_map_used++; } static MOO_INLINE void free_pid (moo_t* moo, moo_oop_process_t proc) { moo_ooi_t pid; pid = MOO_OOP_TO_SMOOI(proc->id); MOO_ASSERT (moo, pid < moo->proc_map_capa); MOO_ASSERT (moo, moo->proc_map_used > 0); /* chain the freed slot at the end of the free list */ moo->proc_map[pid] = MOO_SMOOI_TO_OOP(-1); if (moo->proc_map_free_last <= -1) { MOO_ASSERT (moo, moo->proc_map_free_first <= -1); moo->proc_map_free_first = pid; } else { moo->proc_map[moo->proc_map_free_last] = MOO_SMOOI_TO_OOP(pid); } moo->proc_map_free_last = pid; moo->proc_map_used--; } static moo_oop_process_t make_process (moo_t* moo, moo_oop_context_t c, int proc_flags) { moo_oop_process_t proc; moo_ooi_t total_count; moo_ooi_t suspended_count; total_count = MOO_OOP_TO_SMOOI(moo->processor->total_count); if (total_count >= MOO_SMOOI_MAX) { #if defined(MOO_DEBUG_VM_PROCESSOR) MOO_LOG0 (moo, MOO_LOG_IC | MOO_LOG_FATAL, "Processor - too many processes\n"); #endif moo_seterrbfmt (moo, MOO_EPFULL, "maximum number(%zd) of processes reached", MOO_SMOOI_MAX); return MOO_NULL; } if (moo->proc_map_free_first <= -1 && prepare_to_alloc_pid(moo) <= -1) return MOO_NULL; moo_pushvolat (moo, (moo_oop_t*)&c); proc = (moo_oop_process_t)moo_instantiate(moo, moo->_process, MOO_NULL, moo->option.dfl_procstk_size); moo_popvolat (moo); if (!proc) return MOO_NULL; MOO_OBJ_SET_FLAGS_PROC (proc, proc_flags); /* a special flag to indicate an object is a process instance */ proc->state = MOO_SMOOI_TO_OOP(PROC_STATE_SUSPENDED); /* assign a process id to the process */ alloc_pid (moo, proc); MOO_STORE_OOP (moo, (moo_oop_t*)&proc->initial_context, (moo_oop_t)c); MOO_STORE_OOP (moo, (moo_oop_t*)&proc->current_context, (moo_oop_t)c); proc->sp = MOO_SMOOI_TO_OOP(-1); proc->perr = MOO_ERROR_TO_OOP(MOO_ENOERR); proc->perrmsg = moo->_nil; MOO_ASSERT (moo, (moo_oop_t)c->sender == moo->_nil); #if defined(MOO_DEBUG_VM_PROCESSOR) MOO_LOG2 (moo, MOO_LOG_IC | MOO_LOG_DEBUG, "Processor - process[%zd] **CREATED**->%hs\n", MOO_OOP_TO_SMOOI(proc->id), proc_state_to_string(MOO_OOP_TO_SMOOI(proc->state))); #endif /* a process is created in the SUSPENDED state. chain it to the suspended process list */ suspended_count = MOO_OOP_TO_SMOOI(moo->processor->suspended.count); MOO_APPEND_TO_OOP_LIST (moo, &moo->processor->suspended, moo_oop_process_t, proc, ps); suspended_count++; moo->processor->suspended.count = MOO_SMOOI_TO_OOP(suspended_count); total_count++; moo->processor->total_count = MOO_SMOOI_TO_OOP(total_count); return proc; } static MOO_INLINE void sleep_active_process (moo_t* moo, int state) { STORE_ACTIVE_SP(moo); /* store the current active context to the current process. * it is the suspended context of the process to be suspended */ MOO_ASSERT (moo, moo->processor->active != moo->nil_process); #if defined(MOO_DEBUG_VM_PROCESSOR) MOO_LOG3 (moo, MOO_LOG_IC | MOO_LOG_DEBUG, "Processor - process[%zd] %hs->%hs in sleep_active_process\n", MOO_OOP_TO_SMOOI(moo->processor->active->id), proc_state_to_string(MOO_OOP_TO_SMOOI(moo->processor->active->state)), proc_state_to_string(state)); #endif MOO_STORE_OOP (moo, (moo_oop_t*)&moo->processor->active->current_context, (moo_oop_t)moo->active_context); moo->processor->active->state = MOO_SMOOI_TO_OOP(state); } static MOO_INLINE void wake_process (moo_t* moo, moo_oop_process_t proc) { /* activate the given process */ #if defined(MOO_DEBUG_VM_PROCESSOR) MOO_LOG2 (moo, MOO_LOG_IC | MOO_LOG_DEBUG, "Processor - process[%zd] %hs->RUNNING in wake_process\n", MOO_OOP_TO_SMOOI(proc->id), proc_state_to_string(MOO_OOP_TO_SMOOI(proc->state))); #endif MOO_ASSERT (moo, proc->state == MOO_SMOOI_TO_OOP(PROC_STATE_RUNNABLE)); proc->state = MOO_SMOOI_TO_OOP(PROC_STATE_RUNNING); MOO_STORE_OOP (moo, (moo_oop_t*)&moo->processor->active, (moo_oop_t)proc); /* load the stack pointer from 'proc'. * moo->processor->active points to 'proc' now. */ LOAD_ACTIVE_SP(moo); /* activate the suspended context of the new process */ SWITCH_ACTIVE_CONTEXT (moo, proc->current_context); #if defined(MOO_DEBUG_VM_PROCESSOR) && (MOO_DEBUG_VM_PROCESSOR >= 2) MOO_LOG3 (moo, MOO_LOG_IC | MOO_LOG_DEBUG, "Processor - woke up process[%zd] context %O ip=%zd\n", MOO_OOP_TO_SMOOI(moo->processor->active->id), moo->active_context, moo->ip); #endif } static void switch_to_process (moo_t* moo, moo_oop_process_t proc, int new_state_for_old_active) { /* the new process must not be the currently active process */ MOO_ASSERT (moo, moo->processor->active != proc); /* the new process must be in the runnable state */ MOO_ASSERT (moo, proc->state == MOO_SMOOI_TO_OOP(PROC_STATE_RUNNABLE) || proc->state == MOO_SMOOI_TO_OOP(PROC_STATE_WAITING)); sleep_active_process (moo, new_state_for_old_active); wake_process (moo, proc); moo->proc_switched = 1; } static MOO_INLINE void switch_to_process_from_nil (moo_t* moo, moo_oop_process_t proc) { MOO_ASSERT (moo, moo->processor->active == moo->nil_process); wake_process (moo, proc); moo->proc_switched = 1; } static MOO_INLINE moo_oop_process_t find_next_runnable_process (moo_t* moo) { moo_oop_process_t nrp; MOO_ASSERT (moo, moo->processor->active->state == MOO_SMOOI_TO_OOP(PROC_STATE_RUNNING)); nrp = moo->processor->active->ps.next; if ((moo_oop_t)nrp == moo->_nil) nrp = moo->processor->runnable.first; return nrp; } static MOO_INLINE void switch_to_next_runnable_process (moo_t* moo) { moo_oop_process_t nrp; nrp = find_next_runnable_process (moo); if (nrp != moo->processor->active) switch_to_process (moo, nrp, PROC_STATE_RUNNABLE); } static MOO_INLINE void chain_into_processor (moo_t* moo, moo_oop_process_t proc, int new_state) { /* the process is not scheduled at all. * link it to the processor's process list. */ moo_ooi_t runnable_count; moo_ooi_t suspended_count; /*MOO_ASSERT (moo, (moo_oop_t)proc->ps.prev == moo->_nil); MOO_ASSERT (moo, (moo_oop_t)proc->ps.next == moo->_nil);*/ MOO_ASSERT (moo, proc->state == MOO_SMOOI_TO_OOP(PROC_STATE_SUSPENDED)); MOO_ASSERT (moo, new_state == PROC_STATE_RUNNABLE || new_state == PROC_STATE_RUNNING); #if defined(MOO_DEBUG_VM_PROCESSOR) MOO_LOG3 (moo, MOO_LOG_IC | MOO_LOG_DEBUG, "Processor - process[%zd] %hs->%hs in chain_into_processor\n", MOO_OOP_TO_SMOOI(proc->id), proc_state_to_string(MOO_OOP_TO_SMOOI(proc->state)), proc_state_to_string(new_state)); #endif runnable_count = MOO_OOP_TO_SMOOI(moo->processor->runnable.count); MOO_ASSERT (moo, runnable_count >= 0); suspended_count = MOO_OOP_TO_SMOOI(moo->processor->suspended.count); MOO_DELETE_FROM_OOP_LIST (moo, &moo->processor->suspended, proc, ps); suspended_count--; moo->processor->suspended.count = MOO_SMOOI_TO_OOP(suspended_count); /* append to the runnable list */ MOO_APPEND_TO_OOP_LIST (moo, &moo->processor->runnable, moo_oop_process_t, proc, ps); proc->state = MOO_SMOOI_TO_OOP(new_state); runnable_count++; moo->processor->runnable.count = MOO_SMOOI_TO_OOP(runnable_count); } static MOO_INLINE void unchain_from_processor (moo_t* moo, moo_oop_process_t proc, int new_state) { moo_ooi_t runnable_count; moo_ooi_t suspended_count; moo_ooi_t total_count; MOO_ASSERT (moo, proc->state == MOO_SMOOI_TO_OOP(PROC_STATE_RUNNING) || proc->state == MOO_SMOOI_TO_OOP(PROC_STATE_RUNNABLE) || proc->state == MOO_SMOOI_TO_OOP(PROC_STATE_SUSPENDED)); MOO_ASSERT (moo, proc->state != MOO_SMOOI_TO_OOP(new_state)); #if defined(MOO_DEBUG_VM_PROCESSOR) MOO_LOG3 (moo, MOO_LOG_IC | MOO_LOG_DEBUG, "Processor - process[%zd] %hs->%hs in unchain_from_processor\n", MOO_OOP_TO_SMOOI(proc->id), proc_state_to_string(MOO_OOP_TO_SMOOI(proc->state)), proc_state_to_string(MOO_OOP_TO_SMOOI(new_state))); #endif if (proc->state == MOO_SMOOI_TO_OOP(PROC_STATE_SUSPENDED)) { suspended_count = MOO_OOP_TO_SMOOI(moo->processor->suspended.count); MOO_ASSERT (moo, suspended_count > 0); MOO_DELETE_FROM_OOP_LIST (moo, &moo->processor->suspended, proc, ps); suspended_count--; moo->processor->suspended.count = MOO_SMOOI_TO_OOP(suspended_count); } else { runnable_count = MOO_OOP_TO_SMOOI(moo->processor->runnable.count); MOO_ASSERT (moo, runnable_count > 0); MOO_DELETE_FROM_OOP_LIST (moo, &moo->processor->runnable, proc, ps); runnable_count--; moo->processor->runnable.count = MOO_SMOOI_TO_OOP(runnable_count); if (runnable_count == 0) moo->processor->active = moo->nil_process; } if (new_state == PROC_STATE_TERMINATED) { /* do not chain it to the suspended process list as it's being terminated */ proc->ps.prev = (moo_oop_process_t)moo->_nil; proc->ps.next = (moo_oop_process_t)moo->_nil; total_count = MOO_OOP_TO_SMOOI(moo->processor->total_count); total_count--; moo->processor->total_count = MOO_SMOOI_TO_OOP(total_count); } else { /* append to the suspended process list */ MOO_ASSERT (moo, new_state == PROC_STATE_SUSPENDED); suspended_count = MOO_OOP_TO_SMOOI(moo->processor->suspended.count); MOO_APPEND_TO_OOP_LIST (moo, &moo->processor->suspended, moo_oop_process_t, proc, ps); suspended_count++; moo->processor->suspended.count= MOO_SMOOI_TO_OOP(suspended_count); } proc->state = MOO_SMOOI_TO_OOP(new_state); } static MOO_INLINE void chain_into_semaphore (moo_t* moo, moo_oop_process_t proc, moo_oop_semaphore_t sem) { /* append a process to the process list of a semaphore or a semaphore group */ /* a process chained to a semaphore cannot get chained to * a semaphore again. a process can get chained to a single semaphore * or a single semaphore group only */ if ((moo_oop_t)proc->sem != moo->_nil) return; /* ignore it if it happens anyway. TODO: is it desirable???? */ MOO_ASSERT (moo, (moo_oop_t)proc->sem == moo->_nil); MOO_ASSERT (moo, (moo_oop_t)proc->sem_wait.prev == moo->_nil); MOO_ASSERT (moo, (moo_oop_t)proc->sem_wait.next == moo->_nil); /* a semaphore or a semaphore group must be given for process chaining */ MOO_ASSERT (moo, moo_iskindof(moo, (moo_oop_t)sem, moo->_semaphore) || moo_iskindof(moo, (moo_oop_t)sem, moo->_semaphore_group)); /* i assume the head part of the semaphore has the same layout as * the semaphore group */ MOO_ASSERT (moo, MOO_OFFSETOF(moo_semaphore_t,waiting) == MOO_OFFSETOF(moo_semaphore_group_t,waiting)); MOO_APPEND_TO_OOP_LIST (moo, &sem->waiting, moo_oop_process_t, proc, sem_wait); MOO_STORE_OOP (moo, &proc->sem, (moo_oop_t)sem); } static MOO_INLINE void unchain_from_semaphore (moo_t* moo, moo_oop_process_t proc) { moo_oop_semaphore_t sem; MOO_ASSERT (moo, (moo_oop_t)proc->sem != moo->_nil); MOO_ASSERT (moo, moo_iskindof(moo, proc->sem, moo->_semaphore) || moo_iskindof(moo, proc->sem, moo->_semaphore_group)); MOO_ASSERT (moo, MOO_OFFSETOF(moo_semaphore_t,waiting) == MOO_OFFSETOF(moo_semaphore_group_t,waiting)); /* proc->sem may be one of a semaphore or a semaphore group. * i assume that 'waiting' is defined to the same position * in both Semaphore and SemaphoreGroup. there is no need to * write different code for each class. */ sem = (moo_oop_semaphore_t)proc->sem; /* semgrp = (moo_oop_semaphore_group_t)proc->sem */ MOO_DELETE_FROM_OOP_LIST (moo, &sem->waiting, proc, sem_wait); proc->sem_wait.prev = (moo_oop_process_t)moo->_nil; proc->sem_wait.next = (moo_oop_process_t)moo->_nil; proc->sem = moo->_nil; } static void dump_process_info (moo_t* moo, moo_bitmask_t log_mask) { if (MOO_OOP_TO_SMOOI(moo->processor->runnable.count) > 0) { moo_oop_process_t p; MOO_LOG0 (moo, log_mask, "> Runnable:"); p = moo->processor->runnable.first; while (p) { MOO_LOG1 (moo, log_mask, " %O", p->id); if (p == moo->processor->runnable.last) break; p = p->ps.next; } MOO_LOG0 (moo, log_mask, "\n"); } if (MOO_OOP_TO_SMOOI(moo->processor->suspended.count) > 0) { moo_oop_process_t p; MOO_LOG0 (moo, log_mask, "> Suspended:"); p = moo->processor->suspended.first; while (p) { MOO_LOG1 (moo, log_mask, " %O", p->id); if (p == moo->processor->suspended.last) break; p = p->ps.next; } MOO_LOG0 (moo, log_mask, "\n"); } if (moo->sem_io_wait_count > 0) { moo_ooi_t io_handle; MOO_LOG0 (moo, log_mask, "> IO semaphores:"); for (io_handle = 0; io_handle < moo->sem_io_map_capa; io_handle++) { moo_ooi_t index; index = moo->sem_io_map[io_handle]; if (index >= 0) { moo_oop_semaphore_t sem; MOO_LOG1 (moo, log_mask, " h=%zd", io_handle); /* dump process IDs waiting for input signaling */ MOO_LOG0 (moo, log_mask, "(wpi"); sem = moo->sem_io_tuple[index].sem[MOO_SEMAPHORE_IO_TYPE_INPUT]; if (sem) { moo_oop_process_t wp; /* waiting process */ for (wp = sem->waiting.first; (moo_oop_t)wp != moo->_nil; wp = wp->sem_wait.next) { MOO_LOG1 (moo, log_mask, ":%zd", MOO_OOP_TO_SMOOI(wp->id)); } } else { MOO_LOG0 (moo, log_mask, ":none"); } /* dump process IDs waitingt for output signaling */ MOO_LOG0 (moo, log_mask, ",wpo"); sem = moo->sem_io_tuple[index].sem[MOO_SEMAPHORE_IO_TYPE_OUTPUT]; if (sem) { moo_oop_process_t wp; /* waiting process */ for (wp = sem->waiting.first; (moo_oop_t)wp != moo->_nil; wp = wp->sem_wait.next) { MOO_LOG1 (moo, log_mask, ":%zd", MOO_OOP_TO_SMOOI(wp->id)); } } else { MOO_LOG0 (moo, log_mask, ":none"); } MOO_LOG0 (moo, log_mask, ")"); } } MOO_LOG0 (moo, log_mask, "\n"); } } static void terminate_process (moo_t* moo, moo_oop_process_t proc) { if (proc->state == MOO_SMOOI_TO_OOP(PROC_STATE_RUNNING) || proc->state == MOO_SMOOI_TO_OOP(PROC_STATE_RUNNABLE)) { /* RUNNING/RUNNABLE ---> TERMINATED */ #if defined(MOO_DEBUG_VM_PROCESSOR) MOO_LOG2 (moo, MOO_LOG_IC | MOO_LOG_DEBUG, "Processor - process[%zd] %hs->TERMINATED in terminate_process\n", MOO_OOP_TO_SMOOI(proc->id), proc_state_to_string(MOO_OOP_TO_SMOOI(proc->state))); #endif if (proc == moo->processor->active) { moo_oop_process_t nrp; nrp = find_next_runnable_process(moo); unchain_from_processor (moo, proc, PROC_STATE_TERMINATED); proc->sp = MOO_SMOOI_TO_OOP(-1); /* invalidate the process stack */ MOO_STORE_OOP (moo, (moo_oop_t*)&proc->current_context, (moo_oop_t)proc->initial_context); /* not needed but just in case */ /* a runnable or running process must not be chanined to the process list of a semaphore */ MOO_ASSERT (moo, (moo_oop_t)proc->sem == moo->_nil); if (nrp == proc) { /* no runnable process after termination */ MOO_ASSERT (moo, moo->processor->active == moo->nil_process); if (MOO_LOG_ENABLED(moo, MOO_LOG_IC | MOO_LOG_DEBUG)) { MOO_LOG5 (moo, MOO_LOG_IC | MOO_LOG_DEBUG, "No runnable process after termination of process %zd - total %zd runnable/running %zd suspended %zd - sem_io_wait_count %zu\n", MOO_OOP_TO_SMOOI(proc->id), MOO_OOP_TO_SMOOI(moo->processor->total_count), MOO_OOP_TO_SMOOI(moo->processor->runnable.count), MOO_OOP_TO_SMOOI(moo->processor->suspended.count), moo->sem_io_wait_count ); dump_process_info (moo, MOO_LOG_IC | MOO_LOG_DEBUG); } } else { switch_to_process (moo, nrp, PROC_STATE_TERMINATED); } } else { unchain_from_processor (moo, proc, PROC_STATE_TERMINATED); proc->sp = MOO_SMOOI_TO_OOP(-1); /* invalidate the process stack */ } /* when terminated, clear it from the pid table and set the process id to a negative number */ free_pid (moo, proc); } else if (proc->state == MOO_SMOOI_TO_OOP(PROC_STATE_SUSPENDED)) { /* SUSPENDED ---> TERMINATED */ #if defined(MOO_DEBUG_VM_PROCESSOR) MOO_LOG2 (moo, MOO_LOG_IC | MOO_LOG_DEBUG, "Processor - process[%zd] %hs->TERMINATED in terminate_process\n", MOO_OOP_TO_SMOOI(proc->id), proc_state_to_string(MOO_OOP_TO_SMOOI(proc->state))); #endif /*proc->state = MOO_SMOOI_TO_OOP(PROC_STATE_TERMINATED);*/ unchain_from_processor (moo, proc, PROC_STATE_TERMINATED); proc->sp = MOO_SMOOI_TO_OOP(-1); /* invalidate the proce stack */ if ((moo_oop_t)proc->sem != moo->_nil) { if (MOO_CLASSOF(moo, proc->sem) == moo->_semaphore_group) { if (MOO_OOP_TO_SMOOI(((moo_oop_semaphore_group_t)proc->sem)->sem_io_count) > 0) { MOO_ASSERT (moo, moo->sem_io_wait_count > 0); moo->sem_io_wait_count--; MOO_DEBUG1 (moo, "terminate_process(sg) - lowered sem_io_wait_count to %zu\n", moo->sem_io_wait_count); } } else { MOO_ASSERT (moo, MOO_CLASSOF(moo, proc->sem) == moo->_semaphore); if (((moo_oop_semaphore_t)proc->sem)->subtype == MOO_SMOOI_TO_OOP(MOO_SEMAPHORE_SUBTYPE_IO)) { MOO_ASSERT (moo, moo->sem_io_wait_count > 0); moo->sem_io_wait_count--; MOO_DEBUG3 (moo, "terminate_process(s) - lowered sem_io_wait_count to %zu for IO semaphore at index %zd handle %zd\n", moo->sem_io_wait_count, MOO_OOP_TO_SMOOI(((moo_oop_semaphore_t)proc->sem)->u.io.index), MOO_OOP_TO_SMOOI(((moo_oop_semaphore_t)proc->sem)->u.io.handle) ); } } unchain_from_semaphore (moo, proc); } /* when terminated, clear it from the pid table and set the process id to a negative number */ free_pid (moo, proc); } #if 0 else if (proc->state == MOO_SMOOI_TO_OOP(PROC_STATE_WAITING)) { /* WAITING ---> TERMINATED */ /* TODO: */ } #endif } static void terminate_all_processes (moo_t* moo) { while (MOO_OOP_TO_SMOOI(moo->processor->suspended.count) > 0) { terminate_process (moo, moo->processor->suspended.first); } while (MOO_OOP_TO_SMOOI(moo->processor->runnable.count) > 0) { terminate_process (moo, moo->processor->runnable.first); } MOO_ASSERT (moo, MOO_OOP_TO_SMOOI(moo->processor->total_count) == 0); MOO_ASSERT (moo, moo->processor->active == moo->nil_process); } static void resume_process (moo_t* moo, moo_oop_process_t proc) { if (proc->state == MOO_SMOOI_TO_OOP(PROC_STATE_SUSPENDED)) { /* SUSPENDED ---> RUNNABLE */ /*MOO_ASSERT (moo, (moo_oop_t)proc->ps.prev == moo->_nil); MOO_ASSERT (moo, (moo_oop_t)proc->ps.next == moo->_nil);*/ #if defined(MOO_DEBUG_VM_PROCESSOR) MOO_LOG2 (moo, MOO_LOG_IC | MOO_LOG_DEBUG, "Processor - process[%zd] %hs->RUNNABLE in resume_process\n", MOO_OOP_TO_SMOOI(proc->id), proc_state_to_string(MOO_OOP_TO_SMOOI(proc->state))); #endif /* don't switch to this process. just change the state to RUNNABLE. * process switching should be triggerd by the process scheduler. */ chain_into_processor (moo, proc, PROC_STATE_RUNNABLE); /*MOO_STORE_OOP (moo, &proc->current_context = proc->initial_context);*/ } #if 0 else if (proc->state == MOO_SMOOI_TO_OOP(PROC_STATE_RUNNABLE)) { /* RUNNABLE ---> RUNNING */ /* TODO: should i allow this? */ MOO_ASSERT (moo, moo->processor->active != proc); switch_to_process (moo, proc, PROC_STATE_RUNNABLE); } #endif } static void suspend_process (moo_t* moo, moo_oop_process_t proc) { if (proc->state == MOO_SMOOI_TO_OOP(PROC_STATE_RUNNING) || proc->state == MOO_SMOOI_TO_OOP(PROC_STATE_RUNNABLE)) { /* RUNNING/RUNNABLE ---> SUSPENDED */ #if defined(MOO_DEBUG_VM_PROCESSOR) MOO_LOG2 (moo, MOO_LOG_IC | MOO_LOG_DEBUG, "Processor - process[%zd] %hs->SUSPENDED in suspend_process\n", MOO_OOP_TO_SMOOI(proc->id), proc_state_to_string(MOO_OOP_TO_SMOOI(proc->state))); #endif if (proc == moo->processor->active) { /* suspend the active process */ moo_oop_process_t nrp; nrp = find_next_runnable_process (moo); if (nrp == proc) { /* no runnable process after suspension */ sleep_active_process (moo, PROC_STATE_RUNNABLE); unchain_from_processor (moo, proc, PROC_STATE_SUSPENDED); /* the last running/runnable process has been unchained * from the processor and set to SUSPENDED. the active * process must be the nil process */ MOO_ASSERT (moo, moo->processor->active == moo->nil_process); } else { /* unchain_from_processor moves the process to the suspended * process and sets its state to the given state(SUSPENDED here). * it doesn't change the active process. we switch the active * process with switch_to_process(). setting the state of the * old active process to SUSPENDED is redundant because it's * done in unchain_from_processor(). the state of the active * process is somewhat wrong for a short period of time until * switch_to_process() has changed the active process. */ unchain_from_processor (moo, proc, PROC_STATE_SUSPENDED); MOO_ASSERT (moo, moo->processor->active != moo->nil_process); switch_to_process (moo, nrp, PROC_STATE_SUSPENDED); } } else { unchain_from_processor (moo, proc, PROC_STATE_SUSPENDED); } } } static void yield_process (moo_t* moo, moo_oop_process_t proc) { if (proc->state == MOO_SMOOI_TO_OOP(PROC_STATE_RUNNING)) { /* RUNNING --> RUNNABLE */ moo_oop_process_t nrp; MOO_ASSERT (moo, proc == moo->processor->active); nrp = find_next_runnable_process (moo); /* if there are more than 1 runnable processes, the next * runnable process must be different from proc */ if (nrp != proc) { #if defined(MOO_DEBUG_VM_PROCESSOR) MOO_LOG2 (moo, MOO_LOG_IC | MOO_LOG_DEBUG, "Processor - process[%zd] %hs->RUNNABLE in yield_process\n", MOO_OOP_TO_SMOOI(proc->id), proc_state_to_string(MOO_OOP_TO_SMOOI(proc->state))); #endif switch_to_process (moo, nrp, PROC_STATE_RUNNABLE); } } } #if 0 static int async_signal_semaphore (moo_t* moo, moo_oop_semaphore_t sem) { if (moo->sem_list_count >= SEM_LIST_MAX) { moo_seterrnum (moo, MOO_ESEMFLOOD, "too many semaphores in the semaphore list"); return -1; } if (moo->sem_list_count >= moo->sem_list_capa) { moo_oow_t new_capa; moo_oop_semaphore_t* tmp; new_capa = moo->sem_list_capa + SEM_LIST_INC; /* TODO: overflow check.. */ tmp = (moo_oop_semaphore_t*)moo_reallocmem(moo, moo->sem_list, MOO_SIZEOF(moo_oop_semaphore_t) * new_capa); if (!tmp) return -1; moo->sem_list = tmp; moo->sem_list_capa = new_capa; } moo->sem_list[moo->sem_list_count] = sem; moo->sem_list_count++; return 0; } #endif static moo_oop_process_t signal_semaphore (moo_t* moo, moo_oop_semaphore_t sem) { moo_oop_process_t proc; moo_ooi_t count; moo_oop_semaphore_group_t sg; sg = sem->group; if ((moo_oop_t)sg != moo->_nil) { /* the semaphore belongs to a semaphore group */ if ((moo_oop_t)sg->waiting.first != moo->_nil) { moo_ooi_t sp; /* there is a process waiting on the process group */ proc = sg->waiting.first; /* will wake the first process in the waiting list */ unchain_from_semaphore (moo, proc); resume_process (moo, proc); /* [IMPORTANT] RETURN VALUE of SemaphoreGroup's wait. * ------------------------------------------------------------ * the waiting process has been suspended after a waiting * primitive function in Semaphore or SemaphoreGroup. * the top of the stack of the process must hold the temporary * return value set by await_semaphore() or await_semaphore_group(). * change the return value forcibly to the actual signaled * semaphore */ MOO_ASSERT (moo, MOO_OOP_TO_SMOOI(proc->sp) < (moo_ooi_t)(MOO_OBJ_GET_SIZE(proc) - MOO_PROCESS_NAMED_INSTVARS)); sp = MOO_OOP_TO_SMOOI(proc->sp); MOO_STORE_OOP (moo, &proc->stack[sp], (moo_oop_t)sem); /* i should decrement the counter as long as the group being * signaled contains an IO semaphore */ if (MOO_OOP_TO_SMOOI(sg->sem_io_count) > 0) { MOO_ASSERT (moo, moo->sem_io_wait_count > 0); moo->sem_io_wait_count--; MOO_DEBUG2 (moo, "signal_semaphore(sg) - lowered sem_io_wait_count to %zu for handle %zd\n", moo->sem_io_wait_count, MOO_OOP_TO_SMOOI(sem->u.io.handle)); } return proc; } } /* if the semaphore belongs to a semaphore group and the control reaches * here, no process is waiting on the semaphore group. however, a process * may still be waiting on the semaphore. If a process waits on a semaphore * group and another process wait on a semaphore that belongs to the * semaphore group, the process waiting on the group always wins. * * TODO: implement a fair scheduling policy. or do i simply have to disallow individual wait on a semaphore belonging to a group? * * if it doesn't belong to a sempahore group, i'm free from the starvation issue. */ if ((moo_oop_t)sem->waiting.first == moo->_nil) { /* no process is waiting on this semaphore */ count = MOO_OOP_TO_SMOOI(sem->count); count++; sem->count = MOO_SMOOI_TO_OOP(count); MOO_ASSERT (moo, count >= 1); if (count == 1 && (moo_oop_t)sg != moo->_nil) { /* move the semaphore from the unsignaled list to the signaled list * if the semaphore count has changed from 0 to 1 in a group */ MOO_DELETE_FROM_OOP_LIST (moo, &sg->sems[MOO_SEMAPHORE_GROUP_SEMS_UNSIG], sem, grm); MOO_APPEND_TO_OOP_LIST (moo, &sg->sems[MOO_SEMAPHORE_GROUP_SEMS_SIG], moo_oop_semaphore_t, sem, grm); } /* no process has been resumed */ return (moo_oop_process_t)moo->_nil; } else { proc = sem->waiting.first; /* [NOTE] no GC must occur as 'proc' isn't protected with moo_pushvolat(). */ /* detach a process from a semaphore's waiting list and * make it runnable */ unchain_from_semaphore (moo, proc); resume_process (moo, proc); if (sem->subtype == MOO_SMOOI_TO_OOP(MOO_SEMAPHORE_SUBTYPE_IO)) { MOO_ASSERT (moo, moo->sem_io_wait_count > 0); moo->sem_io_wait_count--; MOO_DEBUG3 (moo, "signal_semaphore(s) - lowered sem_io_wait_count to %zu for IO semaphore at index %zd handle %zd\n", moo->sem_io_wait_count, MOO_OOP_TO_SMOOI(sem->u.io.index), MOO_OOP_TO_SMOOI(sem->u.io.handle)); } /* return the resumed(runnable) process */ return proc; } } static MOO_INLINE int can_await_semaphore (moo_t* moo, moo_oop_semaphore_t sem) { /* a sempahore that doesn't belong to a gruop can be waited on */ return (moo_oop_t)sem->group == moo->_nil; } static MOO_INLINE void await_semaphore (moo_t* moo, moo_oop_semaphore_t sem) { moo_oop_process_t proc; moo_ooi_t count; moo_oop_semaphore_group_t semgrp; semgrp = sem->group; /* the caller of this function must ensure that the semaphore doesn't belong to a group */ MOO_ASSERT (moo, (moo_oop_t)semgrp == moo->_nil); count = MOO_OOP_TO_SMOOI(sem->count); if (count > 0) { /* it's already signaled */ count--; sem->count = MOO_SMOOI_TO_OOP(count); if ((moo_oop_t)semgrp != moo->_nil && count == 0) { int sems_idx; /* TODO: if i disallow individual wait on a semaphore in a group, * this membership manipulation is redundant */ MOO_DELETE_FROM_OOP_LIST (moo, &semgrp->sems[MOO_SEMAPHORE_GROUP_SEMS_SIG], sem, grm); sems_idx = count > 0? MOO_SEMAPHORE_GROUP_SEMS_SIG: MOO_SEMAPHORE_GROUP_SEMS_UNSIG; MOO_APPEND_TO_OOP_LIST (moo, &semgrp->sems[sems_idx], moo_oop_semaphore_t, sem, grm); } } else { /* not signaled. need to wait */ proc = moo->processor->active; /* suspend the active process */ suspend_process (moo, proc); /* link the suspended process to the semaphore's process list */ chain_into_semaphore (moo, proc, sem); MOO_ASSERT (moo, sem->waiting.last == proc); if (sem->subtype == MOO_SMOOI_TO_OOP(MOO_SEMAPHORE_SUBTYPE_IO)) { moo->sem_io_wait_count++; MOO_DEBUG3 (moo, "await_semaphore - raised sem_io_wait_count to %zu for IO semaphore at index %zd handle %zd\n", moo->sem_io_wait_count, MOO_OOP_TO_SMOOI(sem->u.io.index), MOO_OOP_TO_SMOOI(sem->u.io.handle)); } MOO_ASSERT (moo, moo->processor->active != proc); } } static MOO_INLINE moo_oop_t await_semaphore_group (moo_t* moo, moo_oop_semaphore_group_t semgrp) { /* wait for one of semaphores in the group to be signaled */ moo_oop_process_t proc; moo_oop_semaphore_t sem; MOO_ASSERT (moo, moo_iskindof(moo, (moo_oop_t)semgrp, moo->_semaphore_group)); if (MOO_OOP_TO_SMOOI(semgrp->sem_count) <= 0) { /* cannot wait on a semaphore group that has no member semaphores. * return failure if waiting on such a semapohre group is attempted */ MOO_ASSERT (moo, (moo_oop_t)semgrp->sems[MOO_SEMAPHORE_GROUP_SEMS_SIG].first == moo->_nil); MOO_ASSERT (moo, (moo_oop_t)semgrp->sems[MOO_SEMAPHORE_GROUP_SEMS_SIG].last == moo->_nil); return MOO_ERROR_TO_OOP(MOO_EINVAL); /* TODO: better error code? */ } sem = semgrp->sems[MOO_SEMAPHORE_GROUP_SEMS_SIG].first; if ((moo_oop_t)sem != moo->_nil) { moo_ooi_t count; int sems_idx; /* there is a semaphore signaled in the group */ count = MOO_OOP_TO_SMOOI(sem->count); MOO_ASSERT (moo, count > 0); count--; sem->count = MOO_SMOOI_TO_OOP(count); MOO_DELETE_FROM_OOP_LIST (moo, &semgrp->sems[MOO_SEMAPHORE_GROUP_SEMS_SIG], sem, grm); sems_idx = count > 0? MOO_SEMAPHORE_GROUP_SEMS_SIG: MOO_SEMAPHORE_GROUP_SEMS_UNSIG; MOO_APPEND_TO_OOP_LIST (moo, &semgrp->sems[sems_idx], moo_oop_semaphore_t, sem, grm); return (moo_oop_t)sem; } /* no semaphores have been signaled. suspend the current process * until at least one of them is signaled */ proc = moo->processor->active; /* suspend the active process */ suspend_process (moo, proc); /* link the suspended process to the semaphore group's process list */ chain_into_semaphore (moo, proc, (moo_oop_semaphore_t)semgrp); MOO_ASSERT (moo, semgrp->waiting.last == proc); if (MOO_OOP_TO_SMOOI(semgrp->sem_io_count) > 0) { /* there might be more than 1 IO semaphores in the group * but i increment moo->sem_io_wait_count by 1 only */ moo->sem_io_wait_count++; MOO_DEBUG1 (moo, "await_semaphore_group - raised sem_io_wait_count to %zu\n", moo->sem_io_wait_count); } /* the current process will get suspended after the caller (mostly a * a primitive function handler) is over as it's added to a suspened * process list above */ MOO_ASSERT (moo, moo->processor->active != proc); return moo->_nil; } static void sift_up_sem_heap (moo_t* moo, moo_ooi_t index) { if (index > 0) { moo_ooi_t parent; moo_oop_semaphore_t sem, parsem; parent = SEM_HEAP_PARENT(index); sem = moo->sem_heap[index]; parsem = moo->sem_heap[parent]; if (SEM_HEAP_EARLIER_THAN(moo, sem, parsem)) { do { /* move down the parent to the current position */ parsem->u.timed.index = MOO_SMOOI_TO_OOP(index); moo->sem_heap[index] = parsem; /* traverse up */ index = parent; if (index <= 0) break; parent = SEM_HEAP_PARENT(parent); parsem = moo->sem_heap[parent]; } while (SEM_HEAP_EARLIER_THAN(moo, sem, parsem)); sem->u.timed.index = MOO_SMOOI_TO_OOP(index); moo->sem_heap[index] = sem; } } } static void sift_down_sem_heap (moo_t* moo, moo_ooi_t index) { moo_ooi_t base = moo->sem_heap_count / 2; if (index < base) /* at least 1 child is under the 'index' position */ { moo_ooi_t left, right, child; moo_oop_semaphore_t sem, chisem; sem = moo->sem_heap[index]; do { left = SEM_HEAP_LEFT(index); right = SEM_HEAP_RIGHT(index); if (right < moo->sem_heap_count && SEM_HEAP_EARLIER_THAN(moo, moo->sem_heap[right], moo->sem_heap[left])) { child = right; } else { child = left; } chisem = moo->sem_heap[child]; if (SEM_HEAP_EARLIER_THAN(moo, sem, chisem)) break; chisem->u.timed.index = MOO_SMOOI_TO_OOP(index); moo->sem_heap[index] = chisem; index = child; } while (index < base); sem->u.timed.index = MOO_SMOOI_TO_OOP(index); moo->sem_heap[index] = sem; } } static int add_to_sem_heap (moo_t* moo, moo_oop_semaphore_t sem) { moo_ooi_t index; MOO_ASSERT (moo, sem->subtype == moo->_nil); if (moo->sem_heap_count >= SEM_HEAP_MAX) { moo_seterrbfmt(moo, MOO_ESEMFLOOD, "too many semaphores in the semaphore heap"); return -1; } if (moo->sem_heap_count >= moo->sem_heap_capa) { moo_oow_t new_capa; moo_oop_semaphore_t* tmp; /* no overflow check when calculating the new capacity * owing to SEM_HEAP_MAX check above */ new_capa = moo->sem_heap_capa + SEM_HEAP_INC; tmp = moo_reallocmem (moo, moo->sem_heap, MOO_SIZEOF(moo_oop_semaphore_t) * new_capa); if (!tmp) return -1; moo->sem_heap = tmp; moo->sem_heap_capa = new_capa; } MOO_ASSERT (moo, moo->sem_heap_count <= MOO_SMOOI_MAX); index = moo->sem_heap_count; moo->sem_heap[index] = sem; sem->u.timed.index = MOO_SMOOI_TO_OOP(index); sem->subtype = MOO_SMOOI_TO_OOP(MOO_SEMAPHORE_SUBTYPE_TIMED); moo->sem_heap_count++; sift_up_sem_heap (moo, index); return 0; } static void delete_from_sem_heap (moo_t* moo, moo_ooi_t index) { moo_oop_semaphore_t sem, lastsem; MOO_ASSERT (moo, index >= 0 && index < moo->sem_heap_count); sem = moo->sem_heap[index]; sem->subtype = moo->_nil; sem->u.timed.index = moo->_nil; sem->u.timed.ftime_sec = moo->_nil; sem->u.timed.ftime_nsec = moo->_nil; moo->sem_heap_count--; if (/*moo->sem_heap_count > 0 &&*/ index != moo->sem_heap_count) { /* move the last item to the deletion position */ lastsem = moo->sem_heap[moo->sem_heap_count]; lastsem->u.timed.index = MOO_SMOOI_TO_OOP(index); moo->sem_heap[index] = lastsem; if (SEM_HEAP_EARLIER_THAN(moo, lastsem, sem)) sift_up_sem_heap (moo, index); else sift_down_sem_heap (moo, index); } } #if 0 /* unused */ static void update_sem_heap (moo_t* moo, moo_ooi_t index, moo_oop_semaphore_t newsem) { moo_oop_semaphore_t sem; sem = moo->sem_heap[index]; sem->timed.index = moo->_nil; newsem->timed.index = MOO_SMOOI_TO_OOP(index); moo->sem_heap[index] = newsem; if (SEM_HEAP_EARLIER_THAN(moo, newsem, sem)) sift_up_sem_heap (moo, index); else sift_down_sem_heap (moo, index); } #endif static int add_sem_to_sem_io_tuple (moo_t* moo, moo_oop_semaphore_t sem, moo_ooi_t io_handle, moo_semaphore_io_type_t io_type) { moo_ooi_t index; moo_ooi_t new_mask; int n, tuple_added = 0; MOO_ASSERT (moo, sem->subtype == (moo_oop_t)moo->_nil); MOO_ASSERT (moo, sem->u.io.index == (moo_oop_t)moo->_nil); /*MOO_ASSERT (moo, sem->io.handle == (moo_oop_t)moo->_nil); MOO_ASSERT (moo, sem->io.type == (moo_oop_t)moo->_nil);*/ if (io_handle < 0) { moo_seterrbfmt (moo, MOO_EINVAL, "handle %zd out of supported range", io_handle); return -1; } if (io_handle >= moo->sem_io_map_capa) { moo_oow_t new_capa, i; moo_ooi_t* tmp; /* TODO: specify the maximum io_handle supported and check it here instead of just relying on memory allocation success/failure? */ new_capa = MOO_ALIGN_POW2(io_handle + 1, SEM_IO_MAP_ALIGN); tmp = moo_reallocmem (moo, moo->sem_io_map, MOO_SIZEOF(*tmp) * new_capa); if (!tmp) { const moo_ooch_t* oldmsg = moo_backuperrmsg(moo); moo_seterrbfmt (moo, moo->errnum, "handle %zd out of supported range - %js", oldmsg); return -1; } for (i = moo->sem_io_map_capa; i < new_capa; i++) tmp[i] = -1; moo->sem_io_map = tmp; moo->sem_io_map_capa = new_capa; } index = moo->sem_io_map[io_handle]; if (index <= -1) { /* this handle is not in any tuples. add it to a new tuple */ if (moo->sem_io_tuple_count >= SEM_IO_TUPLE_MAX) { moo_seterrbfmt (moo, MOO_ESEMFLOOD, "too many IO semaphore tuples"); return -1; } if (moo->sem_io_tuple_count >= moo->sem_io_tuple_capa) { moo_oow_t new_capa; moo_sem_tuple_t* tmp; /* no overflow check when calculating the new capacity * owing to SEM_IO_TUPLE_MAX check above */ new_capa = moo->sem_io_tuple_capa + SEM_IO_TUPLE_INC; tmp = moo_reallocmem (moo, moo->sem_io_tuple, MOO_SIZEOF(moo_sem_tuple_t) * new_capa); if (!tmp) return -1; moo->sem_io_tuple = tmp; moo->sem_io_tuple_capa = new_capa; } /* this condition must be true assuming SEM_IO_TUPLE_MAX <= MOO_SMOOI_MAX */ MOO_ASSERT (moo, moo->sem_io_tuple_count <= MOO_SMOOI_MAX); index = moo->sem_io_tuple_count; tuple_added = 1; /* safe to initialize before vm_muxadd() because * moo->sem_io_tuple_count has not been incremented. * still no impact even if it fails. */ moo->sem_io_tuple[index].sem[MOO_SEMAPHORE_IO_TYPE_INPUT] = MOO_NULL; moo->sem_io_tuple[index].sem[MOO_SEMAPHORE_IO_TYPE_OUTPUT] = MOO_NULL; moo->sem_io_tuple[index].handle = io_handle; moo->sem_io_tuple[index].mask = 0; new_mask = ((moo_ooi_t)1 << io_type); moo_pushvolat (moo, (moo_oop_t*)&sem); n = moo->vmprim.vm_muxadd(moo, io_handle, new_mask); moo_popvolat (moo); } else { if (moo->sem_io_tuple[index].sem[io_type]) { moo_seterrbfmt (moo, MOO_EINVAL, "handle %zd already linked with an IO semaphore for %hs", io_handle, io_type_str[io_type]); return -1; } new_mask = moo->sem_io_tuple[index].mask; /* existing mask */ new_mask |= ((moo_ooi_t)1 << io_type); moo_pushvolat (moo, (moo_oop_t*)&sem); n = moo->vmprim.vm_muxmod(moo, io_handle, new_mask); moo_popvolat (moo); } if (n <= -1) { MOO_LOG3 (moo, MOO_LOG_WARN, "Failed to add IO semaphore at index %zd for %hs on handle %zd\n", index, io_type_str[io_type], io_handle); return -1; } MOO_LOG3 (moo, MOO_LOG_DEBUG, "Added IO semaphore at index %zd for %hs on handle %zd\n", index, io_type_str[io_type], io_handle); sem->subtype = MOO_SMOOI_TO_OOP(MOO_SEMAPHORE_SUBTYPE_IO); sem->u.io.index = MOO_SMOOI_TO_OOP(index); sem->u.io.handle = MOO_SMOOI_TO_OOP(io_handle); sem->u.io.type = MOO_SMOOI_TO_OOP((moo_ooi_t)io_type); moo->sem_io_tuple[index].handle = io_handle; moo->sem_io_tuple[index].mask = new_mask; moo->sem_io_tuple[index].sem[io_type] = sem; moo->sem_io_count++; if (tuple_added) { moo->sem_io_tuple_count++; moo->sem_io_map[io_handle] = index; } /* update the number of IO semaphores in a group if necessary */ if ((moo_oop_t)sem->group != moo->_nil) { moo_ooi_t count; count = MOO_OOP_TO_SMOOI(sem->group->sem_io_count); count++; sem->group->sem_io_count = MOO_SMOOI_TO_OOP(count); } return 0; } static int delete_sem_from_sem_io_tuple (moo_t* moo, moo_oop_semaphore_t sem, int force) { moo_ooi_t index; moo_ooi_t new_mask, io_handle, io_type; int x; MOO_ASSERT (moo, sem->subtype == MOO_SMOOI_TO_OOP(MOO_SEMAPHORE_SUBTYPE_IO)); MOO_ASSERT (moo, MOO_OOP_IS_SMOOI(sem->u.io.type)); MOO_ASSERT (moo, MOO_OOP_IS_SMOOI(sem->u.io.index)); MOO_ASSERT (moo, MOO_OOP_IS_SMOOI(sem->u.io.handle)); index = MOO_OOP_TO_SMOOI(sem->u.io.index); MOO_ASSERT (moo, index >= 0 && index < moo->sem_io_tuple_count); io_handle = MOO_OOP_TO_SMOOI(sem->u.io.handle); if (io_handle < 0 || io_handle >= moo->sem_io_map_capa) { moo_seterrbfmt (moo, MOO_EINVAL, "handle %zd out of supported range", io_handle); return -1; } MOO_ASSERT (moo, moo->sem_io_map[io_handle] == MOO_OOP_TO_SMOOI(sem->u.io.index)); io_type = MOO_OOP_TO_SMOOI(sem->u.io.type); new_mask = moo->sem_io_tuple[index].mask; new_mask &= ~((moo_ooi_t)1 << io_type); /* this is the new mask after deletion */ moo_pushvolat (moo, (moo_oop_t*)&sem); x = new_mask? moo->vmprim.vm_muxmod(moo, io_handle, new_mask): moo->vmprim.vm_muxdel(moo, io_handle); moo_popvolat (moo); if (x <= -1) { MOO_LOG3 (moo, MOO_LOG_WARN, "Failed to delete IO semaphore at index %zd handle %zd for %hs\n", index, io_handle, io_type_str[io_type]); if (!force) return -1; /* [NOTE] * this means there could be some issue handling the file handles. * the file handle might have been closed before reaching here. * assuming the callback works correctly, it's not likely that the * underlying operating system returns failure for no reason. * i should inspect the overall vm implementation */ MOO_LOG1 (moo, MOO_LOG_ERROR, "Forcibly unmapping the IO semaphored handle %zd as if it's deleted\n", io_handle); } else { MOO_LOG3 (moo, MOO_LOG_DEBUG, "Deleted IO semaphore at index %zd handle %zd for %hs\n", index, io_handle, io_type_str[io_type]); } sem->subtype = moo->_nil; sem->u.io.index = moo->_nil; sem->u.io.handle = moo->_nil; sem->u.io.type = moo->_nil; moo->sem_io_count--; if ((moo_oop_t)sem->group != moo->_nil) { moo_ooi_t count; count = MOO_OOP_TO_SMOOI(sem->group->sem_io_count); MOO_ASSERT (moo, count > 0); count--; sem->group->sem_io_count = MOO_SMOOI_TO_OOP(count); } if (new_mask) { moo->sem_io_tuple[index].mask = new_mask; moo->sem_io_tuple[index].sem[io_type] = MOO_NULL; } else { moo->sem_io_tuple_count--; if (/*moo->sem_io_tuple_count > 0 &&*/ index != moo->sem_io_tuple_count) { /* migrate the last item to the deleted slot to compact the gap */ moo->sem_io_tuple[index] = moo->sem_io_tuple[moo->sem_io_tuple_count]; if (moo->sem_io_tuple[index].sem[MOO_SEMAPHORE_IO_TYPE_INPUT]) moo->sem_io_tuple[index].sem[MOO_SEMAPHORE_IO_TYPE_INPUT]->u.io.index = MOO_SMOOI_TO_OOP(index); if (moo->sem_io_tuple[index].sem[MOO_SEMAPHORE_IO_TYPE_OUTPUT]) moo->sem_io_tuple[index].sem[MOO_SEMAPHORE_IO_TYPE_OUTPUT]->u.io.index = MOO_SMOOI_TO_OOP(index); moo->sem_io_map[moo->sem_io_tuple[index].handle] = index; MOO_LOG2 (moo, MOO_LOG_DEBUG, "Migrated IO semaphore tuple from index %zd to %zd\n", moo->sem_io_tuple_count, index); } moo->sem_io_map[io_handle] = -1; } return 0; } static void _signal_io_semaphore (moo_t* moo, moo_oop_semaphore_t sem) { moo_oop_process_t proc; proc = signal_semaphore (moo, sem); if (moo->processor->active == moo->nil_process && (moo_oop_t)proc != moo->_nil) { /* this is the only runnable process. * switch the process to the running state. * it uses wake_process() instead of * switch_to_process() as there is no running * process at this moment */ MOO_ASSERT (moo, proc->state == MOO_SMOOI_TO_OOP(PROC_STATE_RUNNABLE)); MOO_ASSERT (moo, proc == moo->processor->runnable.first); #if 0 wake_process (moo, proc); /* switch to running */ moo->proc_switched = 1; #else switch_to_process_from_nil (moo, proc); #endif } } static void signal_io_semaphore (moo_t* moo, moo_ooi_t io_handle, moo_ooi_t mask) { if (io_handle >= 0 && io_handle < moo->sem_io_map_capa && moo->sem_io_map[io_handle] >= 0) { moo_oop_semaphore_t insem, outsem; moo_ooi_t sem_io_index; sem_io_index = moo->sem_io_map[io_handle]; insem = moo->sem_io_tuple[sem_io_index].sem[MOO_SEMAPHORE_IO_TYPE_INPUT]; outsem = moo->sem_io_tuple[sem_io_index].sem[MOO_SEMAPHORE_IO_TYPE_OUTPUT]; if (outsem) { if ((mask & (MOO_SEMAPHORE_IO_MASK_OUTPUT | MOO_SEMAPHORE_IO_MASK_ERROR)) || (!insem && (mask & MOO_SEMAPHORE_IO_MASK_HANGUP))) { _signal_io_semaphore (moo, outsem); } } if (insem) { if (mask & (MOO_SEMAPHORE_IO_MASK_INPUT | MOO_SEMAPHORE_IO_MASK_HANGUP | MOO_SEMAPHORE_IO_MASK_ERROR)) { _signal_io_semaphore (moo, insem); } } } else { /* you may come across this warning message if the multiplexer returned * an IO event */ MOO_LOG2 (moo, MOO_LOG_WARN, "Warning - semaphore signaling requested on an unmapped handle %zd with mask %#zx\n", io_handle, mask); } } void moo_releaseiohandle (moo_t* moo, moo_ooi_t io_handle) { /* TODO: optimize io semapore unmapping. since i'm to close the handle, * i don't need to call delete_sem_from_sem_io_tuple() seperately for input * and output. */ if (io_handle < moo->sem_io_map_capa) { moo_ooi_t index; moo_oop_semaphore_t sem; index = moo->sem_io_map[io_handle]; if (index >= 0) { MOO_ASSERT(moo, moo->sem_io_tuple[index].handle == io_handle); sem = moo->sem_io_tuple[index].sem[MOO_SEMAPHORE_IO_TYPE_INPUT]; if (sem) { MOO_ASSERT(moo, sem->subtype == MOO_SMOOI_TO_OOP(MOO_SEMAPHORE_SUBTYPE_IO)); delete_sem_from_sem_io_tuple (moo, sem, 0); } } } if (io_handle < moo->sem_io_map_capa) { moo_ooi_t index; moo_oop_semaphore_t sem; index = moo->sem_io_map[io_handle]; if (index >= 0) { MOO_ASSERT(moo, moo->sem_io_tuple[index].handle == io_handle); sem = moo->sem_io_tuple[index].sem[MOO_SEMAPHORE_IO_TYPE_OUTPUT]; if (sem) { MOO_ASSERT(moo, sem->subtype == MOO_SMOOI_TO_OOP(MOO_SEMAPHORE_SUBTYPE_IO)); delete_sem_from_sem_io_tuple (moo, sem, 0); } } } } /* ------------------------------------------------------------------------- */ static moo_oop_process_t start_initial_process (moo_t* moo, moo_oop_context_t c) { moo_oop_process_t proc; /* there must be no active process when this function is called */ MOO_ASSERT (moo, moo->processor->runnable.count == MOO_SMOOI_TO_OOP(0)); MOO_ASSERT (moo, moo->processor->active == moo->nil_process); proc = make_process(moo, c, MOO_OBJ_FLAGS_PROC_INNATE); if (MOO_UNLIKELY(!proc)) return MOO_NULL; chain_into_processor (moo, proc, PROC_STATE_RUNNING); moo->processor->active = proc; /* do somthing that resume_process() would do with less overhead */ MOO_ASSERT (moo, (moo_oop_t)proc->current_context != moo->_nil); MOO_ASSERT (moo, proc->current_context == proc->initial_context); SWITCH_ACTIVE_CONTEXT (moo, proc->current_context); return proc; } static MOO_INLINE int activate_new_method (moo_t* moo, moo_oop_method_t mth, moo_ooi_t actual_nargs) { moo_oop_context_t ctx; moo_ooi_t i, j; moo_ooi_t ntmprs, nargs, actual_ntmprs; ntmprs = MOO_OOP_TO_SMOOI(mth->tmpr_count); nargs = MOO_OOP_TO_SMOOI(mth->tmpr_nargs); MOO_ASSERT (moo, ntmprs >= 0); MOO_ASSERT (moo, nargs <= ntmprs); if (actual_nargs > nargs) { /* more arguments than the method specification have been passed in. * it must be a variadic or liberal unary method. othewise, the compiler is buggy */ MOO_ASSERT (moo, MOO_METHOD_GET_PREAMBLE_FLAGS(MOO_OOP_TO_SMOOI(mth->preamble)) & (MOO_METHOD_PREAMBLE_FLAG_VARIADIC | MOO_METHOD_PREAMBLE_FLAG_LIBERAL)); actual_ntmprs = ntmprs + (actual_nargs - nargs); } else actual_ntmprs = ntmprs; moo_pushvolat (moo, (moo_oop_t*)&mth); ctx = (moo_oop_context_t)moo_instantiate(moo, moo->_method_context, MOO_NULL, actual_ntmprs); moo_popvolat (moo); if (MOO_UNLIKELY(!ctx)) return -1; MOO_STORE_OOP (moo, (moo_oop_t*)&ctx->sender, (moo_oop_t)moo->active_context); ctx->ip = MOO_SMOOI_TO_OOP(0); /* ctx->sp will be set further down */ /* A context is compose of a fixed part and a variable part. * the variable part holds temporary varibles including arguments. * * Assuming a method context with 2 arguments and 3 local temporary * variables, the context will look like this. * +---------------------+ * | fixed part | * | | * | | * | | * +---------------------+ * | tmp1 (arg1) | slot[0] * | tmp2 (arg2) | slot[1] * | tmp3 | slot[2] * | tmp4 | slot[3] * | tmp5 | slot[4] * +---------------------+ */ ctx->ntmprs = MOO_SMOOI_TO_OOP(ntmprs); MOO_STORE_OOP (moo, &ctx->method_or_nargs, (moo_oop_t)mth); /* the 'home' field of a method context is always moo->_nil. ctx->home = moo->_nil;*/ MOO_STORE_OOP (moo, (moo_oop_t*)&ctx->origin, (moo_oop_t)ctx); /* point to self */ /* * Assume this message sending expression: * obj1 do: #this with: #that with: #it * * It would be compiled to these logical byte-code sequences shown below: * push obj1 * push #this * push #that * push #it * send #do:with: * * After three pushes, the stack looks like this. * * | #it | <- sp * | #that | sp - 1 * | #this | sp - 2 * | obj1 | sp - nargs * * Since the number of arguments is 3, stack[sp - 3] points to * the receiver. When the stack is empty, sp is -1. */ if (actual_nargs >= nargs) { for (i = actual_nargs, j = ntmprs + (actual_nargs - nargs); i > nargs; i--) { /* place variadic arguments after local temporaries */ --j; MOO_STORE_OOP (moo, &ctx->stack[j], MOO_STACK_GETTOP(moo)); MOO_STACK_POP (moo); } MOO_ASSERT (moo, i == nargs); while (i > 0) { /* place normal argument before local temporaries */ --i; MOO_STORE_OOP (moo, &ctx->stack[i], MOO_STACK_GETTOP(moo)); MOO_STACK_POP (moo); } } else { for (i = actual_nargs; i > 0; ) { /* place normal argument before local temporaries */ --i; MOO_STORE_OOP (moo, &ctx->stack[i], MOO_STACK_GETTOP(moo)); MOO_STACK_POP (moo); } } /* copy receiver */ MOO_STORE_OOP (moo, &ctx->receiver_or_base, MOO_STACK_GETTOP(moo)); MOO_STACK_POP (moo); MOO_ASSERT (moo, moo->sp >= -1); /* the stack pointer in a context is a stack pointer of a process * before it is activated. this stack pointer is stored to the context * so that it is used to restore the process stack pointer upon returning * from a method context. */ ctx->sp = MOO_SMOOI_TO_OOP(moo->sp); /* switch the active context to the newly instantiated one*/ SWITCH_ACTIVE_CONTEXT (moo, ctx); return 0; } static MOO_INLINE moo_oop_method_t find_method_in_class (moo_t* moo, moo_oop_class_t _class, int mth_type, const moo_oocs_t* name) { moo_oop_association_t ass; moo_oop_dic_t mthdic; mthdic = _class->mthdic[mth_type]; /* if a kernel class is not defined in the bootstrapping code, * the method dictionary is still nil. you must define all the initial * kernel classes properly before you can use this function */ MOO_ASSERT (moo, (moo_oop_t)mthdic != moo->_nil); MOO_ASSERT (moo, MOO_CLASSOF(moo, mthdic) == moo->_method_dictionary); ass = (moo_oop_association_t)moo_lookupdic_noseterr(moo, mthdic, name); if (ass) { /* found the method */ MOO_ASSERT (moo, MOO_CLASSOF(moo, ass->value) == moo->_method); return (moo_oop_method_t)ass->value; } /* [NOTE] moo_seterrXXX() is not called here */ return MOO_NULL; } static MOO_INLINE moo_oop_method_t find_method_in_class_chain (moo_t* moo, moo_oop_class_t _class, int mth_type, const moo_oocs_t* name) { moo_oop_method_t mth; do { mth = find_method_in_class(moo, _class, mth_type, name); if (mth) return mth; _class = (moo_oop_class_t)_class->superclass; } while ((moo_oop_t)_class != moo->_nil); /* [NOTE] moo_seterrXXX() is not called here */ return MOO_NULL; } moo_oop_method_t moo_findmethodinclass (moo_t* moo, moo_oop_class_t _class, int mth_type, const moo_oocs_t* name) { moo_oop_method_t mth; mth = find_method_in_class(moo, _class, mth_type, name); if (!mth) moo_seterrnum (moo, MOO_ENOENT); return mth; } moo_oop_method_t moo_findmethodinclasschain (moo_t* moo, moo_oop_class_t _class, int mth_type, const moo_oocs_t* name) { moo_oop_method_t mth; mth = find_method_in_class_chain(moo, _class, mth_type, name); if (!mth) moo_seterrnum (moo, MOO_ENOENT); return mth; } moo_oop_method_t moo_findmethod_noseterr (moo_t* moo, moo_oop_t receiver, moo_oop_char_t selector, int in_super) { moo_oop_class_t _class; moo_oop_class_t c; int mth_type; moo_oop_method_t mth; moo_oocs_t message; moo_oow_t mcidx; moo_method_cache_item_t* mcitm; message.ptr = MOO_OBJ_GET_CHAR_SLOT(selector); message.len = MOO_OBJ_GET_SIZE(selector); //MOO_DEBUG4 (moo, "XXXXXXXXXxx %p %p %O %O\n", receiver, selector, receiver, selector); _class = MOO_CLASSOF(moo, receiver); if (_class == moo->_class) { /* receiver is a class object (an instance of Class) */ c = (moo_oop_class_t)receiver; mth_type = MOO_METHOD_CLASS; } else { /* receiver is not a class object. so take its class */ c = _class; mth_type = MOO_METHOD_INSTANCE; } MOO_ASSERT (moo, (moo_oop_t)c != moo->_nil); if (in_super) { MOO_ASSERT (moo, moo->active_method != MOO_NULL); MOO_ASSERT (moo, moo->active_method->owner != MOO_NULL); /* if 'super' is allowed in the interface method, the owner field * can be an interface. super must not be allowed in the interface * method body. */ MOO_ASSERT (moo, MOO_CLASSOF(moo, moo->active_method->owner) == moo->_class); c = (moo_oop_class_t)((moo_oop_class_t)moo->active_method->owner)->superclass; if ((moo_oop_t)c == moo->_nil) goto not_found; /* c is nil if it reached the top of the hierarchy. * otherwise c points to a class object */ } /*mcidx = MOO_HASH_INIT; mcidx = MOO_HASH_VALUE(mcidx, (moo_oow_t)c); mcidx = MOO_HASH_VALUE(mcidx, (moo_oow_t)selector); mcidx &= (MOO_METHOD_CACHE_SIZE - 1);*/ /*mcidx = ((moo_oow_t)c + (moo_oow_t)selector) % MOO_METHOD_CACHE_SIZE; */ mcidx = ((moo_oow_t)_class ^ (moo_oow_t)selector) & (MOO_METHOD_CACHE_SIZE - 1); mcitm = &moo->method_cache[mth_type][mcidx]; if (mcitm->receiver_class == c && mcitm->selector == selector /*&& mcitm->method_type == mth_type*/) { /* cache hit */ #if defined(MOO_PROFILE_VM) moo->stat.method_cache_hits++; #endif return mcitm->method; } /* [IMPORT] the method lookup logic should be the same as ciim_on_each_method() in comp.c */ mth = find_method_in_class_chain(moo, c, mth_type, &message); if (mth) { #if defined(MOO_PROFILE_VM) moo->stat.method_cache_misses++; #endif mcitm->receiver_class = c; mcitm->selector = selector; /*mcitm->method_type = mth_type;*/ mcitm->method = mth; return mth; } not_found: if (_class == moo->_class) { /* the object is an instance of Class. find the method * in an instance method dictionary of Class also */ /*mcidx = MOO_HASH_INIT; mcidx = MOO_HASH_VALUE(mcidx, (moo_oow_t)_class); mcidx = MOO_HASH_VALUE(mcidx, (moo_oow_t)selector); mcidx &= (MOO_METHOD_CACHE_SIZE - 1); */ /* mcidx = ((moo_oow_t)_class + (moo_oow_t)selector) % MOO_METHOD_CACHE_SIZE; */ mcidx = ((moo_oow_t)_class ^ (moo_oow_t)selector) & (MOO_METHOD_CACHE_SIZE - 1); mcitm = &moo->method_cache[MOO_METHOD_INSTANCE][mcidx]; if (mcitm->receiver_class == _class && mcitm->selector == selector /*&& mcitm->method_type == MOO_METHOD_INSTANCE*/) { /* cache hit */ #if defined(MOO_PROFILE_VM) moo->stat.method_cache_hits++; #endif return mcitm->method; } mth = find_method_in_class(moo, _class, MOO_METHOD_INSTANCE, &message); if (mth) { #if defined(MOO_PROFILE_VM) moo->stat.method_cache_misses++; #endif mcitm->receiver_class = c; mcitm->selector = selector; /*mcitm->method_type = MOO_METHOD_INSTANCE;*/ mcitm->method = mth; return mth; } } return MOO_NULL; } moo_oop_method_t moo_findmethod (moo_t* moo, moo_oop_t receiver, moo_oop_char_t selector, int in_super) { moo_oop_method_t tmp; tmp = moo_findmethod_noseterr(moo, receiver, selector, in_super); if (!tmp) { /*MOO_LOG4 (moo, MOO_LOG_DEBUG, "Method '%O>>%.*js' not found in receiver %O\n", _class, message.len, message.ptr, receiver);*/ moo_seterrbfmt (moo, MOO_ENOENT, "unable to find the method '%O>>%.*js' in %O", MOO_CLASSOF(moo, receiver), MOO_OBJ_GET_CHAR_SLOT(selector), MOO_OBJ_GET_SIZE(selector), receiver); } return tmp; } void moo_clearmethodcache (moo_t* moo) { MOO_MEMSET (moo->method_cache, 0, MOO_SIZEOF(moo->method_cache)); } static int start_initial_process_and_context (moo_t* moo, const moo_oocs_t* objname, const moo_oocs_t* mthname) { /* the initial context is a fake context. if objname is 'Moo' and * mthname is 'main', this function emulates message sending 'Moo main'. * it should emulate the following logical byte-code sequences: * * push Moo * send #main */ moo_oop_context_t ctx; moo_oop_method_t mth; moo_oop_process_t proc; moo_oow_t tmp_count = 0; moo_oop_t sym_startup; #if defined(INVOKE_DIRECTLY) moo_oop_association_t ass; #else moo_oop_t s1, s2; static moo_ooch_t str_startup[] = { 's', 't', 'a', 'r', 't', 'u', 'p' }; #endif #if defined(INVOKE_DIRECTLY) ass = moo_lookupsysdic(moo, objname); if (!ass || MOO_CLASSOF(moo, ass->value) != moo->_class) { MOO_LOG2 (moo, MOO_LOG_DEBUG, "Cannot find a class '%.*js'", objname->len, objname->ptr); return -1; } sym_statup = moo_findsymbol(moo, mthname->ptr, mthname->len; if (!sym_startup) { /* the method name should exist as a symbol in the system. * otherwise, a method of such a name also doesn't exist */ MOO_LOG4 (moo, MOO_LOG_DEBUG, "Cannot find a startup method symbol %.*js>>%.*js", objname->len, objname->ptr, mthname->len, mthname->ptr); goto oops; } mth = moo_findmethod(moo, moo, ass->value, sym_startup, 0); if (!mth) { MOO_LOG4 (moo, MOO_LOG_DEBUG, "Cannot find a startup method %.*js>>%.*js", objname->len, objname->ptr, mthname->len, mthname->ptr); return -1; } if (MOO_OOP_TO_SMOOI(mth->tmpr_nargs) > 0) { /* this method expects more than 0 arguments. * i can't use it as a start-up method. TODO: overcome this problem - accept parameters.... */ MOO_LOG4 (moo, MOO_LOG_DEBUG, (moo, "Arguments not supported by a startup method - %.*js>>%.*js", objname->len, objname->ptr, mthname->len, mthname->ptr); moo_seterrbfmt (moo, MOO_EINVAL, "arguments not supported by a startup method %.*js>>%.*js", objname->len, objname->ptr, mthname->len, mthname->ptr); return -1; } moo_pushvolat (moo, (moo_oop_t*)&mth); tmp_count++; moo_pushvolat (moo, (moo_oop_t*)&ass); tmp_count++; #else /* invoke System startup(class_name, method_name) intead of * sending the method to the class directly */ sym_startup = moo_findsymbol(moo, str_startup, MOO_COUNTOF(str_startup)); if (!sym_startup) { /* the method name should exist as a symbol in the system. * otherwise, a method of such a name also doesn't exist */ MOO_LOG0 (moo, MOO_LOG_DEBUG, "Cannot find the symbol #startup"); goto oops; } mth = moo_findmethod(moo, (moo_oop_t)moo->_system, (moo_oop_char_t)sym_startup, 0); if (!mth) { MOO_LOG0 (moo, MOO_LOG_DEBUG, "Cannot find the startup method in the system class"); goto oops; } if (MOO_OOP_TO_SMOOI(mth->tmpr_nargs) != 2) { MOO_LOG1 (moo, MOO_LOG_DEBUG, "Weird argument count %zd for a startup method - should be 2", MOO_OOP_TO_SMOOI(mth->tmpr_nargs)); moo_seterrbfmt (moo, MOO_EINVAL, "%.*js>>%.*js accepting weird argument count %zd, must accept 2 only", objname->len, objname->ptr, mthname->len, mthname->ptr, MOO_OOP_TO_SMOOI(mth->tmpr_nargs)); goto oops; } /* TODO: check if it's variadic.... it should be. and accept more than 2... */ moo_pushvolat (moo, (moo_oop_t*)&mth); tmp_count++; s1 = moo_makesymbol(moo, objname->ptr, objname->len); if (MOO_UNLIKELY(!s1)) goto oops; moo_pushvolat (moo, (moo_oop_t*)&s1); tmp_count++; s2 = moo_makesymbol(moo, mthname->ptr, mthname->len); if (MOO_UNLIKELY(!s2)) goto oops; moo_pushvolat (moo, (moo_oop_t*)&s2); tmp_count++; #endif /* create a fake initial context. */ ctx = (moo_oop_context_t)moo_instantiate(moo, moo->_method_context, MOO_NULL, MOO_OOP_TO_SMOOI(mth->tmpr_nargs)); if (MOO_UNLIKELY(!ctx)) goto oops; moo_pushvolat (moo, (moo_oop_t*)&ctx); tmp_count++; /* TODO: handle preamble */ /* the initial context starts the life of the entire VM * and is not really worked on except that it is used to call the * initial method. so it doesn't really require any extra stack space. */ /* TODO: verify this theory of mine. */ moo->ip = 0; moo->sp = -1; ctx->ip = MOO_SMOOI_TO_OOP(0); /* point to the beginning */ ctx->sp = MOO_SMOOI_TO_OOP(-1); /* pointer to -1 below the bottom */ /* ctx->home = moo->_nil; */ MOO_STORE_OOP (moo, (moo_oop_t*)&ctx->origin, (moo_oop_t)ctx); /* point to self */ MOO_STORE_OOP (moo, (moo_oop_t*)&ctx->method_or_nargs, (moo_oop_t)mth); /* fake. help SWITCH_ACTIVE_CONTEXT() not fail. TODO: create a static fake method and use it... instead of 'mth' */ /* [NOTE] * the receiver field and the sender field of ctx are nils. * especially, the fact that the sender field is nil is used by * the main execution loop for breaking out of the loop */ MOO_ASSERT (moo, moo->active_context == MOO_NULL); MOO_ASSERT (moo, moo->active_method == MOO_NULL); /* moo_gc() uses moo->processor when moo->active_context * is not NULL. at this poinst, moo->processor should point to * an instance of ProcessScheduler. */ MOO_ASSERT (moo, (moo_oop_t)moo->processor != moo->_nil); MOO_ASSERT (moo, moo->processor->runnable.count == MOO_SMOOI_TO_OOP(0)); /* start_initial_process() calls the SWITCH_ACTIVE_CONTEXT() macro. * the macro assumes a non-null value in moo->active_context. * let's forcefully set active_context to ctx directly. */ moo->active_context = ctx; proc = start_initial_process(moo, ctx); moo_popvolats (moo, tmp_count); tmp_count = 0; if (MOO_UNLIKELY(!proc)) goto oops; #if defined(INVOKE_DIRECTLY) MOO_STACK_PUSH (moo, ass->value); /* push the receiver - the object referenced by 'objname' */ #else MOO_STACK_PUSH (moo, (moo_oop_t)moo->_system); MOO_STACK_PUSH (moo, s1); MOO_STACK_PUSH (moo, s2); #endif STORE_ACTIVE_SP (moo); /* moo->active_context->sp = MOO_SMOOI_TO_OOP(moo->sp) */ MOO_ASSERT (moo, moo->processor->active == proc); MOO_ASSERT (moo, moo->processor->active->initial_context == ctx); MOO_ASSERT (moo, moo->processor->active->current_context == ctx); MOO_ASSERT (moo, moo->active_context == ctx); /* emulate the message sending */ #if defined(INVOKE_DIRECTLY) return activate_new_method(moo, mth, 0); #else return activate_new_method(moo, mth, 2); #endif oops: if (tmp_count > 0) moo_popvolats (moo, tmp_count); return -1; } /* ------------------------------------------------------------------------- */ static moo_pfrc_t pf_dump (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_ooi_t i; MOO_ASSERT (moo, nargs >= 0); /*moo_logbfmt (moo, 0, "RECEIVER: %O IN PID %d SP %d XSP %d\n", MOO_STACK_GET(moo, moo->sp - nargs), (int)MOO_OOP_TO_SMOOI(moo->processor->active->id), (int)moo->sp, (int)MOO_OOP_TO_SMOOI(moo->processor->active->sp));*/ moo_logbfmt (moo, MOO_LOG_APP | MOO_LOG_FATAL, "PID[%d] %O\n", (int)MOO_OOP_TO_SMOOI(moo->processor->active->id), MOO_STACK_GET(moo, moo->sp - nargs)); for (i = nargs; i > 0; ) { --i; moo_logbfmt (moo, MOO_LOG_APP | MOO_LOG_FATAL, "ARGUMENT %zd: %O\n", i, MOO_STACK_GET(moo, moo->sp - i)); } MOO_STACK_SETRETTORCV (moo, nargs); /* ^self */ return MOO_PF_SUCCESS; } static void log_char_object (moo_t* moo, moo_bitmask_t mask, moo_oop_char_t msg) { moo_ooi_t n; moo_oow_t rem; const moo_ooch_t* ptr; MOO_ASSERT (moo, MOO_OBJ_GET_FLAGS_TYPE(msg) == MOO_OBJ_TYPE_CHAR); rem = MOO_OBJ_GET_SIZE(msg); ptr = MOO_OBJ_GET_CHAR_SLOT(msg); start_over: while (rem > 0) { if (*ptr == '\0') { n = moo_logbfmt (moo, mask, "%jc", *ptr); MOO_ASSERT (moo, n == 1); rem -= n; ptr += n; goto start_over; } n = moo_logbfmt (moo, mask, "%.*js", rem, ptr); if (n <= -1) break; if (n == 0) { /* to skip the unprinted character. * actually, this check is not needed because of '\0' skipped * at the beginning of the loop */ n = moo_logbfmt (moo, mask, "%jc", *ptr); MOO_ASSERT (moo, n == 1); } rem -= n; ptr += n; } } static moo_pfrc_t pf_add_to_be_finalized (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { /* TODO: check if it has already been added */ moo_regfinalizable (moo, MOO_STACK_GETRCV(moo,nargs)); MOO_STACK_SETRETTORCV (moo, nargs); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_remove_to_be_finalized (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { /* TODO: check if it has already been added */ moo_deregfinalizable (moo, MOO_STACK_GETRCV(moo,nargs)); MOO_STACK_SETRETTORCV (moo, nargs); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_hash (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv; moo_oow_t hv; MOO_ASSERT (moo, nargs == 0); rcv = MOO_STACK_GETRCV(moo, nargs); switch (MOO_OOP_GET_TAG(rcv)) { case MOO_OOP_TAG_SMOOI: hv = MOO_OOP_TO_SMOOI(rcv); break; case MOO_OOP_TAG_SMPTR: hv = (moo_oow_t)MOO_OOP_TO_SMPTR(rcv); break; case MOO_OOP_TAG_CHAR: hv = MOO_OOP_TO_CHAR(rcv); break; case MOO_OOP_TAG_ERROR: hv = MOO_OOP_TO_ERROR(rcv); break; default: { if (rcv == moo->_nil) hv = 0; else if (rcv == moo->_true) hv = 1; else if (rcv == moo->_false) hv = 2; else { MOO_ASSERT (moo, MOO_OOP_IS_POINTER(rcv)); switch (MOO_OBJ_GET_FLAGS_TYPE(rcv)) { case MOO_OBJ_TYPE_BYTE: hv = moo_hash_bytes(MOO_OBJ_GET_BYTE_SLOT(rcv), MOO_OBJ_GET_SIZE(rcv)); break; case MOO_OBJ_TYPE_CHAR: hv = moo_hash_oochars(MOO_OBJ_GET_CHAR_SLOT(rcv), MOO_OBJ_GET_SIZE(rcv)); break; case MOO_OBJ_TYPE_HALFWORD: hv = moo_hash_halfwords(MOO_OBJ_GET_HALFWORD_SLOT(rcv), MOO_OBJ_GET_SIZE(rcv)); break; case MOO_OBJ_TYPE_WORD: hv = moo_hash_words(MOO_OBJ_GET_WORD_SLOT(rcv), MOO_OBJ_GET_SIZE(rcv)); break; default: /* MOO_OBJ_TYPE_OOP, ... */ switch (MOO_OBJ_GET_FLAGS_HASH(rcv)) { case MOO_OBJ_FLAGS_HASH_UNUSED: /* the object in the permanent space doesn't need this. but never mind */ /*if (!MOO_OBJ_GET_FLAGS_PERM(rcv)) */ MOO_OBJ_SET_FLAGS_HASH (rcv, MOO_OBJ_FLAGS_HASH_CALLED); /* fall thru */ case MOO_OBJ_FLAGS_HASH_CALLED: hv = ((moo_oow_t)rcv) % (MOO_SMOOI_MAX + 1); break; case MOO_OBJ_FLAGS_HASH_STORED: hv = *(moo_oow_t*)((moo_uint8_t*)rcv + MOO_SIZEOF(moo_obj_t) + moo_getobjpayloadbytes(moo, rcv) - MOO_SIZEOF(moo_oow_t)); break; default: /* this must not happend. internal error */ moo_seterrbfmt (moo, MOO_EINTERN, "internal error - unknown hash flags %d in %O", (int)MOO_OBJ_GET_FLAGS_HASH(rcv), rcv); return MOO_PF_HARD_FAILURE; } break; } } break; } } /* moo_hashxxx() functions should limit the return value to fall * in the range between 0 and MOO_SMOOI_MAX inclusive */ MOO_ASSERT (moo, hv >= 0 && hv <= MOO_SMOOI_MAX); MOO_STACK_SETRET (moo, nargs, MOO_SMOOI_TO_OOP(hv)); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_perform (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t /*rcv,*/ selector; moo_oow_t ssp, esp, i; MOO_ASSERT (moo, nargs >= 1); /* at least, a selector must be specified */ /*rcv = MOO_STACK_GETRCV(moo, nargs);*/ selector = MOO_STACK_GETARG(moo, nargs, 0); if (MOO_CLASSOF(moo,selector) != moo->_symbol) { moo_seterrbfmt (moo, MOO_EINVAL, "invalid argument - %O", selector); return MOO_PF_FAILURE; } /* remove the selector from the stack */ ssp = MOO_STACK_GETARGSP(moo, nargs, 0); esp = MOO_STACK_GETARGSP(moo, nargs, nargs - 1); for (i = ssp; i < esp; i++) { moo_oop_t t; t = MOO_STACK_GET(moo, i + 1); MOO_STACK_SET(moo, i, t); } MOO_STACK_POP (moo); /* emulate message sending */ if (send_message(moo, (moo_oop_char_t)selector, nargs - 1, 0) <= -1) return MOO_PF_HARD_FAILURE; return MOO_PF_SUCCESS; } /* ------------------------------------------------------------------ */ static moo_pfrc_t pf_context_goto (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv; moo_oop_t pc; /* this primivie provides the similar functionality to MethodContext>>pc: * except that it pops the receiver and arguments and doesn't push a * return value. it's useful when you want to change the instruction * pointer while maintaining the stack level before the call */ MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); MOO_PF_CHECK_RCV (moo, MOO_CLASSOF(moo,rcv) == moo->_method_context); pc = MOO_STACK_GETARG(moo, nargs, 0); if (!MOO_OOP_IS_SMOOI(pc) || MOO_OOP_TO_SMOOI(pc) < 0) { moo_seterrbfmt (moo, MOO_EINVAL, "invalid pc - %O", pc); return MOO_PF_FAILURE; } ((moo_oop_context_t)rcv)->ip = pc; /* no need to MOO_STORE_OOP as pc is a small integer */ LOAD_ACTIVE_IP (moo); MOO_ASSERT (moo, nargs + 1 == 2); MOO_STACK_POPS (moo, 2); /* pop both the argument and the receiver */ return MOO_PF_SUCCESS; } static moo_pfrc_t pf_context_find_exception_handler (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_context_t rcv; moo_ooi_t preamble; moo_oop_class_t except_class; rcv = (moo_oop_context_t)MOO_STACK_GETRCV(moo, nargs); MOO_PF_CHECK_RCV (moo, MOO_CLASSOF(moo,rcv) == moo->_method_context); except_class = (moo_oop_class_t)MOO_STACK_GETARG(moo, nargs, 0); MOO_PF_CHECK_ARGS (moo, nargs, MOO_CLASSOF(moo,except_class) == moo->_class); preamble = MOO_OOP_TO_SMOOI(((moo_oop_method_t)rcv->method_or_nargs)->preamble); if (MOO_METHOD_GET_PREAMBLE_CODE(preamble) == MOO_METHOD_PREAMBLE_EXCEPTION) { /* context * on: ... do: ...*/ moo_oow_t size, i; size = MOO_OBJ_GET_SIZE(rcv); for (i = MOO_CONTEXT_NAMED_INSTVARS; i < size; i += 2) { /* [NOTE] the following loop scans all parameters to the on:do: method. * if the on:do: method contains local temporary variables, * you must change this function to skip scanning local variables. * the current on:do: method has 1 local variable declared. * as local variables are placed after method arguments and * the loop increments 'i' by 2, the last element is naturally * get excluded from inspection. */ moo_oop_class_t on_class; on_class = (moo_oop_class_t)MOO_OBJ_GET_OOP_VAL(rcv, i); if (on_class == except_class || (MOO_CLASSOF(moo, on_class) == moo->_class && moo_ischildclassof(moo, except_class, on_class))) { MOO_STACK_SETRET (moo, nargs, MOO_OBJ_GET_OOP_VAL(rcv, i + 1)); return MOO_PF_SUCCESS; } } } MOO_STACK_SETRET (moo, nargs, moo->_nil); return MOO_PF_SUCCESS; } /* ------------------------------------------------------------------ */ static MOO_INLINE moo_dbgi_method_t* get_dbgi_method (moo_t* moo, moo_oop_method_t mth) { moo_dbgi_method_t* di = MOO_NULL; if (moo->dbgi && MOO_OOP_IS_SMOOI(mth->dbgi_method_offset)) { moo_ooi_t dbgi_method_offset; dbgi_method_offset = MOO_OOP_TO_SMOOI(mth->dbgi_method_offset); if (dbgi_method_offset > 0) { MOO_ASSERT (moo, dbgi_method_offset < moo->dbgi->_len); di = (moo_dbgi_method_t*)MOO_DBGI_GET_DATA(moo, dbgi_method_offset); MOO_ASSERT (moo, MOO_DBGI_GET_TYPE_CODE(di->_type) == MOO_DBGI_TYPE_CODE_METHOD); } } return di; } static moo_pfrc_t pf_method_get_source_file (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_method_t rcv; moo_oop_t retv = moo->_nil; /* TODO: empty string instead of nil? */ /* TODO: return something else lighter-weight than a string? */ rcv = (moo_oop_method_t)MOO_STACK_GETRCV(moo, nargs); MOO_PF_CHECK_RCV (moo, MOO_CLASSOF(moo, rcv) == moo->_method); if (moo->dbgi) { if (MOO_OOP_IS_SMOOI(rcv->dbgi_file_offset)) { moo_ooi_t dbgi_file_offset; dbgi_file_offset = MOO_OOP_TO_SMOOI(rcv->dbgi_file_offset); if (dbgi_file_offset > 0) { moo_dbgi_file_t* di; const moo_ooch_t* file_name; MOO_ASSERT (moo, dbgi_file_offset < moo->dbgi->_len); di = (moo_dbgi_file_t*)MOO_DBGI_GET_DATA(moo, dbgi_file_offset); MOO_ASSERT (moo, MOO_DBGI_GET_TYPE_CODE(di->_type) == MOO_DBGI_TYPE_CODE_FILE); file_name = (const moo_ooch_t*)(di + 1); retv = moo_makestring(moo, file_name, moo_count_oocstr(file_name)); if (!retv) return MOO_PF_FAILURE; } } } MOO_STACK_SETRET (moo, nargs, retv); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_method_get_ip_source_line (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { /* get source line of the given instruction pointer */ moo_oop_method_t rcv; moo_oop_t ip; moo_dbgi_method_t* di; moo_oop_t retv = MOO_SMOOI_TO_OOP(0); rcv = (moo_oop_method_t)MOO_STACK_GETRCV(moo, nargs); MOO_PF_CHECK_RCV (moo, MOO_CLASSOF(moo, rcv) == moo->_method); ip = MOO_STACK_GETARG(moo, nargs, 0); MOO_PF_CHECK_ARGS (moo, nargs, MOO_OOP_IS_SMOOI(ip)); di = get_dbgi_method(moo, rcv); if (di) { moo_ooi_t ipv; moo_oow_t* code_loc_ptr; ipv = MOO_OOP_TO_SMOOI(ip); code_loc_ptr = (moo_oow_t*)((moo_uint8_t*)(di + 1) + di->code_loc_start); if (ipv < di->code_loc_len && code_loc_ptr[ipv] <= MOO_SMOOI_MAX) { retv = moo_oowtoint(moo, code_loc_ptr[ipv]); if (MOO_UNLIKELY(!retv)) return MOO_PF_FAILURE; } } MOO_STACK_SETRET (moo, nargs, retv); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_method_get_source_line (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_method_t rcv; moo_dbgi_method_t* di; moo_oop_t retv = MOO_SMOOI_TO_OOP(0); rcv = (moo_oop_method_t)MOO_STACK_GETRCV(moo, nargs); MOO_PF_CHECK_RCV (moo, MOO_CLASSOF(moo, rcv) == moo->_method); di = get_dbgi_method(moo, rcv); if (di) { retv = moo_oowtoint(moo, di->start_line); if (!retv) return MOO_PF_FAILURE; } MOO_STACK_SETRET (moo, nargs, retv); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_method_get_source_text (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_method_t rcv; moo_dbgi_method_t* di; moo_oop_t retv = moo->_nil; rcv = (moo_oop_method_t)MOO_STACK_GETRCV(moo, nargs); MOO_PF_CHECK_RCV (moo, MOO_CLASSOF(moo, rcv) == moo->_method); di = get_dbgi_method(moo, rcv); if (di) { if (di->text_len > 0) { const moo_ooch_t* text_ptr; text_ptr = (const moo_ooch_t*)((moo_uint8_t*)(di + 1) + di->text_start); retv = moo_makestring(moo, text_ptr, di->text_len); if (!retv) return MOO_PF_FAILURE; } } MOO_STACK_SETRET (moo, nargs, retv); return MOO_PF_SUCCESS; } /* ------------------------------------------------------------------ */ static moo_pfrc_t __block_value (moo_t* moo, moo_oop_block_t rcv_block, moo_ooi_t nargs, moo_ooi_t num_first_arg_elems, moo_oop_context_t* pblkctx) { /* prepare a new block context for activation. * the receiver must be a block context which becomes the base * for a new block context. */ moo_oop_context_t blkctx; moo_ooi_t local_ntmprs, i; moo_ooi_t actual_arg_count; actual_arg_count = (num_first_arg_elems > 0)? num_first_arg_elems: nargs; /* TODO: find a better way to support a reentrant block context. */ /* | sum | * sum := [ :n | (n < 2) ifTrue: [1] ifFalse: [ n + (sum value: (n - 1))] ]. * (sum value: 10). * * For the code above, sum is a block context and it is sent value: inside * itself. Let me simply clone a block context to allow reentrancy like this * while the block context is active */ /* the receiver must be a block context */ MOO_ASSERT (moo, MOO_CLASSOF(moo, rcv_block) == moo->_block); #if 0 if (rcv_block->receiver_or_base != moo->_nil) { /* the 'source' field is not nil. * this block context has already been activated once. * you can't send 'value' again to reactivate it. * For example, [thisContext value] value. */ MOO_ASSERT (moo, MOO_OBJ_GET_SIZE(rcv_block) > MOO_CONTEXT_NAMED_INSTVARS); moo_seterrbfmt (moo, MOO_EPERM, "re-valuing of a block context - %O", rcv_block); return MOO_PF_FAILURE; } #endif MOO_ASSERT (moo, MOO_OBJ_GET_SIZE(rcv_block) == MOO_BLOCK_NAMED_INSTVARS); if (MOO_OOP_TO_SMOOI(rcv_block->nargs) != actual_arg_count /* nargs */) { moo_seterrbfmt (moo, MOO_ENUMARGS, "wrong number of arguments to a block context %O - %zd expected, %zd given", rcv_block, MOO_OOP_TO_SMOOI(rcv_block->nargs), actual_arg_count); return MOO_PF_FAILURE; } /* the number of temporaries stored in the block context * accumulates the number of temporaries starting from the origin. * simple calculation is needed to find the number of local temporaries */ local_ntmprs = MOO_OOP_TO_SMOOI(rcv_block->ntmprs) - MOO_OOP_TO_SMOOI(((moo_oop_context_t)rcv_block->home)->ntmprs); MOO_ASSERT (moo, local_ntmprs >= actual_arg_count); /* create a new block context based on the receiver block(rcv_block) */ moo_pushvolat (moo, (moo_oop_t*)&rcv_block); blkctx = (moo_oop_context_t)moo_instantiate(moo, moo->_block_context, MOO_NULL, local_ntmprs); moo_popvolat (moo); if (MOO_UNLIKELY(!blkctx)) return MOO_PF_FAILURE; blkctx->ip = rcv_block->ip; /* no MOO_STORE_OOP() as it's a small integer */ blkctx->ntmprs = rcv_block->ntmprs; /* no MOO_STORE_OOP() as it's a small integer */ MOO_STORE_OOP (moo, &blkctx->method_or_nargs, rcv_block->nargs); MOO_STORE_OOP (moo, &blkctx->receiver_or_base, (moo_oop_t)rcv_block); MOO_STORE_OOP (moo, &blkctx->home, (moo_oop_t)rcv_block->home); MOO_STORE_OOP (moo, (moo_oop_t*)&blkctx->origin, (moo_oop_t)rcv_block->home->origin); /* TODO: check the stack size of a block context to see if it's large enough to hold arguments */ if (num_first_arg_elems > 0) { /* the first argument should be an array. this function is ordered * to pass array elements to the new block */ moo_oop_oop_t xarg; MOO_ASSERT (moo, nargs == 1); xarg = (moo_oop_oop_t)MOO_STACK_GETTOP(moo); MOO_ASSERT (moo, MOO_OBJ_IS_OOP_POINTER(xarg)); MOO_ASSERT (moo, MOO_OBJ_GET_SIZE(xarg) == num_first_arg_elems); for (i = 0; i < num_first_arg_elems; i++) { MOO_STORE_OOP (moo, &blkctx->stack[i], MOO_OBJ_GET_OOP_VAL(xarg, i)); } } else { /* copy the arguments to the stack */ for (i = 0; i < nargs; i++) { register moo_oop_t tmp = MOO_STACK_GETARG(moo, nargs, i); MOO_STORE_OOP (moo, &blkctx->stack[i], tmp); } } MOO_STACK_POPS (moo, nargs + 1); /* pop arguments and receiver */ MOO_ASSERT (moo, blkctx->home != moo->_nil); blkctx->sp = MOO_SMOOI_TO_OOP(-1); /* not important at all */ MOO_STORE_OOP (moo, (moo_oop_t*)&blkctx->sender, (moo_oop_t)moo->active_context); *pblkctx = blkctx; return MOO_PF_SUCCESS; } static moo_pfrc_t pf_block_value (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_pfrc_t x; moo_oop_block_t rcv_block; moo_oop_context_t blkctx; rcv_block = (moo_oop_block_t)MOO_STACK_GETRCV(moo, nargs); MOO_PF_CHECK_RCV (moo, MOO_CLASSOF(moo,rcv_block) == moo->_block); x = __block_value(moo, rcv_block, nargs, 0, &blkctx); if (x <= MOO_PF_FAILURE) return x; /* hard failure and soft failure */ SWITCH_ACTIVE_CONTEXT (moo, blkctx); return MOO_PF_SUCCESS; } static MOO_INLINE moo_pfrc_t __block_new_process (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs, int proc_flags) { /* create a new process from a block context. * the receiver must be be a block. * [ 1 + 2 ] newProcess. * [ :a :b | a + b ] newProcess(1, 2) */ int x; moo_oop_block_t rcv_block; moo_oop_context_t blkctx; moo_oop_process_t proc; #if 0 moo_ooi_t num_first_arg_elems = 0; MOO_ASSERT (moo, nargs <= 1); if (nargs == 1) { moo_oop_t xarg; xarg = MOO_STACK_GETARG(moo, nargs, 0); if (!MOO_OBJ_IS_OOP_POINTER(xarg)) { /* the only optional argument must be an OOP-indexable * object like an array */ moo_seterrnum (moo, MOO_EINVAL); return MOO_PF_FAILURE; } num_first_arg_elems = MOO_OBJ_GET_SIZE(xarg); } #endif rcv_block = (moo_oop_block_t)MOO_STACK_GETRCV(moo, nargs); MOO_PF_CHECK_RCV (moo, MOO_CLASSOF(moo,rcv_block) == moo->_block); /* this primitive creates a new process with a block as if the block * is sent the value message */ #if 0 x = __block_value(moo, rcv_block, nargs, num_first_arg_elems, &blkctx); #else x = __block_value(moo, rcv_block, nargs, 0, &blkctx); #endif if (x <= 0) return x; /* both hard failure and soft failure */ /* reset the sender field to moo->_nil because this block context * will be the initial context of a new process. you can simply * inspect the sender field to see if a context is an initial * context of a process. */ blkctx->sender = (moo_oop_context_t)moo->_nil; proc = make_process(moo, blkctx, proc_flags); if (MOO_UNLIKELY(!proc)) return MOO_PF_FAILURE; /* hard failure */ /* TOOD: can't this be treated as a soft failure? throw an exception instead?? */ /* __block_value() has popped all arguments and the receiver. * PUSH the return value instead of changing the stack top */ MOO_STACK_PUSH (moo, (moo_oop_t)proc); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_block_new_process (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { return __block_new_process(moo, mod, nargs, 1); } static moo_pfrc_t pf_block_new_system_process (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { return __block_new_process(moo, mod, nargs, 2); } /* ------------------------------------------------------------------ */ static moo_pfrc_t pf_process_sp (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv; rcv = MOO_STACK_GETRCV(moo, nargs); MOO_PF_CHECK_RCV (moo, MOO_CLASSOF(moo,rcv) == moo->_process); /* commit the SP register of the VM to the active process for accuracy */ STORE_ACTIVE_SP (moo); MOO_STACK_SETRET (moo, nargs, moo->processor->active->sp); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_process_is_innate (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv; rcv = MOO_STACK_GETRCV(moo, nargs); MOO_PF_CHECK_RCV (moo, MOO_CLASSOF(moo,rcv) == moo->_process); MOO_STACK_SETRET (moo, nargs, (MOO_OBJ_GET_FLAGS_PROC(rcv) == MOO_OBJ_FLAGS_PROC_INNATE? moo->_true: moo->_false)); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_process_is_normal (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv; rcv = MOO_STACK_GETRCV(moo, nargs); MOO_PF_CHECK_RCV (moo, MOO_CLASSOF(moo,rcv) == moo->_process); MOO_STACK_SETRET (moo, nargs, (MOO_OBJ_GET_FLAGS_PROC(rcv) == MOO_OBJ_FLAGS_PROC_NORMAL? moo->_true: moo->_false)); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_process_resume (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv; rcv = MOO_STACK_GETRCV(moo, nargs); MOO_PF_CHECK_RCV (moo, MOO_CLASSOF(moo,rcv) == moo->_process); /* set the return value before resume_process() in case it changes * the active process. */ MOO_STACK_SETRETTORCV (moo, nargs); resume_process (moo, (moo_oop_process_t)rcv); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_process_terminate (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv; /* TODO: need to run ensure blocks here.. * when it's executed here. it does't have to be in Exception>>handleException when there is no exception handler */ rcv = MOO_STACK_GETRCV(moo, nargs); MOO_PF_CHECK_RCV (moo, MOO_CLASSOF(moo,rcv) == moo->_process); /* set the return value before terminate_process() in case it changes * the active process. */ MOO_STACK_SETRETTORCV (moo, nargs); terminate_process (moo, (moo_oop_process_t)rcv); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_process_yield (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv; rcv = MOO_STACK_GETRCV(moo, nargs); MOO_PF_CHECK_RCV (moo, MOO_CLASSOF(moo,rcv) == moo->_process); /* set the return value before yield_process() in case it changes * the active process. */ MOO_STACK_SETRETTORCV (moo, nargs); yield_process (moo, (moo_oop_process_t)rcv); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_process_suspend (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv; rcv = MOO_STACK_GETRCV(moo, nargs); MOO_PF_CHECK_RCV (moo, MOO_CLASSOF(moo,rcv) == moo->_process); /* set the return value before suspend_process() in case it changes * the active process. */ MOO_STACK_SETRETTORCV (moo, nargs); suspend_process (moo, (moo_oop_process_t)rcv); return MOO_PF_SUCCESS; } /* ------------------------------------------------------------------ */ static moo_pfrc_t pf_semaphore_signal (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_semaphore_t sem; moo_oop_t sec, nsec; moo_ntime_t now, ft; sem = (moo_oop_semaphore_t)MOO_STACK_GETRCV(moo, nargs); MOO_PF_CHECK_RCV (moo, moo_iskindof(moo, (moo_oop_t)sem, moo->_semaphore)); if (nargs <= 0) { /* signal_semaphore() may change the active process though the * implementation as of this writing makes runnable the process waiting * on the signal to be processed. it is safer to set the return value * before calling signal_sempahore() */ MOO_STACK_SETRETTORCV (moo, nargs); signal_semaphore (moo, sem); return MOO_PF_SUCCESS; } sec = MOO_STACK_GETARG(moo, nargs, 0); nsec = (nargs == 2? MOO_STACK_GETARG(moo, nargs, 1): MOO_SMOOI_TO_OOP(0)); if (!MOO_OOP_IS_SMOOI(sec)) { moo_seterrbfmt (moo, MOO_EINVAL, "invalid second - %O", sec); return MOO_PF_FAILURE; } if (!MOO_OOP_IS_SMOOI(sec)) { moo_seterrbfmt (moo, MOO_EINVAL, "invalid nanosecond - %O", nsec); return MOO_PF_FAILURE; } #if 0 if (sem->subtype == MOO_SMOOI_TO_OOP(MOO_SEMAPHORE_SUBTYPE_TIMED)) { MOO_ASSERT (moo, MOO_OOP_IS_SMOOI(sem->u.timed.index) && MOO_OOP_TO_SMOOI(sem->u.timed.index) >= 0); /* if the semaphore is already been added. remove it first */ delete_from_sem_heap (moo, MOO_OOP_TO_SMOOI(sem->u.timed.index)); MOO_ASSERT (moo, sem->subtype == moo->_nil && sem->u.timed.index == moo->_nil); /* Is this more desired??? MOO_STACK_SETRET (moo, nargs, moo->_false); return MOO_PF_SUCCESS; */ } #else if (sem->subtype != moo->_nil) { if (sem->subtype == MOO_SMOOI_TO_OOP(MOO_SEMAPHORE_SUBTYPE_IO)) { MOO_ASSERT (moo, MOO_OOP_IS_SMOOI(sem->u.io.index) && MOO_OOP_TO_SMOOI(sem->u.io.index) >= 0); MOO_ASSERT (moo, MOO_OOP_IS_SMOOI(sem->u.io.handle) && MOO_OOP_TO_SMOOI(sem->u.io.handle) >= 0); MOO_ASSERT (moo, MOO_OOP_IS_SMOOI(sem->u.io.type)); moo_seterrbfmt (moo, MOO_EINVAL, "semaphore already linked with a handle %zd", MOO_OOP_TO_SMOOI(sem->u.io.handle)); } else { MOO_ASSERT (moo, sem->subtype == MOO_SMOOI_TO_OOP(MOO_SEMAPHORE_SUBTYPE_TIMED)); MOO_ASSERT (moo, MOO_OOP_IS_SMOOI(sem->u.timed.index) && MOO_OOP_TO_SMOOI(sem->u.timed.index) >= 0); MOO_ASSERT (moo, MOO_OOP_IS_SMOOI(sem->u.timed.ftime_sec)); MOO_ASSERT (moo, MOO_OOP_IS_SMOOI(sem->u.timed.ftime_nsec)); moo_seterrbfmt (moo, MOO_EINVAL, "semaphore already activated for timer"); } return MOO_PF_FAILURE; } #endif /* this code assumes that the monotonic clock returns a small value * that can fit into a SmallInteger, even after some additions. */ vm_gettime (moo, &now); MOO_ADD_NTIME_SNS (&ft, &now, MOO_OOP_TO_SMOOI(sec), MOO_OOP_TO_SMOOI(nsec)); if (ft.sec < 0 || ft.sec > MOO_SMOOI_MAX) { /* soft error - cannot represent the expiry time in a small integer. */ MOO_LOG3 (moo, MOO_LOG_PRIMITIVE | MOO_LOG_ERROR, "Error(%hs) - time (%ld) out of range(0 - %zd) when adding a timed semaphore\n", __PRIMITIVE_NAME__, (unsigned long int)ft.sec, (moo_ooi_t)MOO_SMOOI_MAX); moo_seterrnum (moo, MOO_ERANGE); return MOO_PF_FAILURE; } sem->u.timed.ftime_sec = MOO_SMOOI_TO_OOP(ft.sec); sem->u.timed.ftime_nsec = MOO_SMOOI_TO_OOP(ft.nsec); if (add_to_sem_heap(moo, sem) <= -1) return MOO_PF_HARD_FAILURE; MOO_ASSERT (moo, sem->subtype == MOO_SMOOI_TO_OOP(MOO_SEMAPHORE_SUBTYPE_TIMED)); MOO_STACK_SETRETTORCV (moo, nargs); /* ^self */ return MOO_PF_SUCCESS; } static moo_pfrc_t pf_semaphore_signal_on_gcfin (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_semaphore_t sem; sem = (moo_oop_semaphore_t)MOO_STACK_GETRCV(moo, nargs); MOO_PF_CHECK_RCV (moo, moo_iskindof(moo, (moo_oop_t)sem, moo->_semaphore)); /* TODO: should i prevent overwriting? */ moo->sem_gcfin = sem; MOO_STACK_SETRETTORCV (moo, nargs); /* ^self */ return MOO_PF_SUCCESS; } static moo_pfrc_t __semaphore_signal_on_io (moo_t* moo, moo_ooi_t nargs, moo_semaphore_io_type_t io_type) { moo_oop_semaphore_t sem; moo_oop_t fd; sem = (moo_oop_semaphore_t)MOO_STACK_GETRCV(moo, nargs); MOO_PF_CHECK_RCV (moo, moo_iskindof(moo, (moo_oop_t)sem, moo->_semaphore)); fd = MOO_STACK_GETARG(moo, nargs, 0); if (!MOO_OOP_IS_SMOOI(fd)) { moo_seterrbfmt (moo, MOO_EINVAL, "handle not a small integer - %O", fd); return MOO_PF_FAILURE; } if (sem->subtype != moo->_nil) { if (sem->subtype == MOO_SMOOI_TO_OOP(MOO_SEMAPHORE_SUBTYPE_IO)) { MOO_ASSERT (moo, MOO_OOP_IS_SMOOI(sem->u.io.index) && MOO_OOP_TO_SMOOI(sem->u.io.index) >= 0); MOO_ASSERT (moo, MOO_OOP_IS_SMOOI(sem->u.io.handle) && MOO_OOP_TO_SMOOI(sem->u.io.handle) >= 0); MOO_ASSERT (moo, MOO_OOP_IS_SMOOI(sem->u.io.type)); moo_seterrbfmt (moo, MOO_EINVAL, "semaphore already linked with a handle %zd", MOO_OOP_TO_SMOOI(sem->u.io.handle)); } else { MOO_ASSERT (moo, sem->subtype == MOO_SMOOI_TO_OOP(MOO_SEMAPHORE_SUBTYPE_TIMED)); MOO_ASSERT (moo, MOO_OOP_IS_SMOOI(sem->u.timed.index) && MOO_OOP_TO_SMOOI(sem->u.timed.index) >= 0); MOO_ASSERT (moo, MOO_OOP_IS_SMOOI(sem->u.timed.ftime_sec)); MOO_ASSERT (moo, MOO_OOP_IS_SMOOI(sem->u.timed.ftime_nsec)); moo_seterrbfmt (moo, MOO_EINVAL, "semaphore already activated for timer"); } return MOO_PF_FAILURE; } if (add_sem_to_sem_io_tuple(moo, sem, MOO_OOP_TO_SMOOI(fd), io_type) <= -1) { const moo_ooch_t* oldmsg = moo_backuperrmsg(moo); moo_seterrbfmt (moo, moo->errnum, "unable to add the handle %zd to the multiplexer for %hs - %js", MOO_OOP_TO_SMOOI(fd), io_type_str[io_type], oldmsg); return MOO_PF_FAILURE; } MOO_STACK_SETRETTORCV (moo, nargs); /* ^self */ return MOO_PF_SUCCESS; } static moo_pfrc_t pf_semaphore_signal_on_input (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { return __semaphore_signal_on_io(moo, nargs, MOO_SEMAPHORE_IO_TYPE_INPUT); } static moo_pfrc_t pf_semaphore_signal_on_output (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { return __semaphore_signal_on_io(moo, nargs, MOO_SEMAPHORE_IO_TYPE_OUTPUT); } static moo_pfrc_t pf_semaphore_wait (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv; rcv = MOO_STACK_GETRCV(moo, nargs); MOO_PF_CHECK_RCV (moo, moo_iskindof(moo, rcv, moo->_semaphore)); if (!can_await_semaphore(moo, (moo_oop_semaphore_t)rcv)) { moo_seterrbfmt (moo, MOO_EPERM, "not allowed to wait on a semaphore that belongs to a semaphore group"); return MOO_PF_FAILURE; } /* i must set the return value before calling await_semaphore(). * await_semaphore() may switch the active process and the stack * manipulation macros target at the active process. i'm not supposed * to change the return value of a new active process. */ MOO_STACK_SETRETTORCV (moo, nargs); await_semaphore (moo, (moo_oop_semaphore_t)rcv); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_semaphore_unsignal (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { /* remove a semaphore from processor's signal scheduling. * it takes no effect on a plain semaphore. */ moo_oop_semaphore_t sem; sem = (moo_oop_semaphore_t)MOO_STACK_GETRCV(moo, nargs); MOO_PF_CHECK_RCV (moo, moo_iskindof(moo, (moo_oop_t)sem, moo->_semaphore)); if (sem == moo->sem_gcfin) { moo->sem_gcfin = (moo_oop_semaphore_t)moo->_nil; } if (sem->subtype == MOO_SMOOI_TO_OOP(MOO_SEMAPHORE_SUBTYPE_TIMED)) { /* the semaphore is in the timed semaphore heap */ MOO_ASSERT (moo, MOO_OOP_IS_SMOOI(sem->u.timed.index) && MOO_OOP_TO_SMOOI(sem->u.timed.index) >= 0); delete_from_sem_heap (moo, MOO_OOP_TO_SMOOI(sem->u.timed.index)); MOO_ASSERT (moo, sem->u.timed.index == moo->_nil); } else if (sem->subtype == MOO_SMOOI_TO_OOP(MOO_SEMAPHORE_SUBTYPE_IO)) { moo_oop_process_t wp; /* waiting process */ /* the semaphore is associated with IO */ MOO_ASSERT (moo, MOO_OOP_IS_SMOOI(sem->u.io.index) && MOO_OOP_TO_SMOOI(sem->u.io.index) >= 0); MOO_ASSERT (moo, MOO_OOP_IS_SMOOI(sem->u.io.type)); MOO_ASSERT (moo, MOO_OOP_IS_SMOOI(sem->u.io.handle) && MOO_OOP_TO_SMOOI(sem->u.io.handle) >= 0); if (delete_sem_from_sem_io_tuple(moo, sem, 0) <= -1) { const moo_ooch_t* oldmsg = moo_backuperrmsg(moo); moo_seterrbfmt (moo, moo->errnum, "cannot delete the handle %zd from the multiplexer - %js", MOO_OOP_TO_SMOOI(sem->u.io.handle), oldmsg); return MOO_PF_FAILURE; } MOO_ASSERT (moo, (moo_oop_t)sem->u.io.index == moo->_nil); MOO_ASSERT (moo, (moo_oop_t)sem->u.io.handle == moo->_nil); /* the semaphore gets changed to a plain semaphore after * delete_sem_from_sem_io_tuple(). if there is a process * waiting on this IO semaphore, the process now is treated * as if it's waiting on a plain semaphore. let's adjust * the number of processes waiting on IO semaphores */ for (wp = sem->waiting.first; (moo_oop_t)wp != moo->_nil; wp = wp->sem_wait.next) { MOO_ASSERT (moo, moo->sem_io_wait_count > 0); moo->sem_io_wait_count--; } } MOO_ASSERT (moo, sem->subtype == moo->_nil); MOO_STACK_SETRETTORCV (moo, nargs); /* ^self */ return MOO_PF_SUCCESS; } /* ------------------------------------------------------------------ */ static moo_pfrc_t pf_semaphore_group_add_semaphore (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_semaphore_group_t sg; moo_oop_semaphore_t sem; sg = (moo_oop_semaphore_group_t)MOO_STACK_GETRCV(moo, nargs); MOO_PF_CHECK_RCV (moo, moo_iskindof(moo, (moo_oop_t)sg, moo->_semaphore_group)); sem = (moo_oop_semaphore_t)MOO_STACK_GETARG(moo, nargs, 0); MOO_PF_CHECK_ARGS (moo, nargs, moo_iskindof(moo, (moo_oop_t)sem, moo->_semaphore)); if ((moo_oop_t)sem->group == moo->_nil) { /* the semaphore doesn't belong to a group */ moo_ooi_t count; int sems_idx; sems_idx = MOO_OOP_TO_SMOOI(sem->count) > 0? MOO_SEMAPHORE_GROUP_SEMS_SIG: MOO_SEMAPHORE_GROUP_SEMS_UNSIG; MOO_APPEND_TO_OOP_LIST (moo, &sg->sems[sems_idx], moo_oop_semaphore_t, sem, grm); MOO_STORE_OOP (moo, (moo_oop_t*)&sem->group, (moo_oop_t)sg); count = MOO_OOP_TO_SMOOI(sg->sem_count); MOO_ASSERT (moo, count >= 0); count++; sg->sem_count = MOO_SMOOI_TO_OOP(count); if (sem->subtype == MOO_SMOOI_TO_OOP(MOO_SEMAPHORE_SUBTYPE_IO)) { /* the semaphore being added is associated with I/O operation. */ MOO_ASSERT (moo, MOO_OOP_IS_SMOOI(sem->u.io.index) && MOO_OOP_TO_SMOOI(sem->u.io.index) >= 0 && MOO_OOP_TO_SMOOI(sem->u.io.index) < moo->sem_io_tuple_count); count = MOO_OOP_TO_SMOOI(sg->sem_io_count); MOO_ASSERT (moo, count >= 0); count++; sg->sem_io_count = MOO_SMOOI_TO_OOP(count); if (count == 1) { /* the first IO semaphore is being added to the semaphore group. * but there are already processes waiting on the semaphore group. * * for instance, * [Process 1] * sg := SemaphoreGroup new. * sg wait. * [Process 2] * sg addSemaphore: (Semaphore new). */ moo_oop_process_t wp; /* TODO: add sem_wait_count to process. no traversal... */ for (wp = sg->waiting.first; (moo_oop_t)wp != moo->_nil; wp = wp->sem_wait.next) { moo->sem_io_wait_count++; MOO_DEBUG1 (moo, "pf_semaphore_group_add_semaphore - raised sem_io_wait_count to %zu\n", moo->sem_io_wait_count); } } } MOO_STACK_SETRETTORCV (moo, nargs); } else if (sem->group == sg) { /* do nothing. don't change the group of the semaphore */ MOO_STACK_SETRETTORCV (moo, nargs); } else { /* the semaphore belongs to a group already */ /* TODO: is it better to move this semaphore to the new group? */ moo_seterrbfmt (moo, MOO_EPERM, "not allowed to relocate a semaphore to a different group"); return MOO_PF_FAILURE; } return MOO_PF_SUCCESS; } static moo_pfrc_t pf_semaphore_group_remove_semaphore (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_semaphore_group_t rcv; moo_oop_semaphore_t sem; moo_ooi_t count; rcv = (moo_oop_semaphore_group_t)MOO_STACK_GETRCV(moo, nargs); MOO_PF_CHECK_RCV (moo, moo_iskindof(moo, (moo_oop_t)rcv, moo->_semaphore_group)); sem = (moo_oop_semaphore_t)MOO_STACK_GETARG(moo, nargs, 0); MOO_PF_CHECK_ARGS (moo, nargs, moo_iskindof(moo, (moo_oop_t)sem, moo->_semaphore)); if (sem->group == rcv) { int sems_idx; #if 0 if ((moo_oop_t)rcv->waiting.first != moo->_nil) { /* there is a process waiting on this semaphore group. * i don't allow a semaphore to be removed from the group. * i want to dodge potential problems arising when removal is allowed. * * for instance, consider this psuedo code. * sg addSemaphore: s * [ sg wait ] fork. * [ sg wait ] fork. * [ sg wait ] fork. * sg removeSemaphore: s. * */ moo_seterrbfmt (moo, MOO_EPERM, "not allowed to remove a semaphore from a group being waited on"); return MOO_PF_FAILURE; } #endif sems_idx = MOO_OOP_TO_SMOOI(sem->count) > 0? MOO_SEMAPHORE_GROUP_SEMS_SIG: MOO_SEMAPHORE_GROUP_SEMS_UNSIG; MOO_DELETE_FROM_OOP_LIST (moo, &rcv->sems[sems_idx], sem, grm); sem->grm.prev = (moo_oop_semaphore_t)moo->_nil; sem->grm.next = (moo_oop_semaphore_t)moo->_nil; sem->group = (moo_oop_semaphore_group_t)moo->_nil; count = MOO_OOP_TO_SMOOI(rcv->sem_count); MOO_ASSERT (moo, count > 0); count--; rcv->sem_count = MOO_SMOOI_TO_OOP(count); if (sem->subtype == MOO_SMOOI_TO_OOP(MOO_SEMAPHORE_SUBTYPE_IO)) { MOO_ASSERT (moo, MOO_OOP_IS_SMOOI(sem->u.io.index) && MOO_OOP_TO_SMOOI(sem->u.io.index) >= 0 && MOO_OOP_TO_SMOOI(sem->u.io.index) < moo->sem_io_tuple_count); count = MOO_OOP_TO_SMOOI(rcv->sem_io_count); MOO_ASSERT (moo, count > 0); count--; rcv->sem_io_count = MOO_SMOOI_TO_OOP(count); if (count == 0) { moo_oop_process_t wp; /* TODO: add sem_wait_count to process. no traversal... */ for (wp = rcv->waiting.first; (moo_oop_t)wp != moo->_nil; wp = wp->sem_wait.next) { MOO_ASSERT (moo, moo->sem_io_wait_count > 0); moo->sem_io_wait_count--; MOO_DEBUG1 (moo, "pf_semaphore_group_remove_semaphore - lowered sem_io_wait_count to %zu\n", moo->sem_io_wait_count); } } } MOO_STACK_SETRETTORCV (moo, nargs); return MOO_PF_SUCCESS; } /* it doesn't belong to a group or belongs to a different group */ moo_seterrbfmt (moo, MOO_EPERM, "not allowed to remove a semaphore from a non-owning group"); return MOO_PF_FAILURE; } static moo_pfrc_t pf_semaphore_group_wait (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, sem; rcv = MOO_STACK_GETRCV(moo, nargs); MOO_PF_CHECK_RCV (moo, moo_iskindof(moo, rcv, moo->_semaphore_group)); /* i must set the return value before calling await_semaphore_group(). * MOO_STACK_SETRETTORCV() manipulates the stack of the currently active * process(moo->processor->active). moo->processor->active may become * moo->nil_process if the current active process must get suspended. * it is safer to set the return value of the calling method here. * but the arguments and the receiver information will be lost from * the stack from this moment on. */ MOO_STACK_SETRETTORCV (moo, nargs); sem = await_semaphore_group(moo, (moo_oop_semaphore_group_t)rcv); if (sem != moo->_nil) { /* there was a signaled semaphore. the active process won't get * suspended. change the return value of the current process * forcibly to the signaled semaphore */ MOO_STACK_SETTOP (moo, sem); } /* the return value will get changed to an actual semaphore signaled * when the semaphore is signaled. see signal_semaphore() */ return MOO_PF_SUCCESS; } /* ------------------------------------------------------------------ */ static moo_pfrc_t pf_system_return_value_to_context (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t ret, ctx; /*MOO_PF_CHECK_RCV (moo, MOO_STACK_GETRCV(moo, nargs) == (moo_oop_t)moo->processor);*/ MOO_ASSERT (moo, nargs == 2); ret = MOO_STACK_GETARG(moo, nargs, 0); ctx = MOO_STACK_GETARG(moo, nargs, 1); MOO_PF_CHECK_ARGS (moo, nargs, MOO_CLASSOF(moo, ctx) == moo->_block_context || MOO_CLASSOF(moo, ctx) == moo->_method_context); MOO_STACK_POPS (moo, nargs + 1); /* pop arguments and receiver */ /* TODO: verify if this is correct? does't it correctly restore the stack pointer? * test complex chains of method contexts and block contexts */ if (MOO_CLASSOF(moo, ctx) == moo->_method_context) { /* when returning to a method context, load the sp register with * the value stored in the context */ moo->sp = MOO_OOP_TO_SMOOI(((moo_oop_context_t)ctx)->sp); } MOO_STACK_PUSH (moo, ret); SWITCH_ACTIVE_CONTEXT (moo, (moo_oop_context_t)ctx); return MOO_PF_SUCCESS; } /* ------------------------------------------------------------------ */ static moo_pfrc_t pf_number_scale (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, res; rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); if (!MOO_OOP_IS_SMOOI(arg)) { moo_seterrbfmt (moo, MOO_EINVAL, "invalid scale - %O", arg); return MOO_PF_FAILURE; } res = moo_truncfpdec(moo, rcv, MOO_OOP_TO_SMOOI(arg)); if (!res) return (moo->errnum == MOO_EINVAL? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } /* ------------------------------------------------------------------ */ static moo_pfrc_t pf_number_add (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, res; MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); res = moo_addnums(moo, rcv, arg); if (!res) return (moo->errnum == MOO_EINVAL? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_number_sub (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, res; MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); res = moo_subnums(moo, rcv, arg); if (!res) return (moo->errnum == MOO_EINVAL? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_number_mul (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, res; MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); res = moo_mulnums(moo, rcv, arg); if (!res) return (moo->errnum == MOO_EINVAL? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_number_mlt (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, res; MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); res = moo_mltnums(moo, rcv, arg); if (!res) return (moo->errnum == MOO_EINVAL? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_number_div (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, res; MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); res = moo_divnums(moo, rcv, arg, 0); if (!res) return (moo->errnum == MOO_EINVAL || moo->errnum == MOO_EDIVBY0? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_number_mdiv (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, res; MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); res = moo_divnums(moo, rcv, arg, 1); if (!res) return (moo->errnum == MOO_EINVAL || moo->errnum == MOO_EDIVBY0? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_number_negated (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, res; MOO_ASSERT (moo, nargs == 0); rcv = MOO_STACK_GETRCV(moo, nargs); res = moo_negatenum(moo, rcv); if (!res) return (moo->errnum == MOO_EINVAL? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_number_eq (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, res; MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); res = moo_eqnums(moo, rcv, arg); if (!res) return (moo->errnum == MOO_EINVAL? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_number_ne (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, res; MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); res = moo_nenums(moo, rcv, arg); if (!res) return (moo->errnum == MOO_EINVAL? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_number_lt (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, res; MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); res = moo_ltnums(moo, rcv, arg); if (!res) return (moo->errnum == MOO_EINVAL? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_number_gt (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, res; MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); res = moo_gtnums(moo, rcv, arg); if (!res) return (moo->errnum == MOO_EINVAL? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_number_le (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, res; MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); res = moo_lenums(moo, rcv, arg); if (!res) return (moo->errnum == MOO_EINVAL? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_number_ge (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, res; MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); res = moo_genums(moo, rcv, arg); if (!res) return (moo->errnum == MOO_EINVAL? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_number_numtostr (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, str; moo_ooi_t radix; MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); if (!MOO_OOP_IS_SMOOI(arg)) return MOO_PF_FAILURE; radix = MOO_OOP_TO_SMOOI(arg); if (radix < 2 || radix > 36) return MOO_PF_FAILURE; str = moo_numtostr(moo, rcv, radix); if (!str) return (moo->errnum == MOO_EINVAL? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); MOO_STACK_SETRET (moo, nargs, str); return MOO_PF_SUCCESS; } /* ------------------------------------------------------------------ */ static moo_pfrc_t pf_integer_add (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, res; MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); res = moo_addints(moo, rcv, arg); if (!res) return (moo->errnum == MOO_EINVAL? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_integer_sub (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, res; MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); res = moo_subints(moo, rcv, arg); if (!res) return (moo->errnum == MOO_EINVAL? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_integer_mul (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, res; MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); res = moo_mulints(moo, rcv, arg); if (!res) return (moo->errnum == MOO_EINVAL? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_integer_div (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, quo; MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); quo = moo_divints(moo, rcv, arg, 0, MOO_NULL); if (!quo) return (moo->errnum == MOO_EINVAL || moo->errnum == MOO_EDIVBY0? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); MOO_STACK_SETRET (moo, nargs, quo); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_integer_rem (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, quo, rem; MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); quo = moo_divints(moo, rcv, arg, 0, &rem); if (!quo) return (moo->errnum == MOO_EINVAL || moo->errnum == MOO_EDIVBY0? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); MOO_STACK_SETRET (moo, nargs, rem); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_integer_mdiv (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, quo; MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); quo = moo_divints(moo, rcv, arg, 1, MOO_NULL); if (!quo) return (moo->errnum == MOO_EINVAL || moo->errnum == MOO_EDIVBY0? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); MOO_STACK_SETRET (moo, nargs, quo); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_integer_mod (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, quo, rem; MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); quo = moo_divints(moo, rcv, arg, 1, &rem); if (!quo) return (moo->errnum == MOO_EINVAL || moo->errnum == MOO_EDIVBY0? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); MOO_STACK_SETRET (moo, nargs, rem); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_integer_negated (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, res; MOO_ASSERT (moo, nargs == 0); rcv = MOO_STACK_GETRCV(moo, nargs); res = moo_negateint(moo, rcv); if (!res) return (moo->errnum == MOO_EINVAL? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_integer_bitat (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, res; MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); res = moo_bitatint (moo, rcv, arg); if (!res) return (moo->errnum == MOO_EINVAL? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_integer_bitand (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, res; moo_oow_t i; MOO_ASSERT (moo, nargs >= 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); res = moo_bitandints(moo, rcv, arg); if (!res) return (moo->errnum == MOO_EINVAL? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); for (i = 1; i < nargs; i++) { arg = MOO_STACK_GETARG(moo, nargs, i); res = moo_bitandints(moo, res, arg); if (!res) return (moo->errnum == MOO_EINVAL? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); } MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_integer_bitor (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, res; moo_oow_t i; MOO_ASSERT (moo, nargs >= 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); res = moo_bitorints(moo, rcv, arg); if (!res) return (moo->errnum == MOO_EINVAL? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); for (i = 1; i < nargs; i++) { arg = MOO_STACK_GETARG(moo, nargs, i); res = moo_bitorints(moo, res, arg); if (!res) return (moo->errnum == MOO_EINVAL? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); } MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_integer_bitxor (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, res; moo_oow_t i; MOO_ASSERT (moo, nargs >= 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); res = moo_bitxorints(moo, rcv, arg); if (!res) return (moo->errnum == MOO_EINVAL? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); for (i = 1; i < nargs; i++) { arg = MOO_STACK_GETARG(moo, nargs, i); res = moo_bitxorints(moo, res, arg); if (!res) return (moo->errnum == MOO_EINVAL? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); } MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_integer_bitinv (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, res; MOO_ASSERT (moo, nargs == 0); rcv = MOO_STACK_GETRCV(moo, nargs); res = moo_bitinvint(moo, rcv); if (!res) return (moo->errnum == MOO_EINVAL? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_integer_bitshift (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, res; MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); res = moo_bitshiftint(moo, rcv, arg); if (!res) return (moo->errnum == MOO_EINVAL? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_integer_eq (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, res; MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); res = moo_eqints(moo, rcv, arg); if (!res) return (moo->errnum == MOO_EINVAL? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_integer_ne (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, res; MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); res = moo_neints(moo, rcv, arg); if (!res) return (moo->errnum == MOO_EINVAL? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_integer_lt (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, res; MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); res = moo_ltints(moo, rcv, arg); if (!res) return (moo->errnum == MOO_EINVAL? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_integer_gt (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, res; MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); res = moo_gtints(moo, rcv, arg); if (!res) return (moo->errnum == MOO_EINVAL? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_integer_le (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, res; MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); res = moo_leints(moo, rcv, arg); if (!res) return (moo->errnum == MOO_EINVAL? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_integer_ge (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, res; MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); res = moo_geints(moo, rcv, arg); if (!res) return (moo->errnum == MOO_EINVAL? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_integer_inttostr (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, str; moo_ooi_t radix; MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); if (!MOO_OOP_IS_SMOOI(arg)) return MOO_PF_FAILURE; radix = MOO_OOP_TO_SMOOI(arg); if (radix < 2 || radix > 36) return MOO_PF_FAILURE; str = moo_inttostr(moo, rcv, radix); if (!str) return (moo->errnum == MOO_EINVAL? MOO_PF_FAILURE: MOO_PF_HARD_FAILURE); MOO_STACK_SETRET (moo, nargs, str); return MOO_PF_SUCCESS; } /* ------------------------------------------------------------------ */ static moo_pfrc_t pf_character_eq (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, res; rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); MOO_PF_CHECK_RCV (moo, MOO_OOP_IS_CHAR(rcv)); /*res = (MOO_OOP_IS_CHAR(arg) && MOO_OOP_TO_CHAR(rcv) == MOO_OOP_TO_CHAR(arg))? moo->_true: moo->_false;*/ res = (rcv == arg)? moo->_true: moo->_false; /* this comparision should be sufficient for characters */ MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_character_ne (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, res; rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); MOO_PF_CHECK_RCV (moo, MOO_OOP_IS_CHAR(rcv)); /*res = (MOO_OOP_IS_CHAR(arg) && MOO_OOP_TO_CHAR(rcv) == MOO_OOP_TO_CHAR(arg))? moo->_false: moo->_true;*/ res = (rcv == arg)? moo->_false: moo->_true; /* this comparision should be sufficient for characters */ MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_character_lt (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, res; MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); MOO_PF_CHECK_RCV (moo, MOO_OOP_IS_CHAR(rcv)); MOO_PF_CHECK_ARGS (moo, nargs, MOO_OOP_IS_CHAR(arg)); res = (MOO_OOP_TO_CHAR(rcv) < MOO_OOP_TO_CHAR(arg))? moo->_true: moo->_false; MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_character_gt (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, res; MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); MOO_PF_CHECK_RCV (moo, MOO_OOP_IS_CHAR(rcv)); MOO_PF_CHECK_ARGS (moo, nargs, MOO_OOP_IS_CHAR(arg)); res = (MOO_OOP_TO_CHAR(rcv) > MOO_OOP_TO_CHAR(arg))? moo->_true: moo->_false; MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_character_le (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, res; MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); MOO_PF_CHECK_RCV (moo, MOO_OOP_IS_CHAR(rcv)); MOO_PF_CHECK_ARGS (moo, nargs, MOO_OOP_IS_CHAR(arg)); res = (MOO_OOP_TO_CHAR(rcv) <= MOO_OOP_TO_CHAR(arg))? moo->_true: moo->_false; MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_character_ge (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, arg, res; MOO_ASSERT (moo, nargs == 1); rcv = MOO_STACK_GETRCV(moo, nargs); arg = MOO_STACK_GETARG(moo, nargs, 0); MOO_PF_CHECK_RCV (moo, MOO_OOP_IS_CHAR(rcv)); MOO_PF_CHECK_ARGS (moo, nargs, MOO_OOP_IS_CHAR(arg)); res = (MOO_OOP_TO_CHAR(rcv) >= MOO_OOP_TO_CHAR(arg))? moo->_true: moo->_false; MOO_STACK_SETRET (moo, nargs, res); return MOO_PF_SUCCESS; } /* ------------------------------------------------------------------ */ static moo_pfrc_t pf_character_as_error (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv; moo_ooi_t c; MOO_ASSERT (moo, nargs == 0); rcv = MOO_STACK_GETRCV(moo, nargs); MOO_PF_CHECK_RCV (moo, MOO_OOP_IS_CHAR(rcv)); c = MOO_OOP_TO_CHAR(rcv); MOO_STACK_SETRET (moo, nargs, MOO_ERROR_TO_OOP(c)); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_character_as_smooi (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv; moo_ooi_t c; MOO_ASSERT (moo, nargs == 0); rcv = MOO_STACK_GETRCV(moo, nargs); MOO_PF_CHECK_RCV (moo, MOO_OOP_IS_CHAR(rcv)); c = MOO_OOP_TO_CHAR(rcv); MOO_STACK_SETRET (moo, nargs, MOO_SMOOI_TO_OOP(c)); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_smooi_as_character (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv; moo_ooi_t ec; MOO_ASSERT (moo, nargs == 0); rcv = MOO_STACK_GETRCV(moo, nargs); MOO_PF_CHECK_RCV (moo, MOO_OOP_IS_SMOOI(rcv)); ec = MOO_OOP_TO_SMOOI(rcv); if (ec < 0) ec = 0; MOO_STACK_SETRET (moo, nargs, MOO_CHAR_TO_OOP(ec)); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_smooi_as_error (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv; moo_ooi_t ec; MOO_ASSERT (moo, nargs == 0); rcv = MOO_STACK_GETRCV(moo, nargs); MOO_PF_CHECK_RCV (moo, MOO_OOP_IS_SMOOI(rcv)); ec = MOO_OOP_TO_SMOOI(rcv); if (ec < MOO_ERROR_MIN) ec = MOO_ERROR_MIN; else if (ec > MOO_ERROR_MAX) ec = MOO_ERROR_MAX; MOO_STACK_SETRET (moo, nargs, MOO_ERROR_TO_OOP(ec)); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_error_as_character (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv; moo_ooi_t ec; MOO_ASSERT (moo, nargs == 0); rcv = MOO_STACK_GETRCV(moo, nargs); MOO_PF_CHECK_RCV (moo, MOO_OOP_IS_ERROR(rcv)); ec = MOO_OOP_TO_ERROR(rcv); MOO_ASSERT (moo, ec >= MOO_CHAR_MIN && ec <= MOO_CHAR_MAX); MOO_STACK_SETRET (moo, nargs, MOO_CHAR_TO_OOP(ec)); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_error_as_integer (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv; moo_ooi_t ec; MOO_ASSERT (moo, nargs == 0); rcv = MOO_STACK_GETRCV(moo, nargs); MOO_PF_CHECK_RCV (moo, MOO_OOP_IS_ERROR(rcv)); ec = MOO_OOP_TO_ERROR(rcv); MOO_ASSERT (moo, MOO_IN_SMOOI_RANGE(ec)); MOO_STACK_SETRET (moo, nargs, MOO_SMOOI_TO_OOP(ec)); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_error_as_string (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, ss; moo_ooi_t ec; const moo_ooch_t* s; MOO_ASSERT (moo, nargs == 0); rcv = MOO_STACK_GETRCV(moo, nargs); MOO_PF_CHECK_RCV (moo, MOO_OOP_IS_ERROR(rcv)); ec = MOO_OOP_TO_ERROR(rcv); MOO_ASSERT (moo, MOO_IN_SMOOI_RANGE(ec)); /* TODO: error string will be mostly the same.. do i really have to call makestring every time? * cache the error string object created? */ s = moo_errnum_to_errstr(ec); ss = moo_makestring (moo, s, moo_count_oocstr(s)); if (!ss) return MOO_PF_FAILURE; MOO_STACK_SETRET (moo, nargs, ss); return MOO_PF_SUCCESS; } static moo_pfrc_t pf_string_format (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { /* ignore the receiver. the first argument is the format string. */ /*moo_oop_t rcv; rcv = MOO_STACK_GETRCV(moo, nargs); MOO_PF_CHECK_RCV (moo, MOO_OBJ_IS_CHAR_POINTER(rcv));*/ if (moo_strfmtcallstack(moo, nargs, 0) <= -1) { MOO_STACK_SETRETTOERRNUM (moo, nargs); } else { moo_oop_t str; str = moo_makestring(moo, moo->sprintf.xbuf.ptr, moo->sprintf.xbuf.len); if (!str) return MOO_PF_FAILURE; MOO_STACK_SETRET (moo, nargs, str); } return MOO_PF_SUCCESS; } static moo_pfrc_t pf_strfmt (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv; rcv = MOO_STACK_GETRCV(moo, nargs); MOO_PF_CHECK_RCV (moo, MOO_OBJ_IS_CHAR_POINTER(rcv)); if (moo_strfmtcallstack(moo, nargs, 1) <= -1) { MOO_STACK_SETRETTOERRNUM (moo, nargs); } else { moo_oop_t str; str = moo_makestring(moo, moo->sprintf.xbuf.ptr, moo->sprintf.xbuf.len); if (!str) return MOO_PF_FAILURE; MOO_STACK_SETRET (moo, nargs, str); } return MOO_PF_SUCCESS; } static moo_pfrc_t pf_strlen (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t rcv, retv; moo_oow_t i, limit; moo_ooch_t* ptr; rcv = MOO_STACK_GETRCV(moo, nargs); MOO_PF_CHECK_RCV (moo, MOO_OBJ_IS_CHAR_POINTER(rcv)); /* [NOTE] the length check loop is directly implemented * here to be able to handle character objects * regardless of the existence of the EXTRA flag */ limit = MOO_OBJ_GET_SIZE(rcv); ptr = MOO_OBJ_GET_CHAR_SLOT(rcv); for (i = 0; i < limit; i++) { if (*ptr == '\0') break; ptr++; } retv = moo_oowtoint(moo, i); if (!retv) return MOO_PF_FAILURE; MOO_STACK_SETRET (moo, nargs, retv); return MOO_PF_SUCCESS; } /* ------------------------------------------------------------------ */ static moo_pfrc_t pf_system_log (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { moo_oop_t msg, level; moo_bitmask_t mask; moo_ooi_t k; MOO_ASSERT (moo, nargs >= 2); /* TODO: enhance this primitive */ level = MOO_STACK_GETARG(moo, nargs, 0); if (!MOO_OOP_IS_SMOOI(level)) mask = MOO_LOG_APP | MOO_LOG_INFO; else mask = MOO_LOG_APP | MOO_OOP_TO_SMOOI(level); for (k = 1; k < nargs; k++) { msg = MOO_STACK_GETARG(moo, nargs, k); if (msg == moo->_nil || msg == moo->_true || msg == moo->_false) { goto dump_object; } else if (MOO_OOP_IS_POINTER(msg)) { if (MOO_OBJ_GET_FLAGS_TYPE(msg) == MOO_OBJ_TYPE_CHAR) { log_char_object (moo, mask, (moo_oop_char_t)msg); } else if (MOO_OBJ_GET_FLAGS_TYPE(msg) == MOO_OBJ_TYPE_OOP) { /* visit only 1-level down into an array-like object */ moo_oop_t inner; moo_oop_class_t _class; moo_oow_t i, spec; _class = MOO_CLASSOF(moo, msg); spec = MOO_OOP_TO_SMOOI(((moo_oop_class_t)_class)->spec); if (MOO_CLASS_SPEC_NAMED_INSTVARS(spec) > 0 || !MOO_CLASS_SPEC_IS_INDEXED(spec)) goto dump_object; for (i = 0; i < MOO_OBJ_GET_SIZE(msg); i++) { inner = MOO_OBJ_GET_OOP_VAL(msg, i); if (i > 0) moo_logbfmt (moo, mask, " "); if (MOO_OOP_IS_POINTER(inner) && MOO_OBJ_GET_FLAGS_TYPE(inner) == MOO_OBJ_TYPE_CHAR) { log_char_object (moo, mask, (moo_oop_char_t)inner); } else { moo_logbfmt (moo, mask, "%O", inner); } } } else goto dump_object; } else { dump_object: moo_logbfmt (moo, mask, "%O", msg); } } MOO_STACK_SETRETTORCV (moo, nargs); /* ^self */ return MOO_PF_SUCCESS; } #define MA MOO_TYPE_MAX(moo_oow_t) struct pf_t { const char* name; /* the name is supposed to be 7-bit ascii only */ moo_pfbase_t pfbase; }; typedef struct pf_t pf_t; static pf_t pftab[] = { { "Apex_=", { moo_pf_equal, 1, 1 } }, { "Apex_==", { moo_pf_identical, 1, 1 } }, { "Apex_addToBeFinalized", { pf_add_to_be_finalized, 0, 0 } }, { "Apex_basicAt:", { moo_pf_basic_at, 1, 1 } }, { "Apex_basicAt:put:", { moo_pf_basic_at_put, 2, 2 } }, { "Apex_basicAt:test:put:", { moo_pf_basic_at_test_put, 3, 3 } }, { "Apex_basicFillFrom:with:count:", { moo_pf_basic_fill, 3, 3 } }, { "Apex_basicFirstIndex", { moo_pf_basic_first_index, 0, 0 } }, { "Apex_basicLastIndex", { moo_pf_basic_last_index, 0, 0 } }, { "Apex_basicNew", { moo_pf_basic_new, 0, 0 } }, { "Apex_basicNew:", { moo_pf_basic_new, 1, 1 } }, { "Apex_basicShiftFrom:to:count:", { moo_pf_basic_shift, 3, 3 } }, { "Apex_basicSize", { moo_pf_basic_size, 0, 0 } }, { "Apex_class", { moo_pf_class, 0, 0 } }, { "Apex_copy", { moo_pf_shallow_copy, 0, 0 } }, { "Apex_hash", { pf_hash, 0, 0 } }, { "Apex_isKindOf:", { moo_pf_is_kind_of, 1, 1, } }, { "Apex_perform", { pf_perform, 1, MA } }, { "Apex_removeToBeFinalized", { pf_remove_to_be_finalized, 0, 0 } }, { "Apex_respondsTo:", { moo_pf_responds_to, 1, 1 } }, { "Apex_shallowCopy", { moo_pf_shallow_copy, 0, 0 } }, { "Apex_~=", { moo_pf_not_equal, 1, 1 } }, { "Apex_~~", { moo_pf_not_identical, 1, 1 } }, { "Character_<", { pf_character_lt, 1, 1 } }, { "Character_<=", { pf_character_le, 1, 1 } }, { "Character_>", { pf_character_gt, 1, 1 } }, { "Character_>=", { pf_character_ge, 1, 1 } }, { "Character_asError", { pf_character_as_error, 0, 0 } }, { "Character_asInteger", { pf_character_as_smooi, 0, 0 } }, { "CompiledBlock_newProcess", { pf_block_new_process, 0, MA } }, { "CompiledBlock_newSystemProcess", { pf_block_new_system_process, 0, MA } }, { "CompiledBlock_value", { pf_block_value, 0, MA } }, { "CompiledMethod_ipSourceLine:", { pf_method_get_ip_source_line, 1, 1 } }, { "CompiledMethod_sourceFile", { pf_method_get_source_file, 0, 0 } }, { "CompiledMethod_sourceLine", { pf_method_get_source_line, 0, 0 } }, { "CompiledMethod_sourceText", { pf_method_get_source_text, 0, 0 } }, { "Error_asCharacter", { pf_error_as_character, 0, 0 } }, { "Error_asInteger", { pf_error_as_integer, 0, 0 } }, { "Error_asString", { pf_error_as_string, 0, 0 } }, { "FixedPointDecimal_scale:", { pf_number_scale, 1, 1 } }, { "Integer_add", { pf_integer_add, 1, 1 } }, { "Integer_bitand", { pf_integer_bitand, 1, MA } }, { "Integer_bitat", { pf_integer_bitat, 1, 1 } }, { "Integer_bitinv", { pf_integer_bitinv, 0, 0 } }, { "Integer_bitor", { pf_integer_bitor, 1, MA } }, { "Integer_bitshift", { pf_integer_bitshift, 1, 1 } }, { "Integer_bitxor", { pf_integer_bitxor, 1, MA } }, { "Integer_div", { pf_integer_div, 1, 1 } }, { "Integer_eq", { pf_integer_eq, 1, 1 } }, { "Integer_ge", { pf_integer_ge, 1, 1 } }, { "Integer_gt", { pf_integer_gt, 1, 1 } }, { "Integer_inttostr", { pf_integer_inttostr, 1, 1 } }, { "Integer_le", { pf_integer_le, 1, 1 } }, { "Integer_lt", { pf_integer_lt, 1, 1 } }, { "Integer_mdiv", { pf_integer_mdiv, 1, 1 } }, { "Integer_mod", { pf_integer_mod, 1, 1 } }, { "Integer_mul", { pf_integer_mul, 1, 1 } }, { "Integer_ne", { pf_integer_ne, 1, 1 } }, { "Integer_negated", { pf_integer_negated, 0, 0 } }, { "Integer_rem", { pf_integer_rem, 1, 1 } }, { "Integer_scale:", { pf_number_scale, 1, 1 } }, { "Integer_sub", { pf_integer_sub, 1, 1 } }, { "MethodContext_findExceptionHandler:", { pf_context_find_exception_handler, 1, 1 } }, { "MethodContext_goto:", { pf_context_goto, 1, 1 } }, { "Process_isInnate", { pf_process_is_innate, 0, 0 } }, { "Process_isNormal", { pf_process_is_normal, 0, 0 } }, { "Process_resume", { pf_process_resume, 0, 0 } }, { "Process_sp", { pf_process_sp, 0, 0 } }, { "Process_suspend", { pf_process_suspend, 0, 0 } }, { "Process_terminate", { pf_process_terminate, 0, 0 } }, { "Process_yield", { pf_process_yield, 0, 0 } }, { "SemaphoreGroup_addSemaphore:", { pf_semaphore_group_add_semaphore, 1, 1 } }, { "SemaphoreGroup_removeSemaphore:", { pf_semaphore_group_remove_semaphore, 1, 1 } }, { "SemaphoreGroup_wait", { pf_semaphore_group_wait, 0, 0 } }, { "Semaphore_signal", { pf_semaphore_signal, 0, 0 } }, { "Semaphore_signalAfterSecs:", { pf_semaphore_signal, 1, 1 } }, { "Semaphore_signalAfterSecs:nanosecs:", { pf_semaphore_signal, 2, 2 } }, { "Semaphore_signalOnGCFin", { pf_semaphore_signal_on_gcfin, 0, 0 } }, { "Semaphore_signalOnInput:", { pf_semaphore_signal_on_input, 1, 1 } }, { "Semaphore_signalOnOutput:", { pf_semaphore_signal_on_output, 1, 1 } }, { "Semaphore_unsignal", { pf_semaphore_unsignal, 0, 0 } }, { "Semaphore_wait", { pf_semaphore_wait, 0, 0 } }, { "SmallInteger_asCharacter", { pf_smooi_as_character, 0, 0 } }, { "SmallInteger_asError", { pf_smooi_as_error, 0, 0 } }, { "SmallPointer_asString", { moo_pf_smptr_as_string, 0, 0 } }, { "SmallPointer_free", { moo_pf_smptr_free, 0, 0 } }, { "SmallPointer_getBytes", { moo_pf_smptr_get_bytes, 4, 4 } }, { "SmallPointer_getInt16", { moo_pf_smptr_get_int16, 1, 1 } }, { "SmallPointer_getInt32", { moo_pf_smptr_get_int32, 1, 1 } }, { "SmallPointer_getInt64", { moo_pf_smptr_get_int64, 1, 1 } }, { "SmallPointer_getInt8", { moo_pf_smptr_get_int8, 1, 1 } }, { "SmallPointer_getUint16", { moo_pf_smptr_get_uint16, 1, 1 } }, { "SmallPointer_getUint32", { moo_pf_smptr_get_uint32, 1, 1 } }, { "SmallPointer_getUint64", { moo_pf_smptr_get_uint64, 1, 1 } }, { "SmallPointer_getUint8", { moo_pf_smptr_get_uint8, 1, 1 } }, { "SmallPointer_putBytes", { moo_pf_smptr_put_bytes, 4, 4 } }, { "SmallPointer_putInt16", { moo_pf_smptr_put_int16, 2, 2 } }, { "SmallPointer_putInt32", { moo_pf_smptr_put_int32, 2, 2 } }, { "SmallPointer_putInt64", { moo_pf_smptr_put_int64, 2, 2 } }, { "SmallPointer_putInt8", { moo_pf_smptr_put_int8, 2, 2 } }, { "SmallPointer_putUint16", { moo_pf_smptr_put_uint16, 2, 2 } }, { "SmallPointer_putUint32", { moo_pf_smptr_put_uint32, 2, 2 } }, { "SmallPointer_putUint64", { moo_pf_smptr_put_uint64, 2, 2 } }, { "SmallPointer_putUint8", { moo_pf_smptr_put_uint8, 2, 2 } }, { "String_format", { pf_string_format, 1, MA } }, { "String_strfmt", { pf_strfmt, 0, MA } }, { "String_strlen", { pf_strlen, 0, 0 } }, { "System_calloc", { moo_pf_system_calloc, 1, 1 } }, { "System_calloc:", { moo_pf_system_calloc, 1, 1 } }, { "System_collectGarbage", { moo_pf_system_collect_garbage, 0, 0 } }, { "System_findFirstProcess", { moo_pf_system_find_first_process, 0, 0 } }, { "System_findLastProcess", { moo_pf_system_find_last_process, 0, 0 } }, { "System_findNextProcess:", { moo_pf_system_find_next_process, 1, 1 } }, { "System_findPreviousProcess:", { moo_pf_system_find_previous_process, 1, 1 } }, { "System_findProcessById:", { moo_pf_system_find_process_by_id, 1, 1 } }, { "System_free", { moo_pf_system_free, 1, 1 } }, { "System_free:", { moo_pf_system_free, 1, 1 } }, { "System_gc", { moo_pf_system_collect_garbage, 0, 0 } }, { "System_getBytes", { moo_pf_system_get_bytes, 5, 5 } }, { "System_getInt16", { moo_pf_system_get_int16, 2, 2 } }, { "System_getInt32", { moo_pf_system_get_int32, 2, 2 } }, { "System_getInt64", { moo_pf_system_get_int64, 2, 2 } }, { "System_getInt8", { moo_pf_system_get_int8, 2, 2 } }, { "System_getSig", { moo_pf_system_get_sig, 0, 0 } }, { "System_getSigfd", { moo_pf_system_get_sigfd, 0, 0 } }, { "System_getUint16", { moo_pf_system_get_uint16, 2, 2 } }, { "System_getUint32", { moo_pf_system_get_uint32, 2, 2 } }, { "System_getUint64", { moo_pf_system_get_uint64, 2, 2 } }, { "System_getUint8", { moo_pf_system_get_uint8, 2, 2 } }, { "System_halting", { moo_pf_system_halting, 0, 0 } }, { "System_log", { pf_system_log, 2, MA } }, { "System_malloc", { moo_pf_system_malloc, 1, 1 } }, { "System_malloc:", { moo_pf_system_malloc, 1, 1 } }, { "System_popCollectable", { moo_pf_system_pop_collectable, 0, 0 } }, { "System_putBytes", { moo_pf_system_put_bytes, 5, 5 } }, { "System_putInt16", { moo_pf_system_put_int16, 3, 3 } }, { "System_putInt32", { moo_pf_system_put_int32, 3, 3 } }, { "System_putInt64", { moo_pf_system_put_int64, 3, 3 } }, { "System_putInt8", { moo_pf_system_put_int8, 3, 3 } }, { "System_putUint16", { moo_pf_system_put_uint16, 3, 3 } }, { "System_putUint32", { moo_pf_system_put_uint32, 3, 3 } }, { "System_putUint64", { moo_pf_system_put_uint64, 3, 3 } }, { "System_putUint8", { moo_pf_system_put_uint8, 3, 3 } }, { "System_return:to:", { pf_system_return_value_to_context, 2, 2 } }, { "System_setSig:", { moo_pf_system_set_sig, 1, 1 } }, { "System_toggleProcessSwitching:", { moo_pf_system_toggle_process_switching, 1, 1 } }, { "_dump", { pf_dump, 0, MA } }, { "_number_add", { pf_number_add, 1, 1 } }, { "_number_div", { pf_number_div, 1, 1 } }, { "_number_eq", { pf_number_eq, 1, 1 } }, { "_number_ge", { pf_number_ge, 1, 1 } }, { "_number_gt", { pf_number_gt, 1, 1 } }, { "_number_le", { pf_number_le, 1, 1 } }, { "_number_lt", { pf_number_lt, 1, 1 } }, { "_number_mdiv", { pf_number_mdiv, 1, 1 } }, { "_number_mlt", { pf_number_mlt, 1, 1 } }, { "_number_mul", { pf_number_mul, 1, 1 } }, { "_number_ne", { pf_number_ne, 1, 1 } }, { "_number_negated", { pf_number_negated, 0, 0 } }, { "_number_numtostr", { pf_number_numtostr, 1, 1 } }, { "_number_scale:", { pf_number_scale, 1, 1 } }, { "_number_sub", { pf_number_sub, 1, 1 } }, { "_utf8_seqlen", { moo_pf_utf8_seqlen, 0, 0 } }, { "_utf8_to_uc", { moo_pf_utf8_to_uc, 0, 0 } }, }; moo_pfbase_t* moo_getpfnum (moo_t* moo, const moo_ooch_t* ptr, moo_oow_t len, moo_ooi_t* pfnum) { moo_oow_t base, mid, lim; int n; for (base = 0, lim = MOO_COUNTOF(pftab); lim > 0; lim >>= 1) { mid = base + (lim >> 1); /* moo_comp_oochars_bcstr() is not aware of multibyte encoding. * so the names above should be composed of the single byte * characters only */ n = moo_comp_oochars_bcstr(ptr, len, pftab[mid].name); if (n == 0) { MOO_ASSERT (moo, MOO_OOI_IN_METHOD_PREAMBLE_INDEX_RANGE(mid)); /* this must never be so big */ *pfnum = mid; return &pftab[mid].pfbase; } if (n > 0) { base = mid + 1; lim--; } } moo_seterrbfmt (moo, MOO_ENOENT, "unknown primitive specifier %.*js", len, ptr); return MOO_NULL; } /* ------------------------------------------------------------------------- */ static int start_method (moo_t* moo, moo_oop_method_t method, moo_oow_t nargs) { moo_ooi_t preamble, preamble_code, preamble_flags; moo_ooi_t /*sp,*/ stack_base; #if defined(MOO_DEBUG_VM_EXEC) /* set it to a fake value */ moo->last_inst_pointer = 0; #endif preamble = MOO_OOP_TO_SMOOI(method->preamble); preamble_flags = MOO_METHOD_GET_PREAMBLE_FLAGS(preamble); if (preamble_flags & MOO_METHOD_PREAMBLE_FLAG_LIBERAL) { /* do nothing - no argument check */ } else if (preamble_flags & MOO_METHOD_PREAMBLE_FLAG_VARIADIC) { if (nargs < MOO_OOP_TO_SMOOI(method->tmpr_nargs)) goto arg_count_mismatch; } else { if (nargs != MOO_OOP_TO_SMOOI(method->tmpr_nargs)) { /* TODO: better to throw a moo exception so that the caller can catch it??? */ arg_count_mismatch: MOO_LOG3 (moo, MOO_LOG_IC | MOO_LOG_FATAL, "Fatal error - Argument count mismatch for a non-variadic method [%O] - %zd expected, %zu given\n", method->name, MOO_OOP_TO_SMOOI(method->tmpr_nargs), nargs); moo_seterrnum (moo, MOO_EINVAL); return -1; } } preamble_code = MOO_METHOD_GET_PREAMBLE_CODE(preamble); switch (preamble_code) { case MOO_METHOD_PREAMBLE_RETURN_RECEIVER: LOG_INST0 (moo, "preamble_return_receiver"); MOO_STACK_POPS (moo, nargs); /* pop arguments only*/ break; /* [NOTE] this is useless becuase it returns a caller's context * as the callee's context has not been created yet. case MOO_METHOD_PREAMBLE_RETURN_CONTEXT: LOG_INST0 (moo, "preamble_return_context"); MOO_STACK_POPS (moo, nargs); MOO_STACK_SETTOP (moo, (moo_oop_t)moo->active_context); break; */ case MOO_METHOD_PREAMBLE_RETURN_PROCESS: LOG_INST0 (moo, "preamble_return_process"); MOO_STACK_POPS (moo, nargs); MOO_STACK_SETTOP (moo, (moo_oop_t)moo->processor->active); break; case MOO_METHOD_PREAMBLE_RETURN_RECEIVER_NS: { moo_oop_t c; LOG_INST0 (moo, "preamble_return_receiver_ns"); MOO_STACK_POPS (moo, nargs); /* pop arguments only*/ c = MOO_STACK_GETTOP (moo); /* get receiver */ c = (moo_oop_t)MOO_CLASSOF(moo, c); if (c == (moo_oop_t)moo->_class) c = MOO_STACK_GETTOP (moo); MOO_STACK_SETTOP (moo, (moo_oop_t)((moo_oop_class_t)c)->nsup); break; } case MOO_METHOD_PREAMBLE_RETURN_NIL: LOG_INST0 (moo, "preamble_return_nil"); MOO_STACK_POPS (moo, nargs); MOO_STACK_SETTOP (moo, moo->_nil); break; case MOO_METHOD_PREAMBLE_RETURN_TRUE: LOG_INST0 (moo, "preamble_return_true"); MOO_STACK_POPS (moo, nargs); MOO_STACK_SETTOP (moo, moo->_true); break; case MOO_METHOD_PREAMBLE_RETURN_FALSE: LOG_INST0 (moo, "preamble_return_false"); MOO_STACK_POPS (moo, nargs); MOO_STACK_SETTOP (moo, moo->_false); break; case MOO_METHOD_PREAMBLE_RETURN_INDEX: /* preamble_index field is used to store a positive integer */ LOG_INST1 (moo, "preamble_return_index %zd", MOO_METHOD_GET_PREAMBLE_INDEX(preamble)); MOO_STACK_POPS (moo, nargs); MOO_STACK_SETTOP (moo, MOO_SMOOI_TO_OOP(MOO_METHOD_GET_PREAMBLE_INDEX(preamble))); break; case MOO_METHOD_PREAMBLE_RETURN_NEGINDEX: /* preamble_index field is used to store a negative integer */ LOG_INST1 (moo, "preamble_return_negindex %zd", MOO_METHOD_GET_PREAMBLE_INDEX(preamble)); MOO_STACK_POPS (moo, nargs); MOO_STACK_SETTOP (moo, MOO_SMOOI_TO_OOP(-MOO_METHOD_GET_PREAMBLE_INDEX(preamble))); break; case MOO_METHOD_PREAMBLE_RETURN_INSTVAR: { moo_oop_oop_t rcv; moo_ooi_t index; MOO_STACK_POPS (moo, nargs); /* pop arguments only */ LOG_INST1 (moo, "preamble_return_instvar %zd", MOO_METHOD_GET_PREAMBLE_INDEX(preamble)); /* replace the receiver by an instance variable of the receiver */ rcv = (moo_oop_oop_t)MOO_STACK_GETTOP(moo); MOO_ASSERT (moo, MOO_OBJ_GET_FLAGS_TYPE(rcv) == MOO_OBJ_TYPE_OOP); MOO_ASSERT (moo, MOO_OBJ_GET_SIZE(rcv) > MOO_METHOD_GET_PREAMBLE_INDEX(preamble)); if (rcv == (moo_oop_oop_t)moo->active_context) { /* the active context object doesn't keep * the most up-to-date information in the * 'ip' and 'sp' field. commit these fields * when the object to be accessed is * the active context. this manual commit * is required because this premable handling * skips activation of a new method context * that would commit these fields. */ STORE_ACTIVE_IP (moo); STORE_ACTIVE_SP (moo); } /* this accesses the instance variable of the receiver */ index = MOO_METHOD_GET_PREAMBLE_INDEX(preamble); MOO_STACK_SET (moo, moo->sp, MOO_OBJ_GET_OOP_VAL(rcv, index)); break; } case MOO_METHOD_PREAMBLE_PRIMITIVE: { moo_ooi_t pfnum; stack_base = moo->sp - nargs - 1; /* stack base before receiver and arguments */ pfnum = MOO_METHOD_GET_PREAMBLE_INDEX(preamble); LOG_INST1 (moo, "preamble_primitive %zd", pfnum); if (pfnum >= 0 && pfnum < MOO_COUNTOF(pftab)) { int n; if ((nargs < pftab[pfnum].pfbase.minargs || nargs > pftab[pfnum].pfbase.maxargs)) { MOO_LOG4 (moo, MOO_LOG_DEBUG, "Soft failure due to argument count mismatch for primitive function %hs - %zu-%zu expected, %zu given\n", pftab[pfnum].name, pftab[pfnum].pfbase.minargs, pftab[pfnum].pfbase.maxargs, nargs); moo_seterrnum (moo, MOO_ENUMARGS); goto activate_primitive_method_body; } moo_pushvolat (moo, (moo_oop_t*)&method); n = pftab[pfnum].pfbase.handler(moo, MOO_NULL, nargs); /* builtin numbered primitive. the second parameter is MOO_NULL */ moo_popvolat (moo); if (n <= MOO_PF_HARD_FAILURE) { MOO_LOG3 (moo, MOO_LOG_DEBUG, "Hard failure indicated by primitive function %p - %hs - return code %d\n", pftab[pfnum].pfbase.handler, pftab[pfnum].name, n); return -1; } if (n >= MOO_PF_SUCCESS) break; MOO_LOG2 (moo, MOO_LOG_DEBUG, "Soft failure indicated by primitive function %p - %hs\n", pftab[pfnum].pfbase.handler, pftab[pfnum].name); } else { MOO_LOG1 (moo, MOO_LOG_DEBUG, "Cannot call primitive function numbered %zd - unknown primitive function number\n", pfnum); moo_seterrbfmt (moo, MOO_ENOENT, "unknown primitive function number %zd", pfnum); } goto activate_primitive_method_body; } case MOO_METHOD_PREAMBLE_NAMED_PRIMITIVE: { moo_ooi_t pf_name_index; moo_pfbase_t* pfbase; moo_oop_t pfname; /*moo_oow_t w;*/ moo_mod_t* mod; stack_base = moo->sp - nargs - 1; /* stack base before receiver and arguments */ /* index to the primitive function identifier in the literal frame */ pf_name_index = MOO_METHOD_GET_PREAMBLE_INDEX(preamble); MOO_ASSERT (moo, pf_name_index >= 0); pfname = method->literal_frame[pf_name_index]; #if defined(MOO_BUILD_DEBUG) LOG_INST1 (moo, "preamble_named_primitive %zd", pf_name_index); #endif MOO_ASSERT (moo, MOO_OBJ_IS_CHAR_POINTER(pfname)); MOO_ASSERT (moo, MOO_OBJ_GET_FLAGS_EXTRA(pfname)); MOO_ASSERT (moo, MOO_CLASSOF(moo,pfname) == moo->_symbol); /* merge two SmallIntegers to get a full pointer from the cached data */ /*w = (moo_oow_t)MOO_OOP_TO_SMOOI(method->preamble_data[0]) << (MOO_OOW_BITS / 2) | (moo_oow_t)MOO_OOP_TO_SMOOI(method->preamble_data[1]); pfbase = (moo_pfbase_t*)w;*/ pfbase = MOO_OOP_TO_SMPTR(method->preamble_data[1]); if (pfbase) { mod = MOO_OOP_TO_SMPTR(method->preamble_data[0]); goto exec_handler; /* skip moo_querymodpf() */ } pfbase = moo_querymodpf(moo, MOO_OBJ_GET_CHAR_SLOT(pfname), MOO_OBJ_GET_SIZE(pfname), &mod); if (pfbase) { int n; /* split a pointer to two OOP fields as SmallIntegers for storing/caching. */ /*method->preamble_data[0] = MOO_SMOOI_TO_OOP((moo_oow_t)pfbase >> (MOO_OOW_BITS / 2)); method->preamble_data[1] = MOO_SMOOI_TO_OOP((moo_oow_t)pfbase & MOO_LBMASK(moo_oow_t, MOO_OOW_BITS / 2));*/ MOO_ASSERT (moo, MOO_IN_SMPTR_RANGE(mod)); MOO_ASSERT (moo, MOO_IN_SMPTR_RANGE(pfbase)); method->preamble_data[0] = MOO_SMPTR_TO_OOP(mod); method->preamble_data[1] = MOO_SMPTR_TO_OOP(pfbase); exec_handler: if (nargs < pfbase->minargs || nargs > pfbase->maxargs) { MOO_LOG5 (moo, MOO_LOG_DEBUG, "Soft failure due to argument count mismatch for primitive function %.*js - %zu-%zu expected, %zu given\n", MOO_OBJ_GET_SIZE(pfname), MOO_OBJ_GET_CHAR_SLOT(pfname), pfbase->minargs, pfbase->maxargs, nargs); moo_seterrbfmt (moo, MOO_ENUMARGS, "argument count mismatch for %.*js, %zu-%zu expected, %zu given", MOO_OBJ_GET_SIZE(pfname), MOO_OBJ_GET_CHAR_SLOT(pfname), pfbase->minargs, pfbase->maxargs, nargs); goto activate_primitive_method_body; } moo_pushvolat (moo, (moo_oop_t*)&method); /* the primitive handler is executed without activating the method itself. * one major difference between the primitive function and the normal method * invocation is that the primitive function handler should access arguments * directly in the stack unlik a normal activated method context where the * arguments are copied to the back. */ moo_seterrnum (moo, MOO_ENOERR); n = pfbase->handler(moo, mod, nargs); moo_popvolat (moo); if (n <= MOO_PF_HARD_FAILURE) { MOO_LOG4 (moo, MOO_LOG_DEBUG, "Hard failure indicated by primitive function %p - %.*js - return code %d\n", pfbase->handler, MOO_OBJ_GET_SIZE(pfname), MOO_OBJ_GET_CHAR_SLOT(pfname), n); return -1; /* hard primitive failure */ } if (n >= MOO_PF_SUCCESS) break; /* primitive ok*/ /* soft primitive failure */ MOO_LOG3 (moo, MOO_LOG_DEBUG, "Soft failure indicated by primitive function %p - %.*js\n", pfbase->handler, MOO_OBJ_GET_SIZE(pfname), MOO_OBJ_GET_CHAR_SLOT(pfname)); } else { /* no handler found */ MOO_LOG2 (moo, MOO_LOG_DEBUG, "Soft failure for non-existent primitive function - %.*js\n", MOO_OBJ_GET_SIZE(pfname), MOO_OBJ_GET_CHAR_SLOT(pfname)); } activate_primitive_method_body: /* set the error number in the current process for 'thisProcess primError' */ moo->processor->active->perr = MOO_ERROR_TO_OOP(moo->errnum); if (moo->errmsg.len > 0) { /* compose an error message string. */ /* TODO: i don't like to do this here. * is it really a good idea to compose a string here which * is not really failure safe without losing integrity???? */ moo_oop_t tmp; moo_pushvolat (moo, (moo_oop_t*)&method); tmp = moo_makestring(moo, moo->errmsg.buf, moo->errmsg.len); moo_popvolat (moo); /* [NOTE] carry on even if instantiation fails */ if (!tmp) goto no_perrmsg; MOO_STORE_OOP (moo, &moo->processor->active->perrmsg, tmp); } else { no_perrmsg: moo->processor->active->perrmsg = moo->_nil; } if (MOO_METHOD_GET_PREAMBLE_FLAGS(preamble) & MOO_METHOD_PREAMBLE_FLAG_LENIENT) { /* convert soft failure to error return */ moo->sp = stack_base; MOO_STACK_PUSH (moo, moo->processor->active->perr); break; } MOO_ASSERT (moo, MOO_OBJ_GET_FLAGS_TRAILER(method)); if (MOO_METHOD_GET_CODE_SIZE(method) == 0) /* this trailer size field is not a small integer */ { /* no byte code to execute - invoke 'self primitiveFailed' */ moo_oow_t i; if (stack_base != moo->sp - nargs - 1) { /* a primitive function handler must not touch the stack when it returns soft failure */ MOO_LOG3 (moo, MOO_LOG_DEBUG, "Stack seems to get corrupted by a primitive handler function - %O>>%.*js\n", method->owner, MOO_OBJ_GET_SIZE(method->name), MOO_OBJ_GET_CHAR_SLOT(method->name)); moo_seterrnum (moo, MOO_EINTERN); return -1; } MOO_LOG3 (moo, MOO_LOG_DEBUG, "Sending primitiveFailed - %O>>%.*js\n", method->owner, MOO_OBJ_GET_SIZE(method->name), MOO_OBJ_GET_CHAR_SLOT(method->name)); /* * | arg1 | <---- stack_base + 3 * | arg0 | <---- stack_base + 2 * | receiver | <---- stack_base + 1 * | | <---- stack_base */ /* push out arguments by one slot */ MOO_STACK_PUSH (moo, moo->_nil); /* fake */ for (i = moo->sp; i > stack_base + 2; i--) MOO_STACK_SET (moo, i, MOO_STACK_GET(moo, i - 1)); /* inject the method as the first argument */ MOO_STACK_SET (moo, stack_base + 2, (moo_oop_t)method); /* send primitiveFailed to self */ if (send_message(moo, moo->primitive_failed_sym, nargs + 1, 0) <= -1) return -1; } else { /* arrange to execute the method body */ if (activate_new_method(moo, method, nargs) <= -1) return -1; } break; } default: MOO_ASSERT (moo, preamble_code == MOO_METHOD_PREAMBLE_NONE || preamble_code == MOO_METHOD_PREAMBLE_RETURN_CONTEXT || preamble_code == MOO_METHOD_PREAMBLE_EXCEPTION || preamble_code == MOO_METHOD_PREAMBLE_ENSURE); if (activate_new_method(moo, method, nargs) <= -1) return -1; break; } return 0; } static int send_message (moo_t* moo, moo_oop_char_t selector, moo_ooi_t nargs, int to_super) { moo_oop_t receiver; moo_oop_method_t method; MOO_ASSERT (moo, MOO_OOP_IS_POINTER(selector)); MOO_ASSERT (moo, MOO_OBJ_GET_FLAGS_TYPE(selector) == MOO_OBJ_TYPE_CHAR); MOO_ASSERT (moo, MOO_CLASSOF(moo, selector) == moo->_symbol); receiver = MOO_STACK_GETRCV(moo, nargs); #if defined(MOO_PROFILE_VM) moo->stat.message_sends++; #endif method = moo_findmethod_noseterr(moo, receiver, selector, to_super); if (!method) { method = moo_findmethod_noseterr(moo, receiver, moo->does_not_understand_sym, 0); if (!method) { /* this must not happen as long as doesNotUnderstand: is implemented under Apex. * this check should indicate a very serious internal problem */ MOO_LOG4 (moo, MOO_LOG_IC | MOO_LOG_FATAL, "Fatal error - unable to find a fallback method [%O>>%.*js] for receiver [%O]\n", MOO_CLASSOF(moo, receiver), MOO_OBJ_GET_SIZE(moo->does_not_understand_sym), MOO_OBJ_GET_CHAR_SLOT(moo->does_not_understand_sym), receiver); moo_seterrbfmt (moo, MOO_EMSGSND, "unable to find a fallback method - %O>>%.*js", MOO_CLASSOF(moo, receiver), MOO_OBJ_GET_SIZE(moo->does_not_understand_sym), MOO_OBJ_GET_CHAR_SLOT(moo->does_not_understand_sym)); return -1; } else { /* manipulate the stack as if 'receier doesNotUnderstand: selector' * has been called. */ /* TODO: if i manipulate the stack this way here, the stack trace for the last call is kind of lost. * how can i preserve it gracefully? */ MOO_STACK_POPS (moo, nargs); nargs = 1; MOO_STACK_PUSH (moo, (moo_oop_t)selector); } } return start_method(moo, method, nargs); } /* ------------------------------------------------------------------------- */ static MOO_INLINE int switch_process_if_needed (moo_t* moo) { if (moo->sem_heap_count > 0) { /* handle timed semaphores */ moo_ntime_t ft, now; vm_gettime (moo, &now); do { MOO_ASSERT (moo, moo->sem_heap[0]->subtype == MOO_SMOOI_TO_OOP(MOO_SEMAPHORE_SUBTYPE_TIMED)); MOO_ASSERT (moo, MOO_OOP_IS_SMOOI(moo->sem_heap[0]->u.timed.ftime_sec)); MOO_ASSERT (moo, MOO_OOP_IS_SMOOI(moo->sem_heap[0]->u.timed.ftime_nsec)); MOO_INIT_NTIME (&ft, MOO_OOP_TO_SMOOI(moo->sem_heap[0]->u.timed.ftime_sec), MOO_OOP_TO_SMOOI(moo->sem_heap[0]->u.timed.ftime_nsec) ); if (MOO_CMP_NTIME(&ft, (moo_ntime_t*)&now) <= 0) { moo_oop_process_t proc; signal_timed: /* waited long enough. signal the semaphore */ proc = signal_semaphore(moo, moo->sem_heap[0]); /* [NOTE] no moo_pushvolat() on proc. no GC must occur * in the following line until it's used for * wake_process() below. */ delete_from_sem_heap (moo, 0); /* moo->sem_heap_count is decremented in delete_from_sem_heap() */ /* if no process is waiting on the semaphore, * signal_semaphore() returns moo->_nil. */ if (moo->processor->active == moo->nil_process && (moo_oop_t)proc != moo->_nil) { /* this is the only runnable process. * switch the process to the running state. * it uses wake_process() instead of * switch_to_process() as there is no running * process at this moment */ #if defined(MOO_DEBUG_VM_PROCESSOR) && (MOO_DEBUG_VM_PROCESSOR >= 2) MOO_LOG2 (moo, MOO_LOG_IC | MOO_LOG_DEBUG, "Processor - switching to a process[%zd] while no process is active - total runnables %zd\n", MOO_OOP_TO_SMOOI(proc->id), MOO_OOP_TO_SMOOI(moo->processor->runnable.count)); #endif MOO_ASSERT (moo, proc->state == MOO_SMOOI_TO_OOP(PROC_STATE_RUNNABLE)); MOO_ASSERT (moo, proc == moo->processor->runnable.last); /* resume_process() appends to the runnable list */ #if 0 wake_process (moo, proc); /* switch to running */ moo->proc_switched = 1; #else switch_to_process_from_nil (moo, proc); #endif } } else if (moo->processor->active == moo->nil_process) { /* no running process. before firing time. */ MOO_SUB_NTIME (&ft, &ft, (moo_ntime_t*)&now); if (moo->sem_io_wait_count > 0) { /* no running process but io semaphore being waited on */ vm_muxwait (moo, &ft); /* exit early if a process has been woken up. * the break in the else part further down will get hit * eventually even if the following line doesn't exist. * having the following line causes to skip firing the * timed semaphore that would expire between now and the * moment the next inspection occurs. */ if (moo->processor->active != moo->nil_process) goto switch_to_next; } else { int halting; /* no running process, no io semaphore */ if ((moo_oop_t)moo->sem_gcfin != moo->_nil && moo->sem_gcfin_sigreq) goto signal_sem_gcfin; halting = vm_sleep(moo, &ft); if (halting) { vm_gettime (moo, &now); goto signal_timed; } } vm_gettime (moo, &now); } else { /* there is a running process. go on */ break; } } while (moo->sem_heap_count > 0 && !moo->abort_req); } if (moo->sem_io_wait_count > 0) { if (moo->processor->active == moo->nil_process) { moo_ntime_t ft; MOO_ASSERT (moo, moo->processor->runnable.count == MOO_SMOOI_TO_OOP(0)); /* no runnable process while there is an io semaphore being waited for */ if ((moo_oop_t)moo->sem_gcfin != moo->_nil && moo->sem_gcfin_sigreq) goto signal_sem_gcfin; if (moo->processor->suspended.count == MOO_SMOOI_TO_OOP(0)) { /* no suspended process. the program is buggy or is probably being * terminated forcibly. * the default signal handler may lead to this situation. */ moo->abort_req = 1; } else { do { MOO_INIT_NTIME (&ft, 3, 0); /* TODO: use a configured time */ vm_muxwait (moo, &ft); } while (moo->processor->active == moo->nil_process && !moo->abort_req); } } else { /* well, there is a process waiting on one or more semaphores while * there are other normal processes to run. check IO activities * before proceeding to handle normal process scheduling */ /* [NOTE] the check with the multiplexer may happen too frequently * because this is called everytime process switching is requested. * the actual callback implementation should try to avoid invoking * actual system calls too frequently for less overhead. */ vm_muxwait (moo, MOO_NULL); } } if ((moo_oop_t)moo->sem_gcfin != moo->_nil) { moo_oop_process_t proc; if (moo->sem_gcfin_sigreq) { signal_sem_gcfin: MOO_LOG0 (moo, MOO_LOG_IC | MOO_LOG_DEBUG, "Signaled GCFIN semaphore\n"); proc = signal_semaphore(moo, moo->sem_gcfin); if (moo->processor->active == moo->nil_process && (moo_oop_t)proc != moo->_nil) { MOO_ASSERT (moo, proc->state == MOO_SMOOI_TO_OOP(PROC_STATE_RUNNABLE)); MOO_ASSERT (moo, proc == moo->processor->runnable.first); switch_to_process_from_nil (moo, proc); } moo->sem_gcfin_sigreq = 0; } else { /* the gcfin semaphore signalling is not requested and there are * no runnable processes nor no waiting semaphores. if there is * process waiting on the gcfin semaphore, i will just schedule * it to run by calling signal_semaphore() on moo->sem_gcfin. */ /* TODO: check if this is the best implementation practice */ if (moo->processor->active == moo->nil_process) { /* there is no active process. in most cases, the only process left * should be the gc finalizer process started in the System>>startup. * if there are other suspended processes at this point, the processes * are not likely to run again. * * imagine the following single line program that creates a process * but never start it. * * method(#class) main { | p | p := [] newProcess. } * * the gc finalizer process and the process assigned to p exist. * when the code reaches here, the 'p' process still is alive * despite no active process nor no process waiting on timers * and semaphores. so when the entire program terminates, there * might still be some suspended processes that are not possible * to schedule. */ MOO_LOG4 (moo, MOO_LOG_IC | MOO_LOG_DEBUG, "Signaled GCFIN semaphore without gcfin signal request - total %zd runnable/running %zd suspended %zd - sem_io_wait_count %zu\n", MOO_OOP_TO_SMOOI(moo->processor->total_count), MOO_OOP_TO_SMOOI(moo->processor->runnable.count), MOO_OOP_TO_SMOOI(moo->processor->suspended.count), moo->sem_io_wait_count); proc = signal_semaphore(moo, moo->sem_gcfin); if ((moo_oop_t)proc != moo->_nil) { MOO_ASSERT (moo, proc->state == MOO_SMOOI_TO_OOP(PROC_STATE_RUNNABLE)); MOO_ASSERT (moo, proc == moo->processor->runnable.first); moo->_system->cvar[2] = moo->_true; /* set gcfin_should_exit in System to true. if the postion of the class variable changes, the index must get changed, too. */ switch_to_process_from_nil (moo, proc); /* sechedule the gc finalizer process */ } } } } #if 0 while (moo->sem_list_count > 0) { /* handle async signals */ --moo->sem_list_count; signal_semaphore (moo, moo->sem_list[moo->sem_list_count]); if (moo->processor->active == moo->nil_process) {suspended process } } /* if (semaphore heap has pending request) { signal them... }*/ #endif if (moo->processor->active == moo->nil_process) { /* no more waiting semaphore and no more process */ MOO_ASSERT (moo, moo->processor->runnable.count = MOO_SMOOI_TO_OOP(0)); MOO_LOG0 (moo, MOO_LOG_IC | MOO_LOG_DEBUG, "No more runnable process\n"); if (MOO_OOP_TO_SMOOI(moo->processor->suspended.count) > 0) { /* there exist suspended processes while no processes are runnable. * most likely, the running program contains process/semaphore related bugs */ MOO_LOG1 (moo, MOO_LOG_IC | MOO_LOG_WARN, "%zd suspended process(es) found - check your program\n", MOO_OOP_TO_SMOOI(moo->processor->suspended.count)); } return 0; } switch_to_next: /* TODO: implement different process switching scheme - time-slice or clock based??? */ #if defined(MOO_EXTERNAL_PROCESS_SWITCH) if (moo->switch_proc) { #endif if (!moo->proc_switched) { switch_to_next_runnable_process (moo); moo->proc_switched = 0; } #if defined(MOO_EXTERNAL_PROCESS_SWITCH) moo->switch_proc = 0; } else moo->proc_switched = 0; #endif return 1; } static MOO_INLINE int do_return (moo_t* moo, moo_oob_t bcode, moo_oop_t return_value) { #if 0 /* put the instruction pointer back to the return * instruction (RETURN_RECEIVER or RETURN_RECEIVER) * if a context returns into this context again, * it'll be able to return as well again. * * Consider a program like this: * * #class MyObject(Object) * { * #declare(#classinst) t1 t2. * #method(#class) xxxx * { * | g1 g2 | * t1 dump. * t2 := [ g1 := 50. g2 := 100. ^g1 + g2 ]. * (t1 < 100) ifFalse: [ ^self ]. * t1 := t1 + 1. * ^self xxxx. * } * #method(#class) main * { * t1 := 1. * self xxxx. * t2 := t2 value. * t2 dump. * } * } * * the 'xxxx' method invoked by 'self xxxx' has * returned even before 't2 value' is executed. * the '^' operator makes the active context to * switch to its 'origin->sender' which is the * method context of 'xxxx' itself. placing its * instruction pointer at the 'return' instruction * helps execute another return when the switching * occurs. * * TODO: verify if this really works * */ moo->ip--; #else if (MOO_UNLIKELY(moo->active_context->origin == moo->processor->active->initial_context->origin)) { /* method return from a processified block * * #method(#class) main * { * [^100] newProcess resume. * '1111' dump. * '1111' dump. * '1111' dump. * ^300. * } * * ^100 doesn't terminate a main process as the block * has been processified. on the other hand, ^100 * in the following program causes main to exit. * * #method(#class) main * { * [^100] value. * '1111' dump. * '1111' dump. * '1111' dump. * ^300. * } */ MOO_ASSERT (moo, MOO_CLASSOF(moo, moo->active_context) == moo->_block_context); MOO_ASSERT (moo, MOO_CLASSOF(moo, moo->processor->active->initial_context) == moo->_block_context); /* decrement the instruction pointer back to the return instruction. * even if the context is reentered, it will just return. *moo->ip--;*/ terminate_process (moo, moo->processor->active); } else { int unwind_protect; moo_oop_context_t unwind_start; moo_oop_context_t unwind_stop; unwind_protect = 0; if (moo->active_context->origin == moo->active_context) { /* returning from a method */ MOO_ASSERT (moo, MOO_CLASSOF(moo, moo->active_context) == moo->_method_context); /* set the instruction pointer to an invalid value. * this is stored into the current method context * before context switching by SWITCH_ACTIVE_CONTEXT() * and marks the context dead */ moo->ip = -1; } else { moo_oop_context_t ctx; /* method return from within a block(including a non-local return) */ MOO_ASSERT (moo, MOO_CLASSOF(moo, moo->active_context) == moo->_block_context); ctx = moo->active_context; while ((moo_oop_t)ctx != moo->_nil) { if (MOO_CLASSOF(moo, ctx) == moo->_method_context) { moo_ooi_t preamble; preamble = MOO_OOP_TO_SMOOI(((moo_oop_method_t)ctx->method_or_nargs)->preamble); if (MOO_METHOD_GET_PREAMBLE_CODE(preamble) == MOO_METHOD_PREAMBLE_ENSURE) { if (!unwind_protect) { unwind_protect = 1; unwind_start = ctx; } unwind_stop = ctx; } } if (ctx == moo->active_context->origin) goto non_local_return_ok; ctx = ctx->sender; } /* cannot return from a method that has returned already */ MOO_ASSERT (moo, MOO_CLASSOF(moo, moo->active_context->origin) == moo->_method_context); MOO_ASSERT (moo, moo->active_context->origin->ip == MOO_SMOOI_TO_OOP(-1)); MOO_LOG0 (moo, MOO_LOG_IC | MOO_LOG_ERROR, "Error - cannot return from dead context\n"); moo_seterrnum (moo, MOO_EINTERN); /* TODO: can i make this error catchable at the moo level? */ return -1; non_local_return_ok: /*MOO_LOG2 (moo, MOO_LOG_DEBUG, "NON_LOCAL RETURN OK TO... %p %p\n", moo->active_context->origin, moo->active_context->origin->sender);*/ if (bcode != BCODE_LOCAL_RETURN) { /* mark that the context is dead */ moo->active_context->origin->ip = MOO_SMOOI_TO_OOP(-1); } } /* the origin must always be a method context for both an active block context * or an active method context */ MOO_ASSERT (moo, MOO_CLASSOF(moo, moo->active_context->origin) == moo->_method_context); /* restore the stack pointer */ moo->sp = MOO_OOP_TO_SMOOI(moo->active_context->origin->sp); if (bcode == BCODE_LOCAL_RETURN && moo->active_context != moo->active_context->origin) { SWITCH_ACTIVE_CONTEXT (moo, moo->active_context->origin); } else { SWITCH_ACTIVE_CONTEXT (moo, moo->active_context->origin->sender); } if (unwind_protect) { /* if ensure: is used over a non-local return, it should reach here. * [^10] ensure: [...] */ MOO_STACK_PUSH (moo, (moo_oop_t)unwind_start); MOO_STACK_PUSH (moo, (moo_oop_t)unwind_stop); MOO_STACK_PUSH (moo, (moo_oop_t)return_value); if (send_message(moo, moo->unwindto_return_sym, 2, 0) <= -1) return -1; } else { /* push the return value to the stack of the new active context */ MOO_STACK_PUSH (moo, return_value); if (moo->active_context == moo->initial_context) { /* the new active context is the fake initial context. * this context can't get executed further. */ MOO_ASSERT (moo, (moo_oop_t)moo->active_context->sender == moo->_nil); MOO_ASSERT (moo, MOO_CLASSOF(moo, moo->active_context) == moo->_method_context); MOO_ASSERT (moo, moo->active_context->receiver_or_base == moo->_nil); MOO_ASSERT (moo, moo->active_context == moo->processor->active->initial_context); MOO_ASSERT (moo, moo->active_context->origin == moo->processor->active->initial_context->origin); MOO_ASSERT (moo, moo->active_context->origin == moo->active_context); /* [NOTE] this condition is true for the processified block context also. * moo->active_context->origin == moo->processor->active->initial_context->origin * however, the check here is done after context switching and the * processified block check has been done against the context before switching */ /* the stack contains the final return value so the stack pointer must be 0. */ MOO_ASSERT (moo, moo->sp == 0); if (moo->option.trait & MOO_TRAIT_AWAIT_PROCS) { terminate_process (moo, moo->processor->active); } else { /* graceful termination of the whole vm */ return 0; } /* TODO: store the return value to the VM register. * the caller to moo_execute() can fetch it to return it to the system */ } } } #endif return 1; } static MOO_INLINE void do_return_from_block (moo_t* moo) { MOO_ASSERT (moo, MOO_CLASSOF(moo, moo->active_context) == moo->_block_context); if (moo->active_context == moo->processor->active->initial_context) { /* the active context to return from is an initial context of * the active process. this process must have been created * over a block using the newProcess method. let's terminate * the process. */ MOO_ASSERT (moo, (moo_oop_t)moo->active_context->sender == moo->_nil); terminate_process (moo, moo->processor->active); } else { /* it is a normal block return as the active block context * is not the initial context of a process */ /* the process stack is shared. the return value * doesn't need to get moved. */ SWITCH_ACTIVE_CONTEXT (moo, (moo_oop_context_t)moo->active_context->sender); } } static MOO_INLINE int make_block (moo_t* moo) { moo_oop_block_t block; moo_oob_t b1, b2; /* b1 - number of block arguments * b2 - number of block temporaries */ FETCH_PARAM_CODE_TO (moo, b1); FETCH_PARAM_CODE_TO (moo, b2); LOG_INST2 (moo, "make_block %zu %zu", b1, b2); MOO_ASSERT (moo, b1 >= 0); MOO_ASSERT (moo, b2 >= b1); /* the block context object created here is used as a base * object for block context activation. pf_block_value() * clones a block context and activates the cloned context. * this base block context is created with no stack for * this reason */ block = (moo_oop_block_t)moo_instantiate(moo, moo->_block, MOO_NULL, 0); if (MOO_UNLIKELY(!block)) return -1; /* the long forward jump instruction has the format of * 11000100 KKKKKKKK or 11000100 KKKKKKKK KKKKKKKK * depending on MOO_BCODE_LONG_PARAM_SIZE. change 'ip' to point to * the instruction after the jump. */ block->ip = MOO_SMOOI_TO_OOP(moo->ip + MOO_BCODE_LONG_PARAM_SIZE + 1); /* the number of arguments for a block context is local to the block */ block->nargs = MOO_SMOOI_TO_OOP(b1); /* the number of temporaries here is an accumulated count including * the number of temporaries of a home context */ block->ntmprs = MOO_SMOOI_TO_OOP(b2); /* set the home context where it's defined */ MOO_STORE_OOP (moo, (moo_oop_t*)&block->home, (moo_oop_t)moo->active_context); /* push the new block context to the stack of the active context */ MOO_STACK_PUSH (moo, (moo_oop_t)block); return 0; } static int __execute (moo_t* moo) { moo_oob_t bcode; moo_oow_t b1, b2; moo_oop_t return_value; #if defined(HAVE_LABELS_AS_VALUES) static void* inst_table[256] = { /* import bytecode label addresses */ #include "bct-lab.h" }; # define BEGIN_DISPATCH_LOOP() __begin_inst_dispatch: # define END_DISPATCH_LOOP() __end_inst_dispatch: # define EXIT_DISPATCH_LOOP() goto __end_inst_dispatch # define NEXT_INST() goto __begin_inst_dispatch # define BEGIN_DISPATCH_TABLE() goto *inst_table[bcode]; # define END_DISPATCH_TABLE() # define ON_INST(code) case_ ## code: # define ON_UNKNOWN_INST() case_ ## DEFAULT: #else # define BEGIN_DISPATCH_LOOP() __begin_inst_dispatch: # define END_DISPATCH_LOOP() __end_inst_dispatch: # define EXIT_DISPATCH_LOOP() goto __end_inst_dispatch # define NEXT_INST() goto __begin_inst_dispatch # define BEGIN_DISPATCH_TABLE() switch (bcode) { # define END_DISPATCH_TABLE() } # define ON_INST(code) case code: # define ON_UNKNOWN_INST() default: #endif MOO_ASSERT (moo, moo->active_context != MOO_NULL); /* TODO: initialize semaphore stuffs * sem_heap * sem_io. * sem_list. * these can get dirty if this function is called again esepcially after failure. */ BEGIN_DISPATCH_LOOP() /* stop requested or no more runnable process */ if (moo->abort_req || (!moo->no_proc_switch && switch_process_if_needed(moo) == 0)) EXIT_DISPATCH_LOOP(); #if defined(MOO_DEBUG_VM_EXEC) moo->last_inst_pointer = moo->ip; #endif bcode = FETCH_BYTE_CODE(moo); #if defined(MOO_PROFILE_VM) moo->stat.inst_counter++; #endif /* ==== DISPATCH TABLE ==== */ BEGIN_DISPATCH_TABLE() ON_INST(BCODE_PUSH_INSTVAR_X) FETCH_PARAM_CODE_TO (moo, b1); goto push_instvar; ON_INST(BCODE_PUSH_INSTVAR_0) ON_INST(BCODE_PUSH_INSTVAR_1) ON_INST(BCODE_PUSH_INSTVAR_2) ON_INST(BCODE_PUSH_INSTVAR_3) ON_INST(BCODE_PUSH_INSTVAR_4) ON_INST(BCODE_PUSH_INSTVAR_5) ON_INST(BCODE_PUSH_INSTVAR_6) ON_INST(BCODE_PUSH_INSTVAR_7) b1 = bcode & 0x7; /* low 3 bits */ push_instvar: LOG_INST1 (moo, "push_instvar %zu", b1); MOO_ASSERT (moo, MOO_OBJ_GET_FLAGS_TYPE(moo->active_context->origin->receiver_or_base) == MOO_OBJ_TYPE_OOP); MOO_STACK_PUSH (moo, MOO_OBJ_GET_OOP_VAL(moo->active_context->origin->receiver_or_base, b1)); NEXT_INST(); /* ------------------------------------------------- */ ON_INST(BCODE_STORE_INTO_INSTVAR_X) FETCH_PARAM_CODE_TO (moo, b1); goto store_instvar; ON_INST(BCODE_STORE_INTO_INSTVAR_0) ON_INST(BCODE_STORE_INTO_INSTVAR_1) ON_INST(BCODE_STORE_INTO_INSTVAR_2) ON_INST(BCODE_STORE_INTO_INSTVAR_3) ON_INST(BCODE_STORE_INTO_INSTVAR_4) ON_INST(BCODE_STORE_INTO_INSTVAR_5) ON_INST(BCODE_STORE_INTO_INSTVAR_6) ON_INST(BCODE_STORE_INTO_INSTVAR_7) b1 = bcode & 0x7; /* low 3 bits */ store_instvar: LOG_INST1 (moo, "store_into_instvar %zu", b1); MOO_ASSERT (moo, MOO_OBJ_GET_FLAGS_TYPE(moo->active_context->receiver_or_base) == MOO_OBJ_TYPE_OOP); MOO_STORE_OOP (moo, MOO_OBJ_GET_OOP_PTR(moo->active_context->origin->receiver_or_base, b1), MOO_STACK_GETTOP(moo)); NEXT_INST(); /* ------------------------------------------------- */ ON_INST(BCODE_POP_INTO_INSTVAR_X) FETCH_PARAM_CODE_TO (moo, b1); goto pop_into_instvar; ON_INST(BCODE_POP_INTO_INSTVAR_0) ON_INST(BCODE_POP_INTO_INSTVAR_1) ON_INST(BCODE_POP_INTO_INSTVAR_2) ON_INST(BCODE_POP_INTO_INSTVAR_3) ON_INST(BCODE_POP_INTO_INSTVAR_4) ON_INST(BCODE_POP_INTO_INSTVAR_5) ON_INST(BCODE_POP_INTO_INSTVAR_6) ON_INST(BCODE_POP_INTO_INSTVAR_7) b1 = bcode & 0x7; /* low 3 bits */ pop_into_instvar: LOG_INST1 (moo, "pop_into_instvar %zu", b1); MOO_ASSERT (moo, MOO_OBJ_GET_FLAGS_TYPE(moo->active_context->receiver_or_base) == MOO_OBJ_TYPE_OOP); MOO_STORE_OOP (moo, MOO_OBJ_GET_OOP_PTR(moo->active_context->origin->receiver_or_base, b1), MOO_STACK_GETTOP(moo)); MOO_STACK_POP (moo); NEXT_INST(); /* ------------------------------------------------- */ ON_INST(BCODE_PUSH_TEMPVAR_X) ON_INST(BCODE_STORE_INTO_TEMPVAR_X) ON_INST(BCODE_POP_INTO_TEMPVAR_X) FETCH_PARAM_CODE_TO (moo, b1); goto handle_tempvar; ON_INST(BCODE_PUSH_TEMPVAR_0) ON_INST(BCODE_PUSH_TEMPVAR_1) ON_INST(BCODE_PUSH_TEMPVAR_2) ON_INST(BCODE_PUSH_TEMPVAR_3) ON_INST(BCODE_PUSH_TEMPVAR_4) ON_INST(BCODE_PUSH_TEMPVAR_5) ON_INST(BCODE_PUSH_TEMPVAR_6) ON_INST(BCODE_PUSH_TEMPVAR_7) ON_INST(BCODE_STORE_INTO_TEMPVAR_0) ON_INST(BCODE_STORE_INTO_TEMPVAR_1) ON_INST(BCODE_STORE_INTO_TEMPVAR_2) ON_INST(BCODE_STORE_INTO_TEMPVAR_3) ON_INST(BCODE_STORE_INTO_TEMPVAR_4) ON_INST(BCODE_STORE_INTO_TEMPVAR_5) ON_INST(BCODE_STORE_INTO_TEMPVAR_6) ON_INST(BCODE_STORE_INTO_TEMPVAR_7) ON_INST(BCODE_POP_INTO_TEMPVAR_0) ON_INST(BCODE_POP_INTO_TEMPVAR_1) ON_INST(BCODE_POP_INTO_TEMPVAR_2) ON_INST(BCODE_POP_INTO_TEMPVAR_3) ON_INST(BCODE_POP_INTO_TEMPVAR_4) ON_INST(BCODE_POP_INTO_TEMPVAR_5) ON_INST(BCODE_POP_INTO_TEMPVAR_6) ON_INST(BCODE_POP_INTO_TEMPVAR_7) { moo_oop_context_t ctx; moo_ooi_t bx; b1 = bcode & 0x7; /* low 3 bits */ handle_tempvar: #if defined(MOO_USE_CTXTEMPVAR) /* when CTXTEMPVAR inststructions are used, the above * instructions are used only for temporary access * outside a block. i can assume that the temporary * variable index is pointing to one of temporaries * in the relevant method context */ ctx = moo->active_context->origin; bx = b1; MOO_ASSERT (moo, MOO_CLASSOF(moo, ctx) == moo->_method_context); #else /* otherwise, the index may point to a temporaries declared inside a block */ if (moo->active_context->home != moo->_nil) { /* this code assumes that the method context and * the block context place some key fields in the * same offset. such fields include 'home', 'ntmprs' */ moo_oop_t home; moo_ooi_t home_ntmprs; ctx = moo->active_context; home = ctx->home; do { /* ntmprs contains the number of defined temporaries * including those defined in the home context */ home_ntmprs = MOO_OOP_TO_SMOOI(((moo_oop_context_t)home)->ntmprs); if (b1 >= home_ntmprs) break; ctx = (moo_oop_context_t)home; home = ((moo_oop_context_t)home)->home; if (home == moo->_nil) { home_ntmprs = 0; break; } } while (1); /* bx is the actual index within the actual context * containing the temporary */ bx = b1 - home_ntmprs; } else { ctx = moo->active_context; bx = b1; } #endif if ((bcode >> 4) & 1) { /* push - bit 4 on */ LOG_INST1 (moo, "push_tempvar %zu", b1); MOO_STACK_PUSH (moo, ctx->stack[bx]); } else { /* store or pop - bit 5 off */ MOO_STORE_OOP (moo, &ctx->stack[bx], MOO_STACK_GETTOP(moo)); if ((bcode >> 3) & 1) { /* pop - bit 3 on */ LOG_INST1 (moo, "pop_into_tempvar %zu", b1); MOO_STACK_POP (moo); } else { LOG_INST1 (moo, "store_into_tempvar %zu", b1); } } NEXT_INST(); } /* ------------------------------------------------- */ ON_INST(BCODE_PUSH_LITERAL_X) FETCH_PARAM_CODE_TO (moo, b1); goto push_literal; ON_INST(BCODE_PUSH_LITERAL_0) ON_INST(BCODE_PUSH_LITERAL_1) ON_INST(BCODE_PUSH_LITERAL_2) ON_INST(BCODE_PUSH_LITERAL_3) ON_INST(BCODE_PUSH_LITERAL_4) ON_INST(BCODE_PUSH_LITERAL_5) ON_INST(BCODE_PUSH_LITERAL_6) ON_INST(BCODE_PUSH_LITERAL_7) b1 = bcode & 0x7; /* low 3 bits */ push_literal: LOG_INST1 (moo, "push_literal @%zu", b1); MOO_STACK_PUSH (moo, moo->active_method->literal_frame[b1]); NEXT_INST(); /* ------------------------------------------------- */ ON_INST(BCODE_PUSH_OBJECT_X) ON_INST(BCODE_STORE_INTO_OBJECT_X) ON_INST(BCODE_POP_INTO_OBJECT_X) FETCH_PARAM_CODE_TO (moo, b1); goto handle_object; ON_INST(BCODE_PUSH_OBJECT_0) ON_INST(BCODE_PUSH_OBJECT_1) ON_INST(BCODE_PUSH_OBJECT_2) ON_INST(BCODE_PUSH_OBJECT_3) ON_INST(BCODE_STORE_INTO_OBJECT_0) ON_INST(BCODE_STORE_INTO_OBJECT_1) ON_INST(BCODE_STORE_INTO_OBJECT_2) ON_INST(BCODE_STORE_INTO_OBJECT_3) ON_INST(BCODE_POP_INTO_OBJECT_0) ON_INST(BCODE_POP_INTO_OBJECT_1) ON_INST(BCODE_POP_INTO_OBJECT_2) ON_INST(BCODE_POP_INTO_OBJECT_3) { moo_oop_association_t ass; b1 = bcode & 0x3; /* low 2 bits */ handle_object: ass = (moo_oop_association_t)moo->active_method->literal_frame[b1]; MOO_ASSERT (moo, MOO_CLASSOF(moo, ass) == moo->_association); if ((bcode >> 3) & 1) { /* store or pop */ MOO_STORE_OOP (moo, &ass->value, MOO_STACK_GETTOP(moo)); if ((bcode >> 2) & 1) { /* pop */ LOG_INST1 (moo, "pop_into_object @%zu", b1); MOO_STACK_POP (moo); } else { LOG_INST1 (moo, "store_into_object @%zu", b1); } } else { /* push */ LOG_INST1 (moo, "push_object @%zu", b1); MOO_STACK_PUSH (moo, ass->value); } NEXT_INST(); } /* -------------------------------------------------------- */ ON_INST(BCODE_JUMP_FORWARD) /* 0xC4 */ FETCH_PARAM_CODE_TO (moo, b1); LOG_INST1 (moo, "jump_forward %zu", b1); moo->ip += b1; NEXT_INST(); ON_INST(BCODE_JUMP2_FORWARD) /* 0xC5 */ FETCH_PARAM_CODE_TO (moo, b1); LOG_INST1 (moo, "jump2_forward %zu", b1); moo->ip += MAX_CODE_JUMP + b1; NEXT_INST(); ON_INST(BCODE_JUMP_FORWARD_IF_TRUE) /* 0xC6 */ FETCH_PARAM_CODE_TO (moo, b1); LOG_INST1 (moo, "jump_forward_if_true %zu", b1); if (MOO_STACK_GETTOP(moo) != moo->_false) moo->ip += b1; NEXT_INST(); ON_INST(BCODE_JUMP2_FORWARD_IF_TRUE) /* 0xC7 */ FETCH_PARAM_CODE_TO (moo, b1); LOG_INST1 (moo, "jump2_forward_if_true %zu", b1); if (MOO_STACK_GETTOP(moo) != moo->_false) moo->ip += MAX_CODE_JUMP + b1; NEXT_INST(); ON_INST(BCODE_JUMP_FORWARD_IF_FALSE) /* 0xC8 */ FETCH_PARAM_CODE_TO (moo, b1); LOG_INST1 (moo, "jump_forward_if_false %zu", b1); if (MOO_STACK_GETTOP(moo) == moo->_false) moo->ip += b1; NEXT_INST(); ON_INST(BCODE_JUMP2_FORWARD_IF_FALSE) /* 0xC9 */ FETCH_PARAM_CODE_TO (moo, b1); LOG_INST1 (moo, "jump2_forward_if_false %zu", b1); if (MOO_STACK_GETTOP(moo) == moo->_false) moo->ip += MAX_CODE_JUMP + b1; NEXT_INST(); /* -- */ ON_INST(BCODE_JMPOP_FORWARD_IF_TRUE) FETCH_PARAM_CODE_TO (moo, b1); LOG_INST1 (moo, "jmpop_forward_if_true %zu", b1); if (MOO_STACK_GETTOP(moo) != moo->_false) moo->ip += b1; MOO_STACK_POP (moo); NEXT_INST(); ON_INST(BCODE_JMPOP2_FORWARD_IF_TRUE) FETCH_PARAM_CODE_TO (moo, b1); LOG_INST1 (moo, "jmpop2_forward_if_true %zu", b1); if (MOO_STACK_GETTOP(moo) != moo->_false) moo->ip += MAX_CODE_JUMP + b1; MOO_STACK_POP (moo); NEXT_INST(); ON_INST(BCODE_JMPOP_FORWARD_IF_FALSE) FETCH_PARAM_CODE_TO (moo, b1); LOG_INST1 (moo, "jmpop_forward_if_false %zu", b1); if (MOO_STACK_GETTOP(moo) == moo->_false) moo->ip += b1; MOO_STACK_POP (moo); NEXT_INST(); ON_INST(BCODE_JMPOP2_FORWARD_IF_FALSE) FETCH_PARAM_CODE_TO (moo, b1); LOG_INST1 (moo, "jmpop2_forward_if_false %zu", b1); if (MOO_STACK_GETTOP(moo) == moo->_false) moo->ip += MAX_CODE_JUMP + b1; MOO_STACK_POP (moo); NEXT_INST(); /* -- */ ON_INST(BCODE_JUMP_BACKWARD) /* 0xCE */ FETCH_PARAM_CODE_TO (moo, b1); LOG_INST1 (moo, "jump_backward %zu", b1); moo->ip -= b1; NEXT_INST(); ON_INST(BCODE_JUMP2_BACKWARD) /* 0xCF */ FETCH_PARAM_CODE_TO (moo, b1); LOG_INST1 (moo, "jump2_backward %zu", b1); moo->ip -= MAX_CODE_JUMP + b1; NEXT_INST(); ON_INST(BCODE_JUMP_BACKWARD_IF_TRUE) /* 0xD0 */ FETCH_PARAM_CODE_TO (moo, b1); LOG_INST1 (moo, "jump_backward_if_true %zu", b1); if (MOO_STACK_GETTOP(moo) != moo->_false) moo->ip -= b1; NEXT_INST(); ON_INST(BCODE_JUMP2_BACKWARD_IF_TRUE) /* 0xD1 */ FETCH_PARAM_CODE_TO (moo, b1); LOG_INST1 (moo, "jump2_backward_if_true %zu", b1); if (MOO_STACK_GETTOP(moo) != moo->_false) moo->ip -= MAX_CODE_JUMP + b1; NEXT_INST(); ON_INST(BCODE_JUMP_BACKWARD_IF_FALSE) /* 0xD2 */ FETCH_PARAM_CODE_TO (moo, b1); LOG_INST1 (moo, "jump_backward_if_false %zu", b1); if (MOO_STACK_GETTOP(moo) == moo->_false) moo->ip -= b1; NEXT_INST(); ON_INST(BCODE_JUMP2_BACKWARD_IF_FALSE) /* 0xD3 */ FETCH_PARAM_CODE_TO (moo, b1); LOG_INST1 (moo, "jump2_backward_if_false %zu", b1); if (MOO_STACK_GETTOP(moo) == moo->_false) moo->ip -= MAX_CODE_JUMP + b1; NEXT_INST(); /* -- */ ON_INST(BCODE_JMPOP2_BACKWARD_IF_TRUE) FETCH_PARAM_CODE_TO (moo, b1); LOG_INST1 (moo, "jmpop2_backward_if_true %zu", b1); if (MOO_STACK_GETTOP(moo) != moo->_false) moo->ip -= MAX_CODE_JUMP + b1; MOO_STACK_POP (moo); NEXT_INST(); ON_INST(BCODE_JMPOP_BACKWARD_IF_TRUE) FETCH_PARAM_CODE_TO (moo, b1); LOG_INST1 (moo, "jmpop_backward_if_true %zu", b1); if (MOO_STACK_GETTOP(moo) != moo->_false) moo->ip -= b1; MOO_STACK_POP (moo); NEXT_INST(); ON_INST(BCODE_JMPOP_BACKWARD_IF_FALSE) FETCH_PARAM_CODE_TO (moo, b1); LOG_INST1 (moo, "jmpop_backward_if_false %zu", b1); if (MOO_STACK_GETTOP(moo) == moo->_false) moo->ip -= b1; MOO_STACK_POP (moo); NEXT_INST(); ON_INST(BCODE_JMPOP2_BACKWARD_IF_FALSE) FETCH_PARAM_CODE_TO (moo, b1); LOG_INST1 (moo, "jmpop2_backward_if_false %zu", b1); if (MOO_STACK_GETTOP(moo) == moo->_false) moo->ip -= MAX_CODE_JUMP + b1; MOO_STACK_POP (moo); NEXT_INST(); /* -------------------------------------------------------- */ ON_INST(BCODE_PUSH_CTXTEMPVAR_X) ON_INST(BCODE_STORE_INTO_CTXTEMPVAR_X) ON_INST(BCODE_POP_INTO_CTXTEMPVAR_X) FETCH_PARAM_CODE_TO (moo, b1); FETCH_PARAM_CODE_TO (moo, b2); goto handle_ctxtempvar; ON_INST(BCODE_PUSH_CTXTEMPVAR_0) ON_INST(BCODE_PUSH_CTXTEMPVAR_1) ON_INST(BCODE_PUSH_CTXTEMPVAR_2) ON_INST(BCODE_PUSH_CTXTEMPVAR_3) ON_INST(BCODE_STORE_INTO_CTXTEMPVAR_0) ON_INST(BCODE_STORE_INTO_CTXTEMPVAR_1) ON_INST(BCODE_STORE_INTO_CTXTEMPVAR_2) ON_INST(BCODE_STORE_INTO_CTXTEMPVAR_3) ON_INST(BCODE_POP_INTO_CTXTEMPVAR_0) ON_INST(BCODE_POP_INTO_CTXTEMPVAR_1) ON_INST(BCODE_POP_INTO_CTXTEMPVAR_2) ON_INST(BCODE_POP_INTO_CTXTEMPVAR_3) { moo_ooi_t i; moo_oop_context_t ctx; b1 = bcode & 0x3; /* low 2 bits */ b2 = FETCH_BYTE_CODE(moo); handle_ctxtempvar: ctx = moo->active_context; MOO_ASSERT (moo, (moo_oop_t)ctx != moo->_nil); for (i = 0; i < b1; i++) { ctx = (moo_oop_context_t)ctx->home; } if ((bcode >> 3) & 1) { /* store or pop */ MOO_STORE_OOP (moo, &ctx->stack[b2], MOO_STACK_GETTOP(moo)); if ((bcode >> 2) & 1) { /* pop */ MOO_STACK_POP (moo); LOG_INST2 (moo, "pop_into_ctxtempvar %zu %zu", b1, b2); } else { LOG_INST2 (moo, "store_into_ctxtempvar %zu %zu", b1, b2); } } else { /* push */ MOO_STACK_PUSH (moo, ctx->stack[b2]); LOG_INST2 (moo, "push_ctxtempvar %zu %zu", b1, b2); } NEXT_INST(); } /* -------------------------------------------------------- */ ON_INST(BCODE_PUSH_OBJVAR_X) ON_INST(BCODE_STORE_INTO_OBJVAR_X) ON_INST(BCODE_POP_INTO_OBJVAR_X) FETCH_PARAM_CODE_TO (moo, b1); FETCH_PARAM_CODE_TO (moo, b2); goto handle_objvar; ON_INST(BCODE_PUSH_OBJVAR_0) ON_INST(BCODE_PUSH_OBJVAR_1) ON_INST(BCODE_PUSH_OBJVAR_2) ON_INST(BCODE_PUSH_OBJVAR_3) ON_INST(BCODE_STORE_INTO_OBJVAR_0) ON_INST(BCODE_STORE_INTO_OBJVAR_1) ON_INST(BCODE_STORE_INTO_OBJVAR_2) ON_INST(BCODE_STORE_INTO_OBJVAR_3) ON_INST(BCODE_POP_INTO_OBJVAR_0) ON_INST(BCODE_POP_INTO_OBJVAR_1) ON_INST(BCODE_POP_INTO_OBJVAR_2) ON_INST(BCODE_POP_INTO_OBJVAR_3) { moo_oop_oop_t t; /* b1 -> variable index to the object indicated by b2. * b2 -> object index stored in the literal frame. */ b1 = bcode & 0x3; /* low 2 bits */ b2 = FETCH_BYTE_CODE(moo); handle_objvar: t = (moo_oop_oop_t)moo->active_method->literal_frame[b2]; MOO_ASSERT (moo, MOO_OBJ_GET_FLAGS_TYPE(t) == MOO_OBJ_TYPE_OOP); MOO_ASSERT (moo, b1 < MOO_OBJ_GET_SIZE(t)); if ((bcode >> 3) & 1) { /* store or pop */ MOO_STORE_OOP (moo, MOO_OBJ_GET_OOP_PTR(t, b1), MOO_STACK_GETTOP(moo)); if ((bcode >> 2) & 1) { /* pop */ MOO_STACK_POP (moo); LOG_INST2 (moo, "pop_into_objvar %zu %zu", b1, b2); } else { LOG_INST2 (moo, "store_into_objvar %zu %zu", b1, b2); } } else { /* push */ LOG_INST2 (moo, "push_objvar %zu %zu", b1, b2); MOO_STACK_PUSH (moo, MOO_OBJ_GET_OOP_VAL(t, b1)); } NEXT_INST(); } /* -------------------------------------------------------- */ ON_INST(BCODE_SEND_MESSAGE_X) ON_INST(BCODE_SEND_MESSAGE_TO_SUPER_X) /* b1 -> number of arguments * b2 -> selector index stored in the literal frame */ FETCH_PARAM_CODE_TO (moo, b1); FETCH_PARAM_CODE_TO (moo, b2); goto handle_send_message; ON_INST(BCODE_SEND_MESSAGE_0) ON_INST(BCODE_SEND_MESSAGE_1) ON_INST(BCODE_SEND_MESSAGE_2) ON_INST(BCODE_SEND_MESSAGE_3) ON_INST(BCODE_SEND_MESSAGE_TO_SUPER_0) ON_INST(BCODE_SEND_MESSAGE_TO_SUPER_1) ON_INST(BCODE_SEND_MESSAGE_TO_SUPER_2) ON_INST(BCODE_SEND_MESSAGE_TO_SUPER_3) { moo_oop_char_t selector; b1 = bcode & 0x3; /* low 2 bits */ b2 = FETCH_BYTE_CODE(moo); handle_send_message: /* get the selector from the literal frame */ selector = (moo_oop_char_t)moo->active_method->literal_frame[b2]; /* if the compiler is not buggy or the byte code gets corrupted, the selector is guaranteed to be a symbol */ LOG_INST3 (moo, "send_message%hs %zu @%zu", (((bcode >> 2) & 1)? "_to_super": ""), b1, b2); if (send_message(moo, selector, b1, ((bcode >> 2) & 1)) <= -1) return -1; NEXT_INST(); } /* -------------------------------------------------------- */ ON_INST(BCODE_PUSH_RECEIVER) /* push self or super */ LOG_INST0 (moo, "push_receiver"); MOO_STACK_PUSH (moo, moo->active_context->origin->receiver_or_base); NEXT_INST(); ON_INST(BCODE_PUSH_NIL) LOG_INST0 (moo, "push_nil"); MOO_STACK_PUSH (moo, moo->_nil); NEXT_INST(); ON_INST(BCODE_PUSH_TRUE) LOG_INST0 (moo, "push_true"); MOO_STACK_PUSH (moo, moo->_true); NEXT_INST(); ON_INST(BCODE_PUSH_FALSE) LOG_INST0 (moo, "push_false"); MOO_STACK_PUSH (moo, moo->_false); NEXT_INST(); ON_INST(BCODE_PUSH_CONTEXT) LOG_INST0 (moo, "push_context"); MOO_STACK_PUSH (moo, (moo_oop_t)moo->active_context); NEXT_INST(); ON_INST(BCODE_PUSH_PROCESS) LOG_INST0 (moo, "push_process"); MOO_STACK_PUSH (moo, (moo_oop_t)moo->processor->active); NEXT_INST(); ON_INST(BCODE_PUSH_RECEIVER_NS) { register moo_oop_t c; LOG_INST0 (moo, "push_receiver_ns"); c = (moo_oop_t)MOO_CLASSOF(moo, moo->active_context->origin->receiver_or_base); if (c == (moo_oop_t)moo->_class) c = moo->active_context->origin->receiver_or_base; MOO_STACK_PUSH (moo, (moo_oop_t)((moo_oop_class_t)c)->nsup); NEXT_INST(); } ON_INST(BCODE_PUSH_NEGONE) LOG_INST0 (moo, "push_negone"); MOO_STACK_PUSH (moo, MOO_SMOOI_TO_OOP(-1)); NEXT_INST(); ON_INST(BCODE_PUSH_ZERO) LOG_INST0 (moo, "push_zero"); MOO_STACK_PUSH (moo, MOO_SMOOI_TO_OOP(0)); NEXT_INST(); ON_INST(BCODE_PUSH_ONE) LOG_INST0 (moo, "push_one"); MOO_STACK_PUSH (moo, MOO_SMOOI_TO_OOP(1)); NEXT_INST(); ON_INST(BCODE_PUSH_TWO) LOG_INST0 (moo, "push_two"); MOO_STACK_PUSH (moo, MOO_SMOOI_TO_OOP(2)); NEXT_INST(); ON_INST(BCODE_PUSH_INTLIT) FETCH_PARAM_CODE_TO (moo, b1); LOG_INST1 (moo, "push_intlit %zu", b1); MOO_STACK_PUSH (moo, MOO_SMOOI_TO_OOP(b1)); NEXT_INST(); ON_INST(BCODE_PUSH_NEGINTLIT) { moo_ooi_t num; FETCH_PARAM_CODE_TO (moo, b1); num = b1; LOG_INST1 (moo, "push_negintlit %zu", b1); MOO_STACK_PUSH (moo, MOO_SMOOI_TO_OOP(-num)); NEXT_INST(); } ON_INST(BCODE_PUSH_CHARLIT) FETCH_PARAM_CODE_TO (moo, b1); LOG_INST1 (moo, "push_charlit %zu", b1); MOO_STACK_PUSH (moo, MOO_CHAR_TO_OOP(b1)); NEXT_INST(); /* -------------------------------------------------------- */ ON_INST(BCODE_MAKE_DICTIONARY) FETCH_PARAM_CODE_TO (moo, b1); LOG_INST1 (moo, "make_dictionary %zu", b1); /* Dictionary new: b1 * doing this allows users to redefine Dictionary whatever way they like. * if i did the followings instead, the internal of Dictionary would get * tied to the system dictionary implementation. the system dictionary * implementation is flawed in that it accepts only a variable character * object as a key. it's better to invoke 'Dictionary new: ...'. t = (moo_oop_t)moo_makedic (moo, moo->_dictionary, b1 + 10); MOO_STACK_PUSH (moo, t); */ MOO_STACK_PUSH (moo, (moo_oop_t)moo->_dictionary); MOO_STACK_PUSH (moo, MOO_SMOOI_TO_OOP(b1)); if (send_message(moo, moo->dicnewsym, 1, 0) <= -1) return -1; NEXT_INST(); ON_INST(BCODE_POP_INTO_DICTIONARY) LOG_INST0 (moo, "pop_into_dictionary"); /* dic __put_assoc: assoc * whether the system dictinoary implementation is flawed or not, * the code would look like this if it were used. t1 = MOO_STACK_GETTOP(moo); MOO_STACK_POP (moo); t2 = MOO_STACK_GETTOP(moo); moo_putatdic (moo, (moo_oop_dic_t)t2, ((moo_oop_association_t)t1)->key, ((moo_oop_association_t)t1)->value); */ if (send_message(moo, moo->dicputassocsym, 1, 0) <= -1) return -1; NEXT_INST(); ON_INST(BCODE_MAKE_ARRAY) { moo_oop_t t; FETCH_PARAM_CODE_TO (moo, b1); LOG_INST1 (moo, "make_array %zu", b1); /* create an empty array */ t = moo_instantiate(moo, moo->_array, MOO_NULL, b1); if (!t) return -1; MOO_STACK_PUSH (moo, t); /* push the array created */ NEXT_INST(); } ON_INST(BCODE_POP_INTO_ARRAY) { moo_oop_t t1, t2; FETCH_PARAM_CODE_TO (moo, b1); LOG_INST1 (moo, "pop_into_array %zu", b1); t1 = MOO_STACK_GETTOP(moo); MOO_STACK_POP (moo); t2 = MOO_STACK_GETTOP(moo); MOO_STORE_OOP (moo, MOO_OBJ_GET_OOP_PTR(t2, b1), t1); NEXT_INST(); } ON_INST(BCODE_MAKE_BYTEARRAY) { moo_oop_t t; FETCH_PARAM_CODE_TO (moo, b1); LOG_INST1 (moo, "make_bytearray %zu", b1); /* create an empty array */ t = moo_instantiate(moo, moo->_byte_array, MOO_NULL, b1); if (!t) return -1; MOO_STACK_PUSH (moo, t); /* push the array created */ NEXT_INST(); } ON_INST(BCODE_POP_INTO_BYTEARRAY) { moo_oop_t t1, t2; moo_uint8_t bv; FETCH_PARAM_CODE_TO (moo, b1); LOG_INST1 (moo, "pop_into_bytearray %zu", b1); t1 = MOO_STACK_GETTOP(moo); MOO_STACK_POP (moo); t2 = MOO_STACK_GETTOP(moo); switch (MOO_OOP_GET_TAG(t1)) { case MOO_OOP_TAG_SMOOI: bv = MOO_OOP_TO_SMOOI(t1) & 0xFF; break; case MOO_OOP_TAG_SMPTR: bv = ((moo_oow_t)MOO_OOP_TO_SMPTR(t1) & 0xFF); break; case MOO_OOP_TAG_CHAR: bv = MOO_OOP_TO_CHAR(t1) & 0xFF; break; case MOO_OOP_TAG_ERROR: bv = MOO_OOP_TO_ERROR(t1) & 0xFF; break; default: /* well, allowing these into a byte array may look a bit awkward. * mostly i take the low 1 byte of the first unit of the first element * and store it at the given position. * note that a byte array literal composed by the compiler itself * treat these differently. */ if (t1 == moo->_nil) bv = 0; else if (t1 == moo->_true) bv = 1; else if (t1 == moo->_false) bv = 0; else { MOO_ASSERT (moo, MOO_OOP_IS_POINTER(t1)); switch (MOO_OBJ_GET_FLAGS_TYPE(t1)) { case MOO_OBJ_TYPE_BYTE: bv = (MOO_OBJ_GET_SIZE(t1) > 0)? (MOO_OBJ_GET_BYTE_VAL(t1, 0) & 0xFF): 0; break; case MOO_OBJ_TYPE_CHAR: bv = (MOO_OBJ_GET_SIZE(t1) > 0)? (MOO_OBJ_GET_CHAR_VAL(t1, 0) & 0xFF): 0; break; case MOO_OBJ_TYPE_HALFWORD: bv = (MOO_OBJ_GET_SIZE(t1) > 0)? (MOO_OBJ_GET_HALFWORD_VAL(t1, 0) & 0xFF): 0; break; case MOO_OBJ_TYPE_WORD: bv = (MOO_OBJ_GET_SIZE(t1) > 0)? (MOO_OBJ_GET_WORD_VAL(t1, 0) & 0xFF): 0; break; default: bv = 0; break; } } break; } MOO_OBJ_SET_BYTE_VAL(t2, b1, bv); NEXT_INST(); } ON_INST(BCODE_DUP_STACKTOP) { moo_oop_t t; LOG_INST0 (moo, "dup_stacktop"); MOO_ASSERT (moo, !MOO_STACK_ISEMPTY(moo)); t = MOO_STACK_GETTOP(moo); MOO_STACK_PUSH (moo, t); NEXT_INST(); } ON_INST(BCODE_POP_STACKTOP) LOG_INST0 (moo, "pop_stacktop"); MOO_ASSERT (moo, !MOO_STACK_ISEMPTY(moo)); MOO_STACK_POP (moo); NEXT_INST(); ON_INST(BCODE_RETURN_STACKTOP) LOG_INST0 (moo, "return_stacktop"); return_value = MOO_STACK_GETTOP(moo); MOO_STACK_POP (moo); goto handle_return; ON_INST(BCODE_RETURN_RECEIVER) LOG_INST0 (moo, "return_receiver"); return_value = moo->active_context->origin->receiver_or_base; handle_return: { int n; if ((n = do_return(moo, bcode, return_value)) <= -1) return -1; if (n == 0) EXIT_DISPATCH_LOOP(); } NEXT_INST(); ON_INST(BCODE_LOCAL_RETURN) LOG_INST0 (moo, "local_return"); return_value = MOO_STACK_GETTOP(moo); MOO_STACK_POP (moo); goto handle_return; ON_INST(BCODE_RETURN_FROM_BLOCK) LOG_INST0 (moo, "return_from_block"); do_return_from_block (moo); NEXT_INST(); ON_INST(BCODE_MAKE_BLOCK) if (make_block(moo) <= -1) return -1; NEXT_INST(); ON_INST(BCODE_NOOP) /* do nothing */ LOG_INST0 (moo, "noop"); NEXT_INST(); ON_UNKNOWN_INST() MOO_LOG1 (moo, MOO_LOG_IC | MOO_LOG_FATAL, "Fatal error - unknown byte code 0x%zx\n", bcode); moo_seterrnum (moo, MOO_EINTERN); return -1; END_DISPATCH_TABLE () /* ==== END OF DISPATCH TABLE ==== */ END_DISPATCH_LOOP() return (moo->abort_req <= -1)? -1: 0; } int moo_execute (moo_t* moo) { int n; int log_default_type_mask; log_default_type_mask = moo->log.default_type_mask; moo->log.default_type_mask |= MOO_LOG_VM; if (vm_startup(moo) <= -1) return -1; moo->proc_switched = 0; moo->abort_req = 0; #if defined(MOO_ENABLE_GC_MARK_SWEEP) moo->gci.lazy_sweep = 1; /* TODO: make it configurable?? */ MOO_INIT_NTIME (&moo->gci.stat.alloc, 0, 0); MOO_INIT_NTIME (&moo->gci.stat.mark, 0, 0); MOO_INIT_NTIME (&moo->gci.stat.sweep, 0, 0); #endif n = __execute (moo); #if defined(MOO_ENABLE_GC_MARK_SWEEP) moo->gci.lazy_sweep = 0; #endif vm_cleanup (moo); moo->log.default_type_mask = log_default_type_mask; return n; } void moo_abort (moo_t* moo) { moo->abort_req = 1; } #if defined(MOO_PROFILE_VM) && defined(MOO_ENABLE_GC_MARK_SWEEP) static void xma_dumper (void* ctx, const char* fmt, ...) { va_list ap; va_start (ap, fmt); moo_logbfmtv ((moo_t*)ctx, MOO_LOG_IC | MOO_LOG_INFO, fmt, ap); va_end (ap); } #endif int moo_invoke (moo_t* moo, const moo_oocs_t* objname, const moo_oocs_t* mthname) { int n; MOO_ASSERT (moo, moo->initial_context == MOO_NULL); MOO_ASSERT (moo, moo->active_context == MOO_NULL); MOO_ASSERT (moo, moo->active_method == MOO_NULL); #if defined(MOO_PROFILE_VM) moo->stat.inst_counter = 0; moo->stat.method_cache_hits = 0; moo->stat.method_cache_misses = 0; moo->stat.message_sends = 0; #endif moo_clearmethodcache (moo); #if 0 /* unless the system is buggy, moo->proc_map_used should be 0. * the standard library terminates all processes before halting. * * [EXPERIMENTAL] * if you like the process allocation to start from 0, uncomment * the following 'if' block */ if (moo->proc_map_capa > 0 && moo->proc_map_used == 0) { /* rechain the process map. it must be compatible with prepare_to_alloc_pid(). * by placing the low indiced slot at the beginning of the free list, * the special processes (main_proc, gcfin_proc, ossig_proc) are allocated * with low process IDs. */ moo_ooi_t i, j; moo->proc_map_free_first = 0; for (i = 0, j = 1; j < moo->proc_map_capa; i++, j++) { moo->proc_map[i] = MOO_SMOOI_TO_OOP(j); } moo->proc_map[i] = MOO_SMOOI_TO_OOP(-1); moo->proc_map_free_last = i; } #endif if (start_initial_process_and_context(moo, objname, mthname) <= -1) return -1; moo->initial_context = moo->processor->active->initial_context; n = moo_execute(moo); /* TODO: reset processor fields. set processor->tally to zero. processor->active to nil_process... */ moo->initial_context = MOO_NULL; moo->active_context = MOO_NULL; moo->active_method = MOO_NULL; #if defined(MOO_PROFILE_VM) MOO_LOG1 (moo, MOO_LOG_IC | MOO_LOG_INFO, "Total message sends: %zu\n", moo->stat.message_sends); MOO_LOG2 (moo, MOO_LOG_IC | MOO_LOG_INFO, "Method cache - hits: %zu, misses: %zu\n", moo->stat.method_cache_hits, moo->stat.method_cache_misses); MOO_LOG1 (moo, MOO_LOG_IC | MOO_LOG_INFO, "Total instructions: %zu\n", moo->stat.inst_counter); #if defined(MOO_ENABLE_GC_MARK_SWEEP) if (moo->gc_type == MOO_GC_TYPE_MARK_SWEEP) { MOO_LOG2 (moo, MOO_LOG_IC | MOO_LOG_INFO, "GC - gci.bsz: %zu, gci.stack.max: %zu\n", moo->gci.bsz, moo->gci.stack.max); if (moo->heap->xma) moo_xma_dump (moo->heap->xma, xma_dumper, moo); } MOO_LOG2 (moo, MOO_LOG_IC | MOO_LOG_INFO, "GC - gci.stat.alloc: %ld.%09u\n", (unsigned long int)moo->gci.stat.alloc.sec, (unsigned int)moo->gci.stat.alloc.nsec); MOO_LOG2 (moo, MOO_LOG_IC | MOO_LOG_INFO, "GC - gci.stat.mark: %ld.%09u\n", (unsigned long int)moo->gci.stat.mark.sec, (unsigned int)moo->gci.stat.mark.nsec); MOO_LOG2 (moo, MOO_LOG_IC | MOO_LOG_INFO, "GC - gci.stat.sweep: %ld.%09u\n", (unsigned long int)moo->gci.stat.sweep.sec, (unsigned int)moo->gci.stat.sweep.nsec); #endif #endif return n; } #if 0 int moo_invoke (moo_t* moo, const moo_oocs_t* objname, const moo_oocs_t* mthname) { /* TODO: .... */ /* call * System initializeClasses * and invoke * objname mthname */ } #endif