diff --git a/moo/configure b/moo/configure index f22a7bb..698052a 100755 --- a/moo/configure +++ b/moo/configure @@ -18687,8 +18687,8 @@ fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking computed goto usability" >&5 -$as_echo_n "checking computed goto usability... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking labels as values" >&5 +$as_echo_n "checking labels as values... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -18704,7 +18704,7 @@ if ac_fn_c_try_compile "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } -$as_echo "#define HAVE_COMPUTED_GOTO 1" >>confdefs.h +$as_echo "#define HAVE_LABELS_AS_VALUES 1" >>confdefs.h else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 diff --git a/moo/configure.ac b/moo/configure.ac index be53fe6..97afc33 100644 --- a/moo/configure.ac +++ b/moo/configure.ac @@ -140,11 +140,11 @@ AC_LINK_IFELSE( [AC_MSG_RESULT(no)] ) -AC_MSG_CHECKING([computed goto usability]) +AC_MSG_CHECKING([labels as values]) AC_COMPILE_IFELSE( [AC_LANG_PROGRAM( [], [[void *jp = &&jpt; goto *jp; 1; jpt: 2; ]])], [AC_MSG_RESULT(yes) - AC_DEFINE(HAVE_COMPUTED_GOTO, 1, [computed gotos])], + AC_DEFINE(HAVE_LABELS_AS_VALUES, 1, [labels as values])], [AC_MSG_RESULT(no)] ) diff --git a/moo/kernel/Collect.moo b/moo/kernel/Collect.moo index 6f3c256..d1638ab 100644 --- a/moo/kernel/Collect.moo +++ b/moo/kernel/Collect.moo @@ -10,7 +10,7 @@ class Collection(Object) { ^self size > 0 } - + method size { (* Each subclass must override this method because @@ -20,18 +20,18 @@ class Collection(Object) self do: [ :el | count := count + 1 ]. ^count } - + method do: block { ^self subclassResponsibility: #do } - + method detect: block { self do: [ :el | if (block value: el) { ^el } ]. ^Error.Code.ENOENT } - + method detect: block ifNone: exception_block { self do: [ :el | if (block value: el) { ^el } ]. @@ -100,12 +100,12 @@ class(#character) String(Array) ^newstr } - + method asString { ^self } - + (* TODO: Symbol is a #final class. Symbol new is not allowed. To create a symbol programatically, you should * build a string and send asSymbol to the string............ method asSymbol @@ -113,7 +113,6 @@ class(#character) String(Array) } *) - (* The strlen method returns the number of characters before a terminating null. * if no terminating null character exists, it returns the same value as the size method *) method(#primitive,#lenient) _strlen. @@ -252,7 +251,7 @@ class Set(Collection) index := hv rem: bs. while ((self.bucket at: index) notNil) { index := (index + 1) rem: bs }. }. - + ass := Association key: key value: value. self.tally := ntally. self.bucket at: index put: ass. diff --git a/moo/kernel/Magnitu.moo b/moo/kernel/Magnitu.moo index bcc990c..4bf57ef 100644 --- a/moo/kernel/Magnitu.moo +++ b/moo/kernel/Magnitu.moo @@ -10,13 +10,13 @@ class Association(Magnitude) { ^self new key: key value: value } - + method key: key value: value { self.key := key. self.value := value. } - + method value: value { self.value := value @@ -31,12 +31,12 @@ class Association(Magnitude) { ^self.value } - + method = ass { ^(self.key = ass key) and: [ self.value = ass value ] } - + method hash { ^(self.key hash) + (self.value hash) @@ -230,7 +230,7 @@ class(#limited) Number(Magnitude) { ^self to: end by: 1 do: block. } - + method priorTo: end by: step do: block { | i | @@ -306,7 +306,7 @@ class(#limited) SmallInteger(Integer) ## { ## ^0 ## } - + method(#primitive) asCharacter. method(#primitive) asError. } diff --git a/moo/kernel/Process.moo b/moo/kernel/Process.moo index 82723e4..0465a46 100644 --- a/moo/kernel/Process.moo +++ b/moo/kernel/Process.moo @@ -47,15 +47,16 @@ class(#pointer,#final,#limited) Process(Object) class Semaphore(Object) { - var count := 0, - waiting_head := nil, + var waiting_head := nil, waiting_tail := nil, + count := 0, heapIndex := -1, fireTimeSec := 0, fireTimeNsec := 0, ioIndex := -1, ioHandle := nil, - ioMask := 0. + ioMask := 0, + group := nil. method(#class) forMutualExclusion { @@ -122,10 +123,73 @@ TODO: timed wait... ^self.fireTimeSec >= (aSemaphore fireTime) } } - + + +(* + xxx := Semaphore new. + xxx on: #signal do: [ ]. + + + ========= CASE 1 ==================== + sg := SemaphoreGroup with (xxx, yyy, zzz). + Processor signal: xxx onInput: aaa. + Processor signal: yyy onInput: bbb. + Processor signal: zzz onOutput: ccc. + + while (true) + { + sem := sg wait. + if (sem == xxx) + { + + } + elsif (sem == yyy) + { + } + elsif (sem == zzz) + { + } + } + + ============ CASE 2==================== + ### ASSOCIATE CALLBACK WITH SEMAPHORE. + + sg := SemaphoreGroup with (xxx, yyy, zzz). + + oldaction := xxx signalAction: [ ... ]. ### similar interface like unix system call signal()???? method signalAction: block {} , method signalAction { ^self.signalAction } + yyy signalAction: [ ... ]. + zzz signalAction: [ ... ]. + + Processor signal: xxx onInput: aaa. + Processor signal: yyy onInput: bbb. + Processor signal: zzz onOutput: ccc. + + + while (true) + { + sem := sg wait. ### the action associated with the semaphore must get executed. => wait may be a primitive. the primitive handler may return failure... if so, the actual primitive body can execute the action easily + } + + + Semaphore>>method wait + { + + if (errorCode == NO ERROR) + { + self.signalAction value. ## which is better??? + self.sginalAction value: self. + } + } +*) + + class SemaphoreGroup(Object) { - var arr, size := 0. + var waiting_head := nil, + waiting_tail := nil, + size := 0, + pos := 0, + semarr := nil. (* TODO: good idea to a shortcut way to prohibit a certain method in the heirarchy chain? @@ -156,13 +220,13 @@ method(#class,#abstract) xxx. => method(#class) xxx { self subclassResponsibilit method initialize { - self.arr := Array new: 10. + self.semarr := Array new: 10. } method initialize: arr { self.size := arr size. - self.arr := arr. + self.semarr := arr. } method(#primitive) wait. diff --git a/moo/kernel/test-001.moo b/moo/kernel/test-001.moo index fad1c26..e24464c 100644 --- a/moo/kernel/test-001.moo +++ b/moo/kernel/test-001.moo @@ -104,6 +104,19 @@ extend MyObject { ## TODO: support import in extend?? + method(#class) makeBlock(a,b) + { + ^[:x | a * x + b] + } + + method(#class) testMakeBlock + { + |a b | + a := self makeBlock (12, 22). + b := self makeBlock (99, 4). + ^(a value: 5) * (b value: 6). ## (12 * 5 + 22) * (99 * 6 + 4) => 49036 + } + method(#class) main { | tc limit | @@ -159,7 +172,8 @@ extend MyObject [MyObject.System.System.System.System new kk == #KK], ## 35 - 39 - [MyObject.System.System.System KING == #KING] + [MyObject.System.System.System KING == #KING], + [self testMakeBlock == 49036] ). limit := tc size. diff --git a/moo/lib/exec.c b/moo/lib/exec.c index 61a5356..4def450 100644 --- a/moo/lib/exec.c +++ b/moo/lib/exec.c @@ -90,7 +90,6 @@ static MOO_INLINE const char* proc_state_to_string (int state) } while (0) #define FETCH_BYTE_CODE(moo) ((moo)->active_code[(moo)->ip++]) -#define FETCH_BYTE_CODE_TO(moo, v_ooi) (v_ooi = FETCH_BYTE_CODE(moo)) #if (MOO_BCODE_LONG_PARAM_SIZE == 2) # define FETCH_PARAM_CODE_TO(moo, v_ooi) \ do { \ @@ -465,15 +464,26 @@ static MOO_INLINE void unchain_from_processor (moo_t* moo, moo_oop_process_t pro 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*/ + /* 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 */ 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); + MOO_ASSERT (moo, MOO_CLASSOF(moo,sem) == moo->_semaphore || + MOO_CLASSOF(moo,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); - proc->sem = sem; + proc->sem = (moo_oop_t)sem; } static MOO_INLINE void unchain_from_semaphore (moo_t* moo, moo_oop_process_t proc) @@ -482,12 +492,22 @@ static MOO_INLINE void unchain_from_semaphore (moo_t* moo, moo_oop_process_t pro MOO_ASSERT (moo, (moo_oop_t)proc->sem != moo->_nil); - sem = proc->sem; - MOO_DELETE_FROM_OOP_LIST (moo, &sem->waiting, proc, sem_wait); + MOO_ASSERT (moo, MOO_CLASSOF(moo, proc->sem) == moo->_semaphore || + MOO_CLASSOF(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_oop_semaphore_t)moo->_nil; + proc->sem = moo->_nil; } static void terminate_process (moo_t* moo, moo_oop_process_t proc) @@ -695,6 +715,24 @@ static moo_oop_process_t signal_semaphore (moo_t* moo, moo_oop_semaphore_t sem) moo_oop_process_t proc; moo_ooi_t count; + if ((moo_oop_t)sem->group != moo->_nil) + { + /* the semaphore belongs to a group */ + moo_oop_semaphore_group_t semgrp; + + semgrp = sem->group; + if ((moo_oop_t)semgrp->waiting.first != moo->_nil) + { + /* there is a process waiting on the process group */ + proc = semgrp->waiting.first; + + unchain_from_semaphore (moo, proc); + resume_process (moo, proc); + + return proc; + } + } + if ((moo_oop_t)sem->waiting.first == moo->_nil) { /* no process is waiting on this semaphore */ @@ -725,6 +763,7 @@ static moo_oop_process_t signal_semaphore (moo_t* moo, moo_oop_semaphore_t sem) static void await_semaphore (moo_t* moo, moo_oop_semaphore_t sem) { +/* TODO: support timeout */ moo_oop_process_t proc; moo_ooi_t count; @@ -754,16 +793,62 @@ static void await_semaphore (moo_t* moo, moo_oop_semaphore_t sem) } } -static void await_semaphore_group (moo_t* moo, moo_oop_semaphore_group_t sem_group) +static moo_oop_t await_semaphore_group (moo_t* moo, moo_oop_semaphore_group_t semgrp) { - moo_oop_oop_t proc; - +/* TODO: support timeout and wait all */ /* wait for one of semaphores in the group to be signaled */ - /*MOO_CLASSOF (moo, MOO_CLASSOF(sem_group) == moo->_semaphore_group);*/ - /* TODO: check if a semaphore has been signalled already.. */ + moo_oop_process_t proc; + moo_oop_semaphore_t sem; + moo_ooi_t numsems, sempos, i, count; - /* if not,, chain all semaphore into the semaphore list... */ + MOO_ASSERT (moo, MOO_CLASSOF(moo,semgrp) == moo->_semaphore_group); + + /* check if there is a signaled semaphore in the group */ + numsems = MOO_OOP_TO_SMOOI(semgrp->size); + sempos = MOO_OOP_TO_SMOOI(semgrp->pos); + for (i = 0; i < numsems; i++) + { + sem = (moo_oop_semaphore_t)((moo_oop_oop_t)semgrp->semarr)->slot[sempos]; + sempos = (sempos + 1) % numsems; + + count = MOO_OOP_TO_SMOOI(sem->count); + if (count > 0) + { + count--; + sem->count = MOO_SMOOI_TO_OOP(count); + semgrp->pos = MOO_SMOOI_TO_OOP(sempos); + return (moo_oop_t)sem; + } + } + + /* no semaphores have been signaled. suspend the current process + * until the at least one of them is signaled */ + proc = moo->processor->active; + + /* suspend the active process */ + suspend_process (moo, proc); + +#if 0 + /* link the suspended process to the semaphore's process list */ + for (i = 0; i < numsems; i++) + { + sem = (moo_oop_semaphore_t)((moo_oop_oop_t)semgrp->semarr)->slot[sempos]; + sempos = (sempos + 1) % numsems; + chain_into_semaphore (moo, proc, sem); + MOO_ASSERT (moo, sem->waiting.last == proc); + if (MOO_OOP_TO_SMOOI(sem->io_index) >= 0) moo->sem_io_wait_count++; + } +#else + /* link the suspended process to the semaphore'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(sem->io_index) >= 0) moo->sem_io_wait_count++;*/ +#endif + + + MOO_ASSERT (moo, moo->processor->active != proc); + return moo->_nil; } static void sift_up_sem_heap (moo_t* moo, moo_ooi_t index) @@ -2392,6 +2477,8 @@ static moo_pfrc_t pf_semaphore_wait (moo_t* moo, moo_ooi_t nargs) static moo_pfrc_t pf_semaphore_group_wait (moo_t* moo, moo_ooi_t nargs) { moo_oop_t rcv; + moo_oop_t sem; + MOO_ASSERT (moo, nargs == 0); rcv = MOO_STACK_GETRCV(moo, nargs); @@ -2401,9 +2488,9 @@ static moo_pfrc_t pf_semaphore_group_wait (moo_t* moo, moo_ooi_t nargs) return MOO_PF_SUCCESS; } - await_semaphore_group (moo, (moo_oop_semaphore_group_t)rcv); + sem = await_semaphore_group (moo, (moo_oop_semaphore_group_t)rcv); - MOO_STACK_SETRETTORCV (moo, nargs); + MOO_STACK_SETRET (moo, nargs, sem); return MOO_PF_SUCCESS; } @@ -4836,14 +4923,338 @@ switch_to_next: } +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 + int unwind_protect; + moo_oop_context_t unwind_start; + moo_oop_context_t unwind_stop; + + 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 + { + unwind_protect = 0; + + /* set the instruction pointer to an invalid value. + * this is stored into the current method context + * before context switching and marks a dead context */ + if (moo->active_context->origin == moo->active_context) + { + /* returning from a method */ + MOO_ASSERT (moo, MOO_CLASSOF(moo, moo->active_context) == moo->_method_context); + + /* mark that the context is dead. it will be + * save to the context object by SWITCH_ACTIVE_CONTEXT() */ + 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_DEBUG2 (moo, "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) + { + static moo_ooch_t fbm[] = { + 'u', 'n', 'w', 'i', 'n', 'd', 'T', 'o', ':', + 'r', 'e', 't', 'u', 'r', 'n', ':' + }; + + 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_with_str (moo, fbm, 16, 0, 2) <= -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_source == 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_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) +{ + LOG_INST_0 (moo, "return_from_block"); + + 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_context_t blkctx; + 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_INST_2 (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 */ + blkctx = (moo_oop_context_t)moo_instantiate (moo, moo->_block_context, MOO_NULL, 0); + if (!blkctx) 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. */ + blkctx->ip = MOO_SMOOI_TO_OOP(moo->ip + MOO_BCODE_LONG_PARAM_SIZE + 1); + /* stack pointer below the bottom. this base block context + * has an empty stack anyway. */ + blkctx->sp = MOO_SMOOI_TO_OOP(-1); + /* the number of arguments for a block context is local to the block */ + blkctx->method_or_nargs = MOO_SMOOI_TO_OOP(b1); + /* the number of temporaries here is an accumulated count including + * the number of temporaries of a home context */ + blkctx->ntmprs = MOO_SMOOI_TO_OOP(b2); + + /* set the home context where it's defined */ + blkctx->home = (moo_oop_t)moo->active_context; + /* no source for a base block context. */ + blkctx->receiver_or_source = moo->_nil; + + blkctx->origin = moo->active_context->origin; + + /* push the new block context to the stack of the active context */ + MOO_STACK_PUSH (moo, (moo_oop_t)blkctx); + return 0; +} + static int __execute (moo_t* moo) { moo_oob_t bcode; moo_oow_t b1, b2; moo_oop_t return_value; - int unwind_protect; - moo_oop_context_t unwind_start; - moo_oop_context_t unwind_stop; + +#if defined(HAVE_LABELS_AS_VALUES) + static void* inst_table[256] = + { + #include "moo-bct.h" + }; + +# define BEGIN_DISPATCH_LOOP() __begin_inst_dispatch: +# define END_DISPATCH_LOOP() __end_inst_dispatch: +# define EXIT_DISPATCH_LOOP(x) 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(x) 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); @@ -4851,1115 +5262,837 @@ static int __execute (moo_t* moo) * sem_heap * sem_io. * sem_list. - * these can be dirty if this function is called again esepcially after failure. + * these can get dirty if this function is called again esepcially after failure. */ - while (!moo->abort_req) - { - /* - if (moo->gc_finalization_pending) - switch_to_gc_process (xxxx); - else */ - if (switch_process_if_needed(moo) == 0) break; /* no more runnable process */ + BEGIN_DISPATCH_LOOP() + /* stop requested or no more runnable process */ + if (moo->abort_req || switch_process_if_needed(moo) == 0) EXIT_DISPATCH_LOOP(); -#if defined(MOO_DEBUG_VM_EXEC) + #if defined(MOO_DEBUG_VM_EXEC) moo->last_inst_pointer = moo->ip; -#endif - FETCH_BYTE_CODE_TO (moo, bcode); - /*while (bcode == BCODE_NOOP) FETCH_BYTE_CODE_TO (moo, bcode);*/ + #endif -#if defined(MOO_PROFILE_VM) + bcode = FETCH_BYTE_CODE(moo); + + #if defined(MOO_PROFILE_VM) moo->inst_counter++; -#endif + #endif - switch (bcode) + /* ==== 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_INST_1 (moo, "push_instvar %zu", b1); + MOO_ASSERT (moo, MOO_OBJ_GET_FLAGS_TYPE(moo->active_context->origin->receiver_or_source) == MOO_OBJ_TYPE_OOP); + MOO_STACK_PUSH (moo, ((moo_oop_oop_t)moo->active_context->origin->receiver_or_source)->slot[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_INST_1 (moo, "store_into_instvar %zu", b1); + MOO_ASSERT (moo, MOO_OBJ_GET_FLAGS_TYPE(moo->active_context->receiver_or_source) == MOO_OBJ_TYPE_OOP); + ((moo_oop_oop_t)moo->active_context->origin->receiver_or_source)->slot[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_INST_1 (moo, "pop_into_instvar %zu", b1); + MOO_ASSERT (moo, MOO_OBJ_GET_FLAGS_TYPE(moo->active_context->receiver_or_source) == MOO_OBJ_TYPE_OOP); + ((moo_oop_oop_t)moo->active_context->origin->receiver_or_source)->slot[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; - case BCODE_PUSH_INSTVAR_X: - FETCH_PARAM_CODE_TO (moo, b1); - goto push_instvar; - case BCODE_PUSH_INSTVAR_0: - case BCODE_PUSH_INSTVAR_1: - case BCODE_PUSH_INSTVAR_2: - case BCODE_PUSH_INSTVAR_3: - case BCODE_PUSH_INSTVAR_4: - case BCODE_PUSH_INSTVAR_5: - case BCODE_PUSH_INSTVAR_6: - case BCODE_PUSH_INSTVAR_7: - b1 = bcode & 0x7; /* low 3 bits */ - push_instvar: - LOG_INST_1 (moo, "push_instvar %zu", b1); - MOO_ASSERT (moo, MOO_OBJ_GET_FLAGS_TYPE(moo->active_context->origin->receiver_or_source) == MOO_OBJ_TYPE_OOP); - MOO_STACK_PUSH (moo, ((moo_oop_oop_t)moo->active_context->origin->receiver_or_source)->slot[b1]); - break; + 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 */ - case BCODE_STORE_INTO_INSTVAR_X: - FETCH_PARAM_CODE_TO (moo, b1); - goto store_instvar; - case BCODE_STORE_INTO_INSTVAR_0: - case BCODE_STORE_INTO_INSTVAR_1: - case BCODE_STORE_INTO_INSTVAR_2: - case BCODE_STORE_INTO_INSTVAR_3: - case BCODE_STORE_INTO_INSTVAR_4: - case BCODE_STORE_INTO_INSTVAR_5: - case BCODE_STORE_INTO_INSTVAR_6: - case BCODE_STORE_INTO_INSTVAR_7: - b1 = bcode & 0x7; /* low 3 bits */ - store_instvar: - LOG_INST_1 (moo, "store_into_instvar %zu", b1); - MOO_ASSERT (moo, MOO_OBJ_GET_FLAGS_TYPE(moo->active_context->receiver_or_source) == MOO_OBJ_TYPE_OOP); - ((moo_oop_oop_t)moo->active_context->origin->receiver_or_source)->slot[b1] = MOO_STACK_GETTOP(moo); - break; - - /* ------------------------------------------------- */ - case BCODE_POP_INTO_INSTVAR_X: - FETCH_PARAM_CODE_TO (moo, b1); - goto pop_into_instvar; - case BCODE_POP_INTO_INSTVAR_0: - case BCODE_POP_INTO_INSTVAR_1: - case BCODE_POP_INTO_INSTVAR_2: - case BCODE_POP_INTO_INSTVAR_3: - case BCODE_POP_INTO_INSTVAR_4: - case BCODE_POP_INTO_INSTVAR_5: - case BCODE_POP_INTO_INSTVAR_6: - case BCODE_POP_INTO_INSTVAR_7: - b1 = bcode & 0x7; /* low 3 bits */ - pop_into_instvar: - LOG_INST_1 (moo, "pop_into_instvar %zu", b1); - MOO_ASSERT (moo, MOO_OBJ_GET_FLAGS_TYPE(moo->active_context->receiver_or_source) == MOO_OBJ_TYPE_OOP); - ((moo_oop_oop_t)moo->active_context->origin->receiver_or_source)->slot[b1] = MOO_STACK_GETTOP(moo); - MOO_STACK_POP (moo); - break; - - /* ------------------------------------------------- */ - case BCODE_PUSH_TEMPVAR_X: - case BCODE_STORE_INTO_TEMPVAR_X: - case BCODE_POP_INTO_TEMPVAR_X: - FETCH_PARAM_CODE_TO (moo, b1); - goto handle_tempvar; - - case BCODE_PUSH_TEMPVAR_0: - case BCODE_PUSH_TEMPVAR_1: - case BCODE_PUSH_TEMPVAR_2: - case BCODE_PUSH_TEMPVAR_3: - case BCODE_PUSH_TEMPVAR_4: - case BCODE_PUSH_TEMPVAR_5: - case BCODE_PUSH_TEMPVAR_6: - case BCODE_PUSH_TEMPVAR_7: - case BCODE_STORE_INTO_TEMPVAR_0: - case BCODE_STORE_INTO_TEMPVAR_1: - case BCODE_STORE_INTO_TEMPVAR_2: - case BCODE_STORE_INTO_TEMPVAR_3: - case BCODE_STORE_INTO_TEMPVAR_4: - case BCODE_STORE_INTO_TEMPVAR_5: - case BCODE_STORE_INTO_TEMPVAR_6: - case BCODE_STORE_INTO_TEMPVAR_7: - case BCODE_POP_INTO_TEMPVAR_0: - case BCODE_POP_INTO_TEMPVAR_1: - case BCODE_POP_INTO_TEMPVAR_2: - case BCODE_POP_INTO_TEMPVAR_3: - case BCODE_POP_INTO_TEMPVAR_4: - case BCODE_POP_INTO_TEMPVAR_5: - case BCODE_POP_INTO_TEMPVAR_6: - case BCODE_POP_INTO_TEMPVAR_7: + if (moo->active_context->home != moo->_nil) { - 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_INST_1 (moo, "push_tempvar %zu", b1); - MOO_STACK_PUSH (moo, ctx->slot[bx]); - } - else - { - /* store or pop - bit 5 off */ - ctx->slot[bx] = MOO_STACK_GETTOP(moo); - - if ((bcode >> 3) & 1) - { - /* pop - bit 3 on */ - LOG_INST_1 (moo, "pop_into_tempvar %zu", b1); - MOO_STACK_POP (moo); - } - else - { - LOG_INST_1 (moo, "store_into_tempvar %zu", b1); - } - } - - break; - } - - /* ------------------------------------------------- */ - case BCODE_PUSH_LITERAL_X: - FETCH_PARAM_CODE_TO (moo, b1); - goto push_literal; - - case BCODE_PUSH_LITERAL_0: - case BCODE_PUSH_LITERAL_1: - case BCODE_PUSH_LITERAL_2: - case BCODE_PUSH_LITERAL_3: - case BCODE_PUSH_LITERAL_4: - case BCODE_PUSH_LITERAL_5: - case BCODE_PUSH_LITERAL_6: - case BCODE_PUSH_LITERAL_7: - b1 = bcode & 0x7; /* low 3 bits */ - push_literal: - LOG_INST_1 (moo, "push_literal @%zu", b1); - MOO_STACK_PUSH (moo, moo->active_method->slot[b1]); - break; - - /* ------------------------------------------------- */ - case BCODE_PUSH_OBJECT_X: - case BCODE_STORE_INTO_OBJECT_X: - case BCODE_POP_INTO_OBJECT_X: - FETCH_PARAM_CODE_TO (moo, b1); - goto handle_object; - - case BCODE_PUSH_OBJECT_0: - case BCODE_PUSH_OBJECT_1: - case BCODE_PUSH_OBJECT_2: - case BCODE_PUSH_OBJECT_3: - case BCODE_STORE_INTO_OBJECT_0: - case BCODE_STORE_INTO_OBJECT_1: - case BCODE_STORE_INTO_OBJECT_2: - case BCODE_STORE_INTO_OBJECT_3: - case BCODE_POP_INTO_OBJECT_0: - case BCODE_POP_INTO_OBJECT_1: - case BCODE_POP_INTO_OBJECT_2: - case 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->slot[b1]; - MOO_ASSERT (moo, MOO_CLASSOF(moo, ass) == moo->_association); - - if ((bcode >> 3) & 1) - { - /* store or pop */ - ass->value = MOO_STACK_GETTOP(moo); - - if ((bcode >> 2) & 1) - { - /* pop */ - LOG_INST_1 (moo, "pop_into_object @%zu", b1); - MOO_STACK_POP (moo); - } - else - { - LOG_INST_1 (moo, "store_into_object @%zu", b1); - } - } - else - { - /* push */ - LOG_INST_1 (moo, "push_object @%zu", b1); - MOO_STACK_PUSH (moo, ass->value); - } - break; - } - - /* -------------------------------------------------------- */ - - case BCODE_JUMP_FORWARD_X: - FETCH_PARAM_CODE_TO (moo, b1); - LOG_INST_1 (moo, "jump_forward %zu", b1); - moo->ip += b1; - break; - - case BCODE_JUMP_FORWARD_0: - case BCODE_JUMP_FORWARD_1: - case BCODE_JUMP_FORWARD_2: - case BCODE_JUMP_FORWARD_3: - LOG_INST_1 (moo, "jump_forward %zu", (moo_oow_t)(bcode & 0x3)); - moo->ip += (bcode & 0x3); /* low 2 bits */ - break; - - case BCODE_JUMP_BACKWARD_X: - FETCH_PARAM_CODE_TO (moo, b1); - LOG_INST_1 (moo, "jump_backward %zu", b1); - moo->ip -= b1; - break; - - case BCODE_JUMP_BACKWARD_0: - case BCODE_JUMP_BACKWARD_1: - case BCODE_JUMP_BACKWARD_2: - case BCODE_JUMP_BACKWARD_3: - LOG_INST_1 (moo, "jump_backward %zu", (moo_oow_t)(bcode & 0x3)); - moo->ip -= (bcode & 0x3); /* low 2 bits */ - break; - - case BCODE_JUMP_BACKWARD_IF_FALSE_X: - FETCH_PARAM_CODE_TO (moo, b1); - LOG_INST_1 (moo, "jump_backward_if_false %zu", b1); - if (MOO_STACK_GETTOP(moo) == moo->_false) moo->ip -= b1; - MOO_STACK_POP (moo); - break; - - case BCODE_JUMP_BACKWARD_IF_FALSE_0: - case BCODE_JUMP_BACKWARD_IF_FALSE_1: - case BCODE_JUMP_BACKWARD_IF_FALSE_2: - case BCODE_JUMP_BACKWARD_IF_FALSE_3: - LOG_INST_1 (moo, "jump_backward_if_false %zu", (moo_oow_t)(bcode & 0x3)); - if (MOO_STACK_GETTOP(moo) == moo->_false) moo->ip -= (bcode & 0x3); /* low 2 bits */ - MOO_STACK_POP (moo); - break; - - case BCODE_JUMP_BACKWARD_IF_TRUE_X: - FETCH_PARAM_CODE_TO (moo, b1); - LOG_INST_1 (moo, "jump_backward_if_true %zu", b1); - /*if (MOO_STACK_GETTOP(moo) == moo->_true) moo->ip -= b1;*/ - if (MOO_STACK_GETTOP(moo) != moo->_false) moo->ip -= b1; - MOO_STACK_POP (moo); - break; - - case BCODE_JUMP_BACKWARD_IF_TRUE_0: - case BCODE_JUMP_BACKWARD_IF_TRUE_1: - case BCODE_JUMP_BACKWARD_IF_TRUE_2: - case BCODE_JUMP_BACKWARD_IF_TRUE_3: - LOG_INST_1 (moo, "jump_backward_if_true %zu", (moo_oow_t)(bcode & 0x3)); - /*if (MOO_STACK_GETTOP(moo) == moo->_true) moo->ip -= (bcode & 0x3);*/ /* low 2 bits */ - if (MOO_STACK_GETTOP(moo) != moo->_false) moo->ip -= (bcode & 0x3); - MOO_STACK_POP (moo); - break; - - case BCODE_JUMP_FORWARD_IF_FALSE: - FETCH_PARAM_CODE_TO (moo, b1); - LOG_INST_1 (moo, "jump_forward_if_false %zu", b1); - if (MOO_STACK_GETTOP(moo) == moo->_false) moo->ip += b1; - MOO_STACK_POP (moo); - break; - - case BCODE_JUMP_FORWARD_IF_TRUE: - FETCH_PARAM_CODE_TO (moo, b1); - LOG_INST_1 (moo, "jump_forward_if_true %zu", b1); - /*if (MOO_STACK_GETTOP(moo) == moo->_true) moo->ip += b1;*/ - if (MOO_STACK_GETTOP(moo) != moo->_false) moo->ip += b1; - MOO_STACK_POP (moo); - break; - - case BCODE_JUMP2_FORWARD: - FETCH_PARAM_CODE_TO (moo, b1); - LOG_INST_1 (moo, "jump2_forward %zu", b1); - moo->ip += MAX_CODE_JUMP + b1; - break; - - case BCODE_JUMP2_BACKWARD: - FETCH_PARAM_CODE_TO (moo, b1); - LOG_INST_1 (moo, "jump2_backward %zu", b1); - moo->ip -= MAX_CODE_JUMP + b1; - break; - - case BCODE_JUMP2_FORWARD_IF_FALSE: - FETCH_PARAM_CODE_TO (moo, b1); - LOG_INST_1 (moo, "jump2_forward_if_false %zu", b1); - if (MOO_STACK_GETTOP(moo) == moo->_false) moo->ip += MAX_CODE_JUMP + b1; - MOO_STACK_POP (moo); - break; - - case BCODE_JUMP2_FORWARD_IF_TRUE: - FETCH_PARAM_CODE_TO (moo, b1); - LOG_INST_1 (moo, "jump2_forward_if_true %zu", b1); - /*if (MOO_STACK_GETTOP(moo) == moo->_true) moo->ip += MAX_CODE_JUMP + b1;*/ - if (MOO_STACK_GETTOP(moo) != moo->_false) moo->ip += MAX_CODE_JUMP + b1; - MOO_STACK_POP (moo); - break; - - case BCODE_JUMP2_BACKWARD_IF_FALSE: - FETCH_PARAM_CODE_TO (moo, b1); - LOG_INST_1 (moo, "jump2_backward_if_false %zu", b1); - if (MOO_STACK_GETTOP(moo) == moo->_false) moo->ip -= MAX_CODE_JUMP + b1; - MOO_STACK_POP (moo); - break; - - case BCODE_JUMP2_BACKWARD_IF_TRUE: - FETCH_PARAM_CODE_TO (moo, b1); - LOG_INST_1 (moo, "jump2_backward_if_true %zu", b1); - /* if (MOO_STACK_GETTOP(moo) == moo->_true) moo->ip -= MAX_CODE_JUMP + b1; */ - if (MOO_STACK_GETTOP(moo) != moo->_false) moo->ip -= MAX_CODE_JUMP + b1; - MOO_STACK_POP (moo); - break; - /* -------------------------------------------------------- */ - - case BCODE_PUSH_CTXTEMPVAR_X: - case BCODE_STORE_INTO_CTXTEMPVAR_X: - case BCODE_POP_INTO_CTXTEMPVAR_X: - FETCH_PARAM_CODE_TO (moo, b1); - FETCH_PARAM_CODE_TO (moo, b2); - goto handle_ctxtempvar; - case BCODE_PUSH_CTXTEMPVAR_0: - case BCODE_PUSH_CTXTEMPVAR_1: - case BCODE_PUSH_CTXTEMPVAR_2: - case BCODE_PUSH_CTXTEMPVAR_3: - case BCODE_STORE_INTO_CTXTEMPVAR_0: - case BCODE_STORE_INTO_CTXTEMPVAR_1: - case BCODE_STORE_INTO_CTXTEMPVAR_2: - case BCODE_STORE_INTO_CTXTEMPVAR_3: - case BCODE_POP_INTO_CTXTEMPVAR_0: - case BCODE_POP_INTO_CTXTEMPVAR_1: - case BCODE_POP_INTO_CTXTEMPVAR_2: - case BCODE_POP_INTO_CTXTEMPVAR_3: - { - moo_ooi_t i; - moo_oop_context_t ctx; - - b1 = bcode & 0x3; /* low 2 bits */ - FETCH_BYTE_CODE_TO (moo, b2); - - handle_ctxtempvar: + /* 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; - MOO_ASSERT (moo, (moo_oop_t)ctx != moo->_nil); - for (i = 0; i < b1; i++) + home = ctx->home; + + do { - ctx = (moo_oop_context_t)ctx->home; + /* 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_INST_1 (moo, "push_tempvar %zu", b1); + MOO_STACK_PUSH (moo, ctx->slot[bx]); + } + else + { + /* store or pop - bit 5 off */ + ctx->slot[bx] = MOO_STACK_GETTOP(moo); if ((bcode >> 3) & 1) { - /* store or pop */ - ctx->slot[b2] = MOO_STACK_GETTOP(moo); - - if ((bcode >> 2) & 1) - { - /* pop */ - MOO_STACK_POP (moo); - LOG_INST_2 (moo, "pop_into_ctxtempvar %zu %zu", b1, b2); - } - else - { - LOG_INST_2 (moo, "store_into_ctxtempvar %zu %zu", b1, b2); - } + /* pop - bit 3 on */ + LOG_INST_1 (moo, "pop_into_tempvar %zu", b1); + MOO_STACK_POP (moo); } else { - /* push */ - MOO_STACK_PUSH (moo, ctx->slot[b2]); - LOG_INST_2 (moo, "push_ctxtempvar %zu %zu", b1, b2); + LOG_INST_1 (moo, "store_into_tempvar %zu", b1); } - - break; - } - /* -------------------------------------------------------- */ - - case BCODE_PUSH_OBJVAR_X: - case BCODE_STORE_INTO_OBJVAR_X: - case BCODE_POP_INTO_OBJVAR_X: - FETCH_PARAM_CODE_TO (moo, b1); - FETCH_PARAM_CODE_TO (moo, b2); - goto handle_objvar; - - case BCODE_PUSH_OBJVAR_0: - case BCODE_PUSH_OBJVAR_1: - case BCODE_PUSH_OBJVAR_2: - case BCODE_PUSH_OBJVAR_3: - case BCODE_STORE_INTO_OBJVAR_0: - case BCODE_STORE_INTO_OBJVAR_1: - case BCODE_STORE_INTO_OBJVAR_2: - case BCODE_STORE_INTO_OBJVAR_3: - case BCODE_POP_INTO_OBJVAR_0: - case BCODE_POP_INTO_OBJVAR_1: - case BCODE_POP_INTO_OBJVAR_2: - case 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 */ - FETCH_BYTE_CODE_TO (moo, b2); - - handle_objvar: - t = (moo_oop_oop_t)moo->active_method->slot[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 */ - - t->slot[b1] = MOO_STACK_GETTOP(moo); - - if ((bcode >> 2) & 1) - { - /* pop */ - MOO_STACK_POP (moo); - LOG_INST_2 (moo, "pop_into_objvar %zu %zu", b1, b2); - } - else - { - LOG_INST_2 (moo, "store_into_objvar %zu %zu", b1, b2); - } - } - else - { - /* push */ - LOG_INST_2 (moo, "push_objvar %zu %zu", b1, b2); - MOO_STACK_PUSH (moo, t->slot[b1]); - } - break; } - /* -------------------------------------------------------- */ - case BCODE_SEND_MESSAGE_X: - case 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; - - case BCODE_SEND_MESSAGE_0: - case BCODE_SEND_MESSAGE_1: - case BCODE_SEND_MESSAGE_2: - case BCODE_SEND_MESSAGE_3: - case BCODE_SEND_MESSAGE_TO_SUPER_0: - case BCODE_SEND_MESSAGE_TO_SUPER_1: - case BCODE_SEND_MESSAGE_TO_SUPER_2: - case BCODE_SEND_MESSAGE_TO_SUPER_3: - { - moo_oop_char_t selector; - - b1 = bcode & 0x3; /* low 2 bits */ - FETCH_BYTE_CODE_TO (moo, b2); - - handle_send_message: - /* get the selector from the literal frame */ - selector = (moo_oop_char_t)moo->active_method->slot[b2]; - - LOG_INST_3 (moo, "send_message%hs %zu @%zu", (((bcode >> 2) & 1)? "_to_super": ""), b1, b2); - if (send_message (moo, selector, ((bcode >> 2) & 1), b1) <= -1) return -1; - break; - } - - /* -------------------------------------------------------- */ - - case BCODE_PUSH_RECEIVER: - LOG_INST_0 (moo, "push_receiver"); - MOO_STACK_PUSH (moo, moo->active_context->origin->receiver_or_source); - break; - - case BCODE_PUSH_NIL: - LOG_INST_0 (moo, "push_nil"); - MOO_STACK_PUSH (moo, moo->_nil); - break; - - case BCODE_PUSH_TRUE: - LOG_INST_0 (moo, "push_true"); - MOO_STACK_PUSH (moo, moo->_true); - break; - - case BCODE_PUSH_FALSE: - LOG_INST_0 (moo, "push_false"); - MOO_STACK_PUSH (moo, moo->_false); - break; - - case BCODE_PUSH_CONTEXT: - LOG_INST_0 (moo, "push_context"); - MOO_STACK_PUSH (moo, (moo_oop_t)moo->active_context); - break; - - case BCODE_PUSH_PROCESS: - LOG_INST_0 (moo, "push_process"); - MOO_STACK_PUSH (moo, (moo_oop_t)moo->processor->active); - break; - - case BCODE_PUSH_RECEIVER_NS: - { - register moo_oop_t c; - LOG_INST_0 (moo, "push_receiver_ns"); - c = (moo_oop_t)MOO_CLASSOF(moo, moo->active_context->origin->receiver_or_source); - if (c == (moo_oop_t)moo->_class) c = moo->active_context->origin->receiver_or_source; - MOO_STACK_PUSH (moo, (moo_oop_t)((moo_oop_class_t)c)->nsup); - break; - } - - case BCODE_PUSH_NEGONE: - LOG_INST_0 (moo, "push_negone"); - MOO_STACK_PUSH (moo, MOO_SMOOI_TO_OOP(-1)); - break; - - case BCODE_PUSH_ZERO: - LOG_INST_0 (moo, "push_zero"); - MOO_STACK_PUSH (moo, MOO_SMOOI_TO_OOP(0)); - break; - - case BCODE_PUSH_ONE: - LOG_INST_0 (moo, "push_one"); - MOO_STACK_PUSH (moo, MOO_SMOOI_TO_OOP(1)); - break; - - case BCODE_PUSH_TWO: - LOG_INST_0 (moo, "push_two"); - MOO_STACK_PUSH (moo, MOO_SMOOI_TO_OOP(2)); - break; - - case BCODE_PUSH_INTLIT: - FETCH_PARAM_CODE_TO (moo, b1); - LOG_INST_1 (moo, "push_intlit %zu", b1); - MOO_STACK_PUSH (moo, MOO_SMOOI_TO_OOP(b1)); - break; - - case BCODE_PUSH_NEGINTLIT: - { - moo_ooi_t num; - FETCH_PARAM_CODE_TO (moo, b1); - num = b1; - LOG_INST_1 (moo, "push_negintlit %zu", b1); - MOO_STACK_PUSH (moo, MOO_SMOOI_TO_OOP(-num)); - break; - } - - case BCODE_PUSH_CHARLIT: - FETCH_PARAM_CODE_TO (moo, b1); - LOG_INST_1 (moo, "push_charlit %zu", b1); - MOO_STACK_PUSH (moo, MOO_CHAR_TO_OOP(b1)); - break; - /* -------------------------------------------------------- */ - - case BCODE_MAKE_DICTIONARY: - FETCH_PARAM_CODE_TO (moo, b1); - LOG_INST_1 (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, 0, 1) <= -1) return -1; - break; - - case BCODE_POP_INTO_DICTIONARY: - LOG_INST_0 (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, 0, 1) <= -1) return -1; - break; - - case BCODE_MAKE_ARRAY: - { - moo_oop_t t; - - FETCH_PARAM_CODE_TO (moo, b1); - LOG_INST_1 (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 */ - break; - } - - case BCODE_POP_INTO_ARRAY: - { - moo_oop_t t1, t2; - - FETCH_PARAM_CODE_TO (moo, b1); - LOG_INST_1 (moo, "pop_into_array %zu", b1); - - t1 = MOO_STACK_GETTOP(moo); - MOO_STACK_POP (moo); - t2 = MOO_STACK_GETTOP(moo); - ((moo_oop_oop_t)t2)->slot[b1] = t1; - break; - } - - case BCODE_DUP_STACKTOP: - { - moo_oop_t t; - LOG_INST_0 (moo, "dup_stacktop"); - MOO_ASSERT (moo, !MOO_STACK_ISEMPTY(moo)); - t = MOO_STACK_GETTOP(moo); - MOO_STACK_PUSH (moo, t); - break; - } - - case BCODE_POP_STACKTOP: - LOG_INST_0 (moo, "pop_stacktop"); - MOO_ASSERT (moo, !MOO_STACK_ISEMPTY(moo)); - MOO_STACK_POP (moo); - break; - - case BCODE_RETURN_STACKTOP: - LOG_INST_0 (moo, "return_stacktop"); - return_value = MOO_STACK_GETTOP(moo); - MOO_STACK_POP (moo); - goto handle_return; - - case BCODE_RETURN_RECEIVER: - LOG_INST_0 (moo, "return_receiver"); - return_value = moo->active_context->origin->receiver_or_source; - - handle_return: - #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 - { - unwind_protect = 0; - - /* set the instruction pointer to an invalid value. - * this is stored into the current method context - * before context switching and marks a dead context */ - if (moo->active_context->origin == moo->active_context) - { - /* returning from a method */ - MOO_ASSERT (moo, MOO_CLASSOF(moo, moo->active_context) == moo->_method_context); - - /* mark that the context is dead. it will be - * save to the context object by SWITCH_ACTIVE_CONTEXT() */ - 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_DEBUG2 (moo, "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) - { - static moo_ooch_t fbm[] = { - 'u', 'n', 'w', 'i', 'n', 'd', 'T', 'o', ':', - 'r', 'e', 't', 'u', 'r', 'n', ':' - }; - - 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_with_str (moo, fbm, 16, 0, 2) <= -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_source == 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_AWAIT_PROCS) - { - terminate_process (moo, moo->processor->active); - } - else - { - /* graceful termination of the whole vm */ - goto done; - } - - /* TODO: store the return value to the VM register. - * the caller to moo_execute() can fetch it to return it to the system */ - } - } - } - - #endif - break; - - case BCODE_LOCAL_RETURN: - LOG_INST_0 (moo, "local_return"); - return_value = MOO_STACK_GETTOP(moo); - MOO_STACK_POP (moo); - goto handle_return; - - case BCODE_RETURN_FROM_BLOCK: - LOG_INST_0 (moo, "return_from_block"); - - 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); - } - - break; - - case BCODE_MAKE_BLOCK: - { - moo_oop_context_t blkctx; - - /* b1 - number of block arguments - * b2 - number of block temporaries */ - FETCH_PARAM_CODE_TO (moo, b1); - FETCH_PARAM_CODE_TO (moo, b2); - - LOG_INST_2 (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 */ - blkctx = (moo_oop_context_t)moo_instantiate (moo, moo->_block_context, MOO_NULL, 0); - if (!blkctx) 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. */ - blkctx->ip = MOO_SMOOI_TO_OOP(moo->ip + MOO_BCODE_LONG_PARAM_SIZE + 1); - /* stack pointer below the bottom. this base block context - * has an empty stack anyway. */ - blkctx->sp = MOO_SMOOI_TO_OOP(-1); - /* the number of arguments for a block context is local to the block */ - blkctx->method_or_nargs = MOO_SMOOI_TO_OOP(b1); - /* the number of temporaries here is an accumulated count including - * the number of temporaries of a home context */ - blkctx->ntmprs = MOO_SMOOI_TO_OOP(b2); - - /* set the home context where it's defined */ - blkctx->home = (moo_oop_t)moo->active_context; - /* no source for a base block context. */ - blkctx->receiver_or_source = moo->_nil; - - blkctx->origin = moo->active_context->origin; - - /* push the new block context to the stack of the active context */ - MOO_STACK_PUSH (moo, (moo_oop_t)blkctx); - break; - } - - case BCODE_SEND_BLOCK_COPY: - { - moo_ooi_t nargs, ntmprs; - moo_oop_context_t rctx; - moo_oop_context_t blkctx; - - LOG_INST_0 (moo, "send_block_copy"); - - /* it emulates thisContext blockCopy: nargs ofTmprCount: ntmprs */ - MOO_ASSERT (moo, moo->sp >= 2); - - MOO_ASSERT (moo, MOO_CLASSOF(moo, MOO_STACK_GETTOP(moo)) == moo->_small_integer); - ntmprs = MOO_OOP_TO_SMOOI(MOO_STACK_GETTOP(moo)); - MOO_STACK_POP (moo); - - MOO_ASSERT (moo, MOO_CLASSOF(moo, MOO_STACK_GETTOP(moo)) == moo->_small_integer); - nargs = MOO_OOP_TO_SMOOI(MOO_STACK_GETTOP(moo)); - MOO_STACK_POP (moo); - - MOO_ASSERT (moo, nargs >= 0); - MOO_ASSERT (moo, ntmprs >= nargs); - - /* 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. */ - blkctx = (moo_oop_context_t)moo_instantiate (moo, moo->_block_context, MOO_NULL, 0); - if (!blkctx) return -1; - - /* get the receiver to the block copy message after block context instantiation - * not to get affected by potential GC */ - rctx = (moo_oop_context_t)MOO_STACK_GETTOP(moo); - MOO_ASSERT (moo, rctx == moo->active_context); - - /* [NOTE] - * blkctx->sender is left to nil. it is set to the - * active context before it gets activated. see - * pf_block_value(). - * - * blkctx->home is set here to the active context. - * it's redundant to have them pushed to the stack - * though it is to emulate the message sending of - * blockCopy:withNtmprs:. BCODE_MAKE_BLOCK has been - * added to replace BCODE_SEND_BLOCK_COPY and pusing - * arguments to the stack. - * - * blkctx->origin is set here by copying the origin - * of the active context. - */ - - /* the extended jump instruction has the format of - * 0000XXXX KKKKKKKK or 0000XXXX KKKKKKKK KKKKKKKK - * depending on MOO_BCODE_LONG_PARAM_SIZE. change 'ip' to point to - * the instruction after the jump. */ - blkctx->ip = MOO_SMOOI_TO_OOP(moo->ip + MOO_BCODE_LONG_PARAM_SIZE + 1); - blkctx->sp = MOO_SMOOI_TO_OOP(-1); - /* the number of arguments for a block context is local to the block */ - blkctx->method_or_nargs = MOO_SMOOI_TO_OOP(nargs); - /* the number of temporaries here is an accumulated count including - * the number of temporaries of a home context */ - blkctx->ntmprs = MOO_SMOOI_TO_OOP(ntmprs); - - blkctx->home = (moo_oop_t)rctx; - blkctx->receiver_or_source = moo->_nil; - - /* [NOTE] - * the origin of a method context is set to itself - * when it's created. so it's safe to simply copy - * the origin field this way. - * - * if the context that receives the blockCopy message - * is a method context, the following conditions are all true. - * rctx->home == moo->_nil - * MOO_CLASSOF(moo, rctx) == moo->_method_context - * rctx == (moo_oop_t)moo->active_context - * rctx == rctx->origin - * - * if it is a block context, the following condition is true. - * MOO_CLASSOF(moo, rctx) == moo->_block_context - */ - blkctx->origin = rctx->origin; - - MOO_STACK_SETTOP (moo, (moo_oop_t)blkctx); - break; - } - - case BCODE_NOOP: - /* do nothing */ - LOG_INST_0 (moo, "noop"); - break; - - default: - 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; + NEXT_INST(); } - } -done: + /* ------------------------------------------------- */ + 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_INST_1 (moo, "push_literal @%zu", b1); + MOO_STACK_PUSH (moo, moo->active_method->slot[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->slot[b1]; + MOO_ASSERT (moo, MOO_CLASSOF(moo, ass) == moo->_association); + + if ((bcode >> 3) & 1) + { + /* store or pop */ + ass->value = MOO_STACK_GETTOP(moo); + + if ((bcode >> 2) & 1) + { + /* pop */ + LOG_INST_1 (moo, "pop_into_object @%zu", b1); + MOO_STACK_POP (moo); + } + else + { + LOG_INST_1 (moo, "store_into_object @%zu", b1); + } + } + else + { + /* push */ + LOG_INST_1 (moo, "push_object @%zu", b1); + MOO_STACK_PUSH (moo, ass->value); + } + NEXT_INST(); + } + + /* -------------------------------------------------------- */ + + ON_INST(BCODE_JUMP_FORWARD_X) + FETCH_PARAM_CODE_TO (moo, b1); + LOG_INST_1 (moo, "jump_forward %zu", b1); + moo->ip += b1; + NEXT_INST(); + + ON_INST(BCODE_JUMP_FORWARD_0) + ON_INST(BCODE_JUMP_FORWARD_1) + ON_INST(BCODE_JUMP_FORWARD_2) + ON_INST(BCODE_JUMP_FORWARD_3) + LOG_INST_1 (moo, "jump_forward %zu", (moo_oow_t)(bcode & 0x3)); + moo->ip += (bcode & 0x3); /* low 2 bits */ + NEXT_INST(); + + ON_INST(BCODE_JUMP_BACKWARD_X) + FETCH_PARAM_CODE_TO (moo, b1); + LOG_INST_1 (moo, "jump_backward %zu", b1); + moo->ip -= b1; + NEXT_INST(); + + ON_INST(BCODE_JUMP_BACKWARD_0) + ON_INST(BCODE_JUMP_BACKWARD_1) + ON_INST(BCODE_JUMP_BACKWARD_2) + ON_INST(BCODE_JUMP_BACKWARD_3) + LOG_INST_1 (moo, "jump_backward %zu", (moo_oow_t)(bcode & 0x3)); + moo->ip -= (bcode & 0x3); /* low 2 bits */ + NEXT_INST(); + + ON_INST(BCODE_JUMP_BACKWARD_IF_FALSE_X) + FETCH_PARAM_CODE_TO (moo, b1); + LOG_INST_1 (moo, "jump_backward_if_false %zu", b1); + if (MOO_STACK_GETTOP(moo) == moo->_false) moo->ip -= b1; + MOO_STACK_POP (moo); + NEXT_INST(); + + ON_INST(BCODE_JUMP_BACKWARD_IF_FALSE_0) + ON_INST(BCODE_JUMP_BACKWARD_IF_FALSE_1) + ON_INST(BCODE_JUMP_BACKWARD_IF_FALSE_2) + ON_INST(BCODE_JUMP_BACKWARD_IF_FALSE_3) + LOG_INST_1 (moo, "jump_backward_if_false %zu", (moo_oow_t)(bcode & 0x3)); + if (MOO_STACK_GETTOP(moo) == moo->_false) moo->ip -= (bcode & 0x3); /* low 2 bits */ + MOO_STACK_POP (moo); + NEXT_INST(); + + ON_INST(BCODE_JUMP_BACKWARD_IF_TRUE_X) + FETCH_PARAM_CODE_TO (moo, b1); + LOG_INST_1 (moo, "jump_backward_if_true %zu", b1); + /*if (MOO_STACK_GETTOP(moo) == moo->_true) moo->ip -= b1;*/ + if (MOO_STACK_GETTOP(moo) != moo->_false) moo->ip -= b1; + MOO_STACK_POP (moo); + NEXT_INST(); + + ON_INST(BCODE_JUMP_BACKWARD_IF_TRUE_0) + ON_INST(BCODE_JUMP_BACKWARD_IF_TRUE_1) + ON_INST(BCODE_JUMP_BACKWARD_IF_TRUE_2) + ON_INST(BCODE_JUMP_BACKWARD_IF_TRUE_3) + LOG_INST_1 (moo, "jump_backward_if_true %zu", (moo_oow_t)(bcode & 0x3)); + /*if (MOO_STACK_GETTOP(moo) == moo->_true) moo->ip -= (bcode & 0x3);*/ /* low 2 bits */ + if (MOO_STACK_GETTOP(moo) != moo->_false) moo->ip -= (bcode & 0x3); + MOO_STACK_POP (moo); + NEXT_INST(); + + ON_INST(BCODE_JUMP_FORWARD_IF_FALSE) + FETCH_PARAM_CODE_TO (moo, b1); + LOG_INST_1 (moo, "jump_forward_if_false %zu", b1); + if (MOO_STACK_GETTOP(moo) == moo->_false) moo->ip += b1; + MOO_STACK_POP (moo); + NEXT_INST(); + + ON_INST(BCODE_JUMP_FORWARD_IF_TRUE) + FETCH_PARAM_CODE_TO (moo, b1); + LOG_INST_1 (moo, "jump_forward_if_true %zu", b1); + /*if (MOO_STACK_GETTOP(moo) == moo->_true) moo->ip += b1;*/ + if (MOO_STACK_GETTOP(moo) != moo->_false) moo->ip += b1; + MOO_STACK_POP (moo); + NEXT_INST(); + + ON_INST(BCODE_JUMP2_FORWARD) + FETCH_PARAM_CODE_TO (moo, b1); + LOG_INST_1 (moo, "jump2_forward %zu", b1); + moo->ip += MAX_CODE_JUMP + b1; + NEXT_INST(); + + ON_INST(BCODE_JUMP2_BACKWARD) + FETCH_PARAM_CODE_TO (moo, b1); + LOG_INST_1 (moo, "jump2_backward %zu", b1); + moo->ip -= MAX_CODE_JUMP + b1; + NEXT_INST(); + + ON_INST(BCODE_JUMP2_FORWARD_IF_FALSE) + FETCH_PARAM_CODE_TO (moo, b1); + LOG_INST_1 (moo, "jump2_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_JUMP2_FORWARD_IF_TRUE) + FETCH_PARAM_CODE_TO (moo, b1); + LOG_INST_1 (moo, "jump2_forward_if_true %zu", b1); + /*if (MOO_STACK_GETTOP(moo) == moo->_true) moo->ip += MAX_CODE_JUMP + b1;*/ + if (MOO_STACK_GETTOP(moo) != moo->_false) moo->ip += MAX_CODE_JUMP + b1; + MOO_STACK_POP (moo); + NEXT_INST(); + + ON_INST(BCODE_JUMP2_BACKWARD_IF_FALSE) + FETCH_PARAM_CODE_TO (moo, b1); + LOG_INST_1 (moo, "jump2_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_JUMP2_BACKWARD_IF_TRUE) + FETCH_PARAM_CODE_TO (moo, b1); + LOG_INST_1 (moo, "jump2_backward_if_true %zu", b1); + /* if (MOO_STACK_GETTOP(moo) == moo->_true) moo->ip -= MAX_CODE_JUMP + 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 */ + ctx->slot[b2] = MOO_STACK_GETTOP(moo); + + if ((bcode >> 2) & 1) + { + /* pop */ + MOO_STACK_POP (moo); + LOG_INST_2 (moo, "pop_into_ctxtempvar %zu %zu", b1, b2); + } + else + { + LOG_INST_2 (moo, "store_into_ctxtempvar %zu %zu", b1, b2); + } + } + else + { + /* push */ + MOO_STACK_PUSH (moo, ctx->slot[b2]); + LOG_INST_2 (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->slot[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 */ + t->slot[b1] = MOO_STACK_GETTOP(moo); + + if ((bcode >> 2) & 1) + { + /* pop */ + MOO_STACK_POP (moo); + LOG_INST_2 (moo, "pop_into_objvar %zu %zu", b1, b2); + } + else + { + LOG_INST_2 (moo, "store_into_objvar %zu %zu", b1, b2); + } + } + else + { + /* push */ + LOG_INST_2 (moo, "push_objvar %zu %zu", b1, b2); + MOO_STACK_PUSH (moo, t->slot[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->slot[b2]; + + LOG_INST_3 (moo, "send_message%hs %zu @%zu", (((bcode >> 2) & 1)? "_to_super": ""), b1, b2); + if (send_message (moo, selector, ((bcode >> 2) & 1), b1) <= -1) return -1; + NEXT_INST(); + } + + /* -------------------------------------------------------- */ + + ON_INST(BCODE_PUSH_RECEIVER) + LOG_INST_0 (moo, "push_receiver"); + MOO_STACK_PUSH (moo, moo->active_context->origin->receiver_or_source); + NEXT_INST(); + + ON_INST(BCODE_PUSH_NIL) + LOG_INST_0 (moo, "push_nil"); + MOO_STACK_PUSH (moo, moo->_nil); + NEXT_INST(); + + ON_INST(BCODE_PUSH_TRUE) + LOG_INST_0 (moo, "push_true"); + MOO_STACK_PUSH (moo, moo->_true); + NEXT_INST(); + + ON_INST(BCODE_PUSH_FALSE) + LOG_INST_0 (moo, "push_false"); + MOO_STACK_PUSH (moo, moo->_false); + NEXT_INST(); + + ON_INST(BCODE_PUSH_CONTEXT) + LOG_INST_0 (moo, "push_context"); + MOO_STACK_PUSH (moo, (moo_oop_t)moo->active_context); + NEXT_INST(); + + ON_INST(BCODE_PUSH_PROCESS) + LOG_INST_0 (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_INST_0 (moo, "push_receiver_ns"); + c = (moo_oop_t)MOO_CLASSOF(moo, moo->active_context->origin->receiver_or_source); + if (c == (moo_oop_t)moo->_class) c = moo->active_context->origin->receiver_or_source; + MOO_STACK_PUSH (moo, (moo_oop_t)((moo_oop_class_t)c)->nsup); + NEXT_INST(); + } + + ON_INST(BCODE_PUSH_NEGONE) + LOG_INST_0 (moo, "push_negone"); + MOO_STACK_PUSH (moo, MOO_SMOOI_TO_OOP(-1)); + NEXT_INST(); + + ON_INST(BCODE_PUSH_ZERO) + LOG_INST_0 (moo, "push_zero"); + MOO_STACK_PUSH (moo, MOO_SMOOI_TO_OOP(0)); + NEXT_INST(); + + ON_INST(BCODE_PUSH_ONE) + LOG_INST_0 (moo, "push_one"); + MOO_STACK_PUSH (moo, MOO_SMOOI_TO_OOP(1)); + NEXT_INST(); + + ON_INST(BCODE_PUSH_TWO) + LOG_INST_0 (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_INST_1 (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_INST_1 (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_INST_1 (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_INST_1 (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, 0, 1) <= -1) return -1; + NEXT_INST(); + + ON_INST(BCODE_POP_INTO_DICTIONARY) + LOG_INST_0 (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, 0, 1) <= -1) return -1; + NEXT_INST(); + + ON_INST(BCODE_MAKE_ARRAY) + { + moo_oop_t t; + + FETCH_PARAM_CODE_TO (moo, b1); + LOG_INST_1 (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_INST_1 (moo, "pop_into_array %zu", b1); + t1 = MOO_STACK_GETTOP(moo); + MOO_STACK_POP (moo); + t2 = MOO_STACK_GETTOP(moo); + ((moo_oop_oop_t)t2)->slot[b1] = t1; + NEXT_INST(); + } + + ON_INST(BCODE_DUP_STACKTOP) + { + moo_oop_t t; + LOG_INST_0 (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_INST_0 (moo, "pop_stacktop"); + MOO_ASSERT (moo, !MOO_STACK_ISEMPTY(moo)); + MOO_STACK_POP (moo); + NEXT_INST(); + + ON_INST(BCODE_RETURN_STACKTOP) + LOG_INST_0 (moo, "return_stacktop"); + return_value = MOO_STACK_GETTOP(moo); + MOO_STACK_POP (moo); + goto handle_return; + + ON_INST(BCODE_RETURN_RECEIVER) + LOG_INST_0 (moo, "return_receiver"); + return_value = moo->active_context->origin->receiver_or_source; + 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_INST_0 (moo, "local_return"); + return_value = MOO_STACK_GETTOP(moo); + MOO_STACK_POP (moo); + goto handle_return; + + ON_INST(BCODE_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_SEND_BLOCK_COPY) + { + moo_ooi_t nargs, ntmprs; + moo_oop_context_t rctx; + moo_oop_context_t blkctx; + + LOG_INST_0 (moo, "send_block_copy"); + + /* it emulates thisContext blockCopy: nargs ofTmprCount: ntmprs */ + MOO_ASSERT (moo, moo->sp >= 2); + + MOO_ASSERT (moo, MOO_CLASSOF(moo, MOO_STACK_GETTOP(moo)) == moo->_small_integer); + ntmprs = MOO_OOP_TO_SMOOI(MOO_STACK_GETTOP(moo)); + MOO_STACK_POP (moo); + + MOO_ASSERT (moo, MOO_CLASSOF(moo, MOO_STACK_GETTOP(moo)) == moo->_small_integer); + nargs = MOO_OOP_TO_SMOOI(MOO_STACK_GETTOP(moo)); + MOO_STACK_POP (moo); + + MOO_ASSERT (moo, nargs >= 0); + MOO_ASSERT (moo, ntmprs >= nargs); + + /* 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. */ + blkctx = (moo_oop_context_t)moo_instantiate (moo, moo->_block_context, MOO_NULL, 0); + if (!blkctx) return -1; + + /* get the receiver to the block copy message after block context instantiation + * not to get affected by potential GC */ + rctx = (moo_oop_context_t)MOO_STACK_GETTOP(moo); + MOO_ASSERT (moo, rctx == moo->active_context); + + /* [NOTE] + * blkctx->sender is left to nil. it is set to the + * active context before it gets activated. see + * pf_block_value(). + * + * blkctx->home is set here to the active context. + * it's redundant to have them pushed to the stack + * though it is to emulate the message sending of + * blockCopy:withNtmprs:. BCODE_MAKE_BLOCK has been + * added to replace BCODE_SEND_BLOCK_COPY and pusing + * arguments to the stack. + * + * blkctx->origin is set here by copying the origin + * of the active context. + */ + + /* the extended jump instruction has the format of + * 0000XXXX KKKKKKKK or 0000XXXX KKKKKKKK KKKKKKKK + * depending on MOO_BCODE_LONG_PARAM_SIZE. change 'ip' to point to + * the instruction after the jump. */ + blkctx->ip = MOO_SMOOI_TO_OOP(moo->ip + MOO_BCODE_LONG_PARAM_SIZE + 1); + blkctx->sp = MOO_SMOOI_TO_OOP(-1); + /* the number of arguments for a block context is local to the block */ + blkctx->method_or_nargs = MOO_SMOOI_TO_OOP(nargs); + /* the number of temporaries here is an accumulated count including + * the number of temporaries of a home context */ + blkctx->ntmprs = MOO_SMOOI_TO_OOP(ntmprs); + + blkctx->home = (moo_oop_t)rctx; + blkctx->receiver_or_source = moo->_nil; + + /* [NOTE] + * the origin of a method context is set to itself + * when it's created. so it's safe to simply copy + * the origin field this way. + * + * if the context that receives the blockCopy message + * is a method context, the following conditions are all true. + * rctx->home == moo->_nil + * MOO_CLASSOF(moo, rctx) == moo->_method_context + * rctx == (moo_oop_t)moo->active_context + * rctx == rctx->origin + * + * if it is a block context, the following condition is true. + * MOO_CLASSOF(moo, rctx) == moo->_block_context + */ + blkctx->origin = rctx->origin; + + MOO_STACK_SETTOP (moo, (moo_oop_t)blkctx); + NEXT_INST(); + } + + ON_INST(BCODE_NOOP) + /* do nothing */ + LOG_INST_0 (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 0; } diff --git a/moo/lib/gc.c b/moo/lib/gc.c index 136b949..9a4af68 100644 --- a/moo/lib/gc.c +++ b/moo/lib/gc.c @@ -833,9 +833,9 @@ void moo_gc (moo_t* moo) old_nil = moo->_nil; /* move _nil and the root object table */ - moo->_nil = moo_moveoop (moo, moo->_nil); - moo->_true = moo_moveoop (moo, moo->_true); - moo->_false = moo_moveoop (moo, moo->_false); + moo->_nil = moo_moveoop (moo, moo->_nil); + moo->_true = moo_moveoop (moo, moo->_true); + moo->_false = moo_moveoop (moo, moo->_false); for (i = 0; i < MOO_COUNTOF(kernel_classes); i++) { @@ -878,7 +878,6 @@ void moo_gc (moo_t* moo) *moo->tmp_stack[i] = moo_moveoop (moo, *moo->tmp_stack[i]); } - if (moo->initial_context) moo->initial_context = (moo_oop_context_t)moo_moveoop (moo, (moo_oop_t)moo->initial_context); if (moo->active_context) @@ -953,7 +952,6 @@ void moo_gc (moo_t* moo) moo->curheap->base, moo->curheap->ptr, moo->newheap->base, moo->newheap->ptr); } - void moo_pushtmp (moo_t* moo, moo_oop_t* oop_ptr) { /* if you have too many temporaries pushed, something must be wrong. @@ -974,7 +972,6 @@ void moo_poptmps (moo_t* moo, moo_oow_t count) moo->tmp_count -= count; } - moo_oop_t moo_shallowcopy (moo_t* moo, moo_oop_t oop) { if (MOO_OOP_IS_POINTER(oop) && MOO_OBJ_GET_CLASS(oop) != moo->_symbol) @@ -1018,7 +1015,6 @@ int moo_regfinalizable (moo_t* moo, moo_oop_t oop) { moo_finalizable_t* x; - if (!MOO_OOP_IS_POINTER(oop) || (MOO_OBJ_GET_FLAGS_GCFIN(oop) & (MOO_GCFIN_FINALIZABLE | MOO_GCFIN_FINALIZED))) { moo_seterrnum (moo, MOO_EINVAL); diff --git a/moo/lib/genbct.awk b/moo/lib/genbct.awk new file mode 100644 index 0000000..c0c0258 --- /dev/null +++ b/moo/lib/genbct.awk @@ -0,0 +1,41 @@ +# generate instruction dispatch table for VM. +# qseawk -f genbct.awk moo-prv.h +# +# TODO: rewrite this script in moo itself. +# + +BEGIN { + on = 0; + for (i = 0; i < 255; i++) code_label[i] = "&&case_DEFAULT"; +} + +/^enum moo_bcode_t$/ { on = 1; } + +/^enum moo_bcode_t {$/ { on = 2; } + +/^{$/ { if (on == 1) on = 2; else on = 0; } + +/^};$/ { on = 0; } + +on == 2 && match($0, /^[[:space:]]*BCODE_([[:alnum:]_]+)[[:space:]]*=[[:space:]]*(0x[[:xdigit:]]+)/, 1, sm) >= 1 { + count = length(sm) \ 2; + if (count == 2) + { + name = substr ($0, sm[1,"start"], sm[1,"length"]); + code = substr ($0, sm[2,"start"], sm[2,"length"]); + code_label[int(code)] = "&&case_BCODE_" name; + } +} + +END { + printf ("/* generated by genbct.awk */\n\n"); + + ##printf ("\tstatic void* inst_jump_labels[] =\n"); + ##printf ("\t{\n"); + for (i = 0; i < 255; i++) + { + printf ("\t\t/* %3d */ %s,\n", i, code_label[i]); + } + printf ("\t\t/* %3d */ %s\n", i, code_label[i]); + ##printf ("\t};\n"); +} diff --git a/moo/lib/logfmtv.h b/moo/lib/logfmtv.h index a42cb3b..8589274 100644 --- a/moo/lib/logfmtv.h +++ b/moo/lib/logfmtv.h @@ -240,6 +240,7 @@ reswitch: case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': + if (flagc & FLAGC_LENMOD) goto invalid_format; for (n = 0;; ++fmt) { n = n * 10 + ch - '0'; @@ -359,6 +360,9 @@ reswitch: case 'x': base = 16; goto handle_nosign; + case 'b': + base = 2; + goto handle_nosign; /* end of unsigned integer conversions */ case 'p': /* pointer */ @@ -889,6 +893,11 @@ number: if ((flagc & FLAGC_SHARP) && num != 0) { + if (base == 2) + { + PUT_OOCH ('0', 1); + PUT_OOCH ('b', 1); + } if (base == 8) { PUT_OOCH ('0', 1); diff --git a/moo/lib/moo-bct.h b/moo/lib/moo-bct.h new file mode 100644 index 0000000..035dde9 --- /dev/null +++ b/moo/lib/moo-bct.h @@ -0,0 +1,258 @@ +/* generated by genbct.awk */ + + /* 0 */ &&case_BCODE_STORE_INTO_INSTVAR_0, + /* 1 */ &&case_BCODE_STORE_INTO_INSTVAR_1, + /* 2 */ &&case_BCODE_STORE_INTO_INSTVAR_2, + /* 3 */ &&case_BCODE_STORE_INTO_INSTVAR_3, + /* 4 */ &&case_BCODE_STORE_INTO_INSTVAR_4, + /* 5 */ &&case_BCODE_STORE_INTO_INSTVAR_5, + /* 6 */ &&case_BCODE_STORE_INTO_INSTVAR_6, + /* 7 */ &&case_BCODE_STORE_INTO_INSTVAR_7, + /* 8 */ &&case_BCODE_POP_INTO_INSTVAR_0, + /* 9 */ &&case_BCODE_POP_INTO_INSTVAR_1, + /* 10 */ &&case_BCODE_POP_INTO_INSTVAR_2, + /* 11 */ &&case_BCODE_POP_INTO_INSTVAR_3, + /* 12 */ &&case_BCODE_POP_INTO_INSTVAR_4, + /* 13 */ &&case_BCODE_POP_INTO_INSTVAR_5, + /* 14 */ &&case_BCODE_POP_INTO_INSTVAR_6, + /* 15 */ &&case_BCODE_POP_INTO_INSTVAR_7, + /* 16 */ &&case_BCODE_PUSH_INSTVAR_0, + /* 17 */ &&case_BCODE_PUSH_INSTVAR_1, + /* 18 */ &&case_BCODE_PUSH_INSTVAR_2, + /* 19 */ &&case_BCODE_PUSH_INSTVAR_3, + /* 20 */ &&case_BCODE_PUSH_INSTVAR_4, + /* 21 */ &&case_BCODE_PUSH_INSTVAR_5, + /* 22 */ &&case_BCODE_PUSH_INSTVAR_6, + /* 23 */ &&case_BCODE_PUSH_INSTVAR_7, + /* 24 */ &&case_BCODE_PUSH_TEMPVAR_0, + /* 25 */ &&case_BCODE_PUSH_TEMPVAR_1, + /* 26 */ &&case_BCODE_PUSH_TEMPVAR_2, + /* 27 */ &&case_BCODE_PUSH_TEMPVAR_3, + /* 28 */ &&case_BCODE_PUSH_TEMPVAR_4, + /* 29 */ &&case_BCODE_PUSH_TEMPVAR_5, + /* 30 */ &&case_BCODE_PUSH_TEMPVAR_6, + /* 31 */ &&case_BCODE_PUSH_TEMPVAR_7, + /* 32 */ &&case_BCODE_STORE_INTO_TEMPVAR_0, + /* 33 */ &&case_BCODE_STORE_INTO_TEMPVAR_1, + /* 34 */ &&case_BCODE_STORE_INTO_TEMPVAR_2, + /* 35 */ &&case_BCODE_STORE_INTO_TEMPVAR_3, + /* 36 */ &&case_BCODE_STORE_INTO_TEMPVAR_4, + /* 37 */ &&case_BCODE_STORE_INTO_TEMPVAR_5, + /* 38 */ &&case_BCODE_STORE_INTO_TEMPVAR_6, + /* 39 */ &&case_BCODE_STORE_INTO_TEMPVAR_7, + /* 40 */ &&case_BCODE_POP_INTO_TEMPVAR_0, + /* 41 */ &&case_BCODE_POP_INTO_TEMPVAR_1, + /* 42 */ &&case_BCODE_POP_INTO_TEMPVAR_2, + /* 43 */ &&case_BCODE_POP_INTO_TEMPVAR_3, + /* 44 */ &&case_BCODE_POP_INTO_TEMPVAR_4, + /* 45 */ &&case_BCODE_POP_INTO_TEMPVAR_5, + /* 46 */ &&case_BCODE_POP_INTO_TEMPVAR_6, + /* 47 */ &&case_BCODE_POP_INTO_TEMPVAR_7, + /* 48 */ &&case_BCODE_PUSH_LITERAL_0, + /* 49 */ &&case_BCODE_PUSH_LITERAL_1, + /* 50 */ &&case_BCODE_PUSH_LITERAL_2, + /* 51 */ &&case_BCODE_PUSH_LITERAL_3, + /* 52 */ &&case_BCODE_PUSH_LITERAL_4, + /* 53 */ &&case_BCODE_PUSH_LITERAL_5, + /* 54 */ &&case_BCODE_PUSH_LITERAL_6, + /* 55 */ &&case_BCODE_PUSH_LITERAL_7, + /* 56 */ &&case_BCODE_STORE_INTO_OBJECT_0, + /* 57 */ &&case_BCODE_STORE_INTO_OBJECT_1, + /* 58 */ &&case_BCODE_STORE_INTO_OBJECT_2, + /* 59 */ &&case_BCODE_STORE_INTO_OBJECT_3, + /* 60 */ &&case_BCODE_POP_INTO_OBJECT_0, + /* 61 */ &&case_BCODE_POP_INTO_OBJECT_1, + /* 62 */ &&case_BCODE_POP_INTO_OBJECT_2, + /* 63 */ &&case_BCODE_POP_INTO_OBJECT_3, + /* 64 */ &&case_BCODE_PUSH_OBJECT_0, + /* 65 */ &&case_BCODE_PUSH_OBJECT_1, + /* 66 */ &&case_BCODE_PUSH_OBJECT_2, + /* 67 */ &&case_BCODE_PUSH_OBJECT_3, + /* 68 */ &&case_BCODE_JUMP_FORWARD_0, + /* 69 */ &&case_BCODE_JUMP_FORWARD_1, + /* 70 */ &&case_BCODE_JUMP_FORWARD_2, + /* 71 */ &&case_BCODE_JUMP_FORWARD_3, + /* 72 */ &&case_BCODE_JUMP_BACKWARD_0, + /* 73 */ &&case_BCODE_JUMP_BACKWARD_1, + /* 74 */ &&case_BCODE_JUMP_BACKWARD_2, + /* 75 */ &&case_BCODE_JUMP_BACKWARD_3, + /* 76 */ &&case_BCODE_JUMP_BACKWARD_IF_FALSE_0, + /* 77 */ &&case_BCODE_JUMP_BACKWARD_IF_FALSE_1, + /* 78 */ &&case_BCODE_JUMP_BACKWARD_IF_FALSE_2, + /* 79 */ &&case_BCODE_JUMP_BACKWARD_IF_FALSE_3, + /* 80 */ &&case_BCODE_JUMP_BACKWARD_IF_TRUE_0, + /* 81 */ &&case_BCODE_JUMP_BACKWARD_IF_TRUE_1, + /* 82 */ &&case_BCODE_JUMP_BACKWARD_IF_TRUE_2, + /* 83 */ &&case_BCODE_JUMP_BACKWARD_IF_TRUE_3, + /* 84 */ &&case_DEFAULT, + /* 85 */ &&case_DEFAULT, + /* 86 */ &&case_DEFAULT, + /* 87 */ &&case_DEFAULT, + /* 88 */ &&case_BCODE_STORE_INTO_CTXTEMPVAR_0, + /* 89 */ &&case_BCODE_STORE_INTO_CTXTEMPVAR_1, + /* 90 */ &&case_BCODE_STORE_INTO_CTXTEMPVAR_2, + /* 91 */ &&case_BCODE_STORE_INTO_CTXTEMPVAR_3, + /* 92 */ &&case_BCODE_POP_INTO_CTXTEMPVAR_0, + /* 93 */ &&case_BCODE_POP_INTO_CTXTEMPVAR_1, + /* 94 */ &&case_BCODE_POP_INTO_CTXTEMPVAR_2, + /* 95 */ &&case_BCODE_POP_INTO_CTXTEMPVAR_3, + /* 96 */ &&case_BCODE_PUSH_CTXTEMPVAR_0, + /* 97 */ &&case_BCODE_PUSH_CTXTEMPVAR_1, + /* 98 */ &&case_BCODE_PUSH_CTXTEMPVAR_2, + /* 99 */ &&case_BCODE_PUSH_CTXTEMPVAR_3, + /* 100 */ &&case_BCODE_PUSH_OBJVAR_0, + /* 101 */ &&case_BCODE_PUSH_OBJVAR_1, + /* 102 */ &&case_BCODE_PUSH_OBJVAR_2, + /* 103 */ &&case_BCODE_PUSH_OBJVAR_3, + /* 104 */ &&case_BCODE_STORE_INTO_OBJVAR_0, + /* 105 */ &&case_BCODE_STORE_INTO_OBJVAR_1, + /* 106 */ &&case_BCODE_STORE_INTO_OBJVAR_2, + /* 107 */ &&case_BCODE_STORE_INTO_OBJVAR_3, + /* 108 */ &&case_BCODE_POP_INTO_OBJVAR_0, + /* 109 */ &&case_BCODE_POP_INTO_OBJVAR_1, + /* 110 */ &&case_BCODE_POP_INTO_OBJVAR_2, + /* 111 */ &&case_BCODE_POP_INTO_OBJVAR_3, + /* 112 */ &&case_BCODE_SEND_MESSAGE_0, + /* 113 */ &&case_BCODE_SEND_MESSAGE_1, + /* 114 */ &&case_BCODE_SEND_MESSAGE_2, + /* 115 */ &&case_BCODE_SEND_MESSAGE_3, + /* 116 */ &&case_BCODE_SEND_MESSAGE_TO_SUPER_0, + /* 117 */ &&case_BCODE_SEND_MESSAGE_TO_SUPER_1, + /* 118 */ &&case_BCODE_SEND_MESSAGE_TO_SUPER_2, + /* 119 */ &&case_BCODE_SEND_MESSAGE_TO_SUPER_3, + /* 120 */ &&case_DEFAULT, + /* 121 */ &&case_DEFAULT, + /* 122 */ &&case_DEFAULT, + /* 123 */ &&case_DEFAULT, + /* 124 */ &&case_DEFAULT, + /* 125 */ &&case_DEFAULT, + /* 126 */ &&case_DEFAULT, + /* 127 */ &&case_DEFAULT, + /* 128 */ &&case_BCODE_STORE_INTO_INSTVAR_X, + /* 129 */ &&case_BCODE_PUSH_RECEIVER, + /* 130 */ &&case_BCODE_PUSH_NIL, + /* 131 */ &&case_BCODE_PUSH_TRUE, + /* 132 */ &&case_BCODE_PUSH_FALSE, + /* 133 */ &&case_BCODE_PUSH_CONTEXT, + /* 134 */ &&case_BCODE_PUSH_PROCESS, + /* 135 */ &&case_BCODE_PUSH_RECEIVER_NS, + /* 136 */ &&case_BCODE_POP_INTO_INSTVAR_X, + /* 137 */ &&case_BCODE_PUSH_NEGONE, + /* 138 */ &&case_BCODE_PUSH_ZERO, + /* 139 */ &&case_BCODE_PUSH_ONE, + /* 140 */ &&case_BCODE_PUSH_TWO, + /* 141 */ &&case_DEFAULT, + /* 142 */ &&case_DEFAULT, + /* 143 */ &&case_DEFAULT, + /* 144 */ &&case_BCODE_PUSH_INSTVAR_X, + /* 145 */ &&case_DEFAULT, + /* 146 */ &&case_DEFAULT, + /* 147 */ &&case_DEFAULT, + /* 148 */ &&case_DEFAULT, + /* 149 */ &&case_DEFAULT, + /* 150 */ &&case_DEFAULT, + /* 151 */ &&case_DEFAULT, + /* 152 */ &&case_BCODE_PUSH_TEMPVAR_X, + /* 153 */ &&case_DEFAULT, + /* 154 */ &&case_DEFAULT, + /* 155 */ &&case_DEFAULT, + /* 156 */ &&case_DEFAULT, + /* 157 */ &&case_DEFAULT, + /* 158 */ &&case_DEFAULT, + /* 159 */ &&case_DEFAULT, + /* 160 */ &&case_BCODE_STORE_INTO_TEMPVAR_X, + /* 161 */ &&case_DEFAULT, + /* 162 */ &&case_DEFAULT, + /* 163 */ &&case_DEFAULT, + /* 164 */ &&case_DEFAULT, + /* 165 */ &&case_DEFAULT, + /* 166 */ &&case_DEFAULT, + /* 167 */ &&case_DEFAULT, + /* 168 */ &&case_BCODE_POP_INTO_TEMPVAR_X, + /* 169 */ &&case_DEFAULT, + /* 170 */ &&case_DEFAULT, + /* 171 */ &&case_DEFAULT, + /* 172 */ &&case_DEFAULT, + /* 173 */ &&case_DEFAULT, + /* 174 */ &&case_DEFAULT, + /* 175 */ &&case_DEFAULT, + /* 176 */ &&case_BCODE_PUSH_LITERAL_X, + /* 177 */ &&case_DEFAULT, + /* 178 */ &&case_BCODE_PUSH_INTLIT, + /* 179 */ &&case_BCODE_PUSH_NEGINTLIT, + /* 180 */ &&case_BCODE_PUSH_CHARLIT, + /* 181 */ &&case_DEFAULT, + /* 182 */ &&case_DEFAULT, + /* 183 */ &&case_DEFAULT, + /* 184 */ &&case_BCODE_STORE_INTO_OBJECT_X, + /* 185 */ &&case_DEFAULT, + /* 186 */ &&case_DEFAULT, + /* 187 */ &&case_DEFAULT, + /* 188 */ &&case_BCODE_POP_INTO_OBJECT_X, + /* 189 */ &&case_DEFAULT, + /* 190 */ &&case_DEFAULT, + /* 191 */ &&case_DEFAULT, + /* 192 */ &&case_BCODE_PUSH_OBJECT_X, + /* 193 */ &&case_DEFAULT, + /* 194 */ &&case_DEFAULT, + /* 195 */ &&case_DEFAULT, + /* 196 */ &&case_BCODE_JUMP_FORWARD_X, + /* 197 */ &&case_BCODE_JUMP2_FORWARD, + /* 198 */ &&case_DEFAULT, + /* 199 */ &&case_DEFAULT, + /* 200 */ &&case_BCODE_JUMP_BACKWARD_X, + /* 201 */ &&case_BCODE_JUMP2_BACKWARD, + /* 202 */ &&case_DEFAULT, + /* 203 */ &&case_DEFAULT, + /* 204 */ &&case_BCODE_JUMP_BACKWARD_IF_FALSE_X, + /* 205 */ &&case_BCODE_JUMP2_BACKWARD_IF_FALSE, + /* 206 */ &&case_DEFAULT, + /* 207 */ &&case_DEFAULT, + /* 208 */ &&case_BCODE_JUMP_BACKWARD_IF_TRUE_X, + /* 209 */ &&case_BCODE_JUMP2_BACKWARD_IF_TRUE, + /* 210 */ &&case_DEFAULT, + /* 211 */ &&case_DEFAULT, + /* 212 */ &&case_BCODE_JUMP_FORWARD_IF_FALSE, + /* 213 */ &&case_BCODE_JUMP2_FORWARD_IF_FALSE, + /* 214 */ &&case_BCODE_JUMP_FORWARD_IF_TRUE, + /* 215 */ &&case_BCODE_JUMP2_FORWARD_IF_TRUE, + /* 216 */ &&case_BCODE_STORE_INTO_CTXTEMPVAR_X, + /* 217 */ &&case_DEFAULT, + /* 218 */ &&case_DEFAULT, + /* 219 */ &&case_DEFAULT, + /* 220 */ &&case_BCODE_POP_INTO_CTXTEMPVAR_X, + /* 221 */ &&case_DEFAULT, + /* 222 */ &&case_DEFAULT, + /* 223 */ &&case_DEFAULT, + /* 224 */ &&case_BCODE_PUSH_CTXTEMPVAR_X, + /* 225 */ &&case_DEFAULT, + /* 226 */ &&case_DEFAULT, + /* 227 */ &&case_DEFAULT, + /* 228 */ &&case_BCODE_PUSH_OBJVAR_X, + /* 229 */ &&case_DEFAULT, + /* 230 */ &&case_DEFAULT, + /* 231 */ &&case_DEFAULT, + /* 232 */ &&case_BCODE_STORE_INTO_OBJVAR_X, + /* 233 */ &&case_DEFAULT, + /* 234 */ &&case_DEFAULT, + /* 235 */ &&case_DEFAULT, + /* 236 */ &&case_BCODE_POP_INTO_OBJVAR_X, + /* 237 */ &&case_DEFAULT, + /* 238 */ &&case_DEFAULT, + /* 239 */ &&case_DEFAULT, + /* 240 */ &&case_BCODE_SEND_MESSAGE_X, + /* 241 */ &&case_DEFAULT, + /* 242 */ &&case_BCODE_MAKE_DICTIONARY, + /* 243 */ &&case_BCODE_POP_INTO_DICTIONARY, + /* 244 */ &&case_BCODE_SEND_MESSAGE_TO_SUPER_X, + /* 245 */ &&case_BCODE_MAKE_ARRAY, + /* 246 */ &&case_BCODE_POP_INTO_ARRAY, + /* 247 */ &&case_BCODE_DUP_STACKTOP, + /* 248 */ &&case_BCODE_POP_STACKTOP, + /* 249 */ &&case_BCODE_RETURN_STACKTOP, + /* 250 */ &&case_BCODE_RETURN_RECEIVER, + /* 251 */ &&case_BCODE_RETURN_FROM_BLOCK, + /* 252 */ &&case_BCODE_LOCAL_RETURN, + /* 253 */ &&case_BCODE_MAKE_BLOCK, + /* 254 */ &&case_BCODE_SEND_BLOCK_COPY, + /* 255 */ &&case_BCODE_NOOP diff --git a/moo/lib/moo-cfg.h.in b/moo/lib/moo-cfg.h.in index fb5882b..a72f37c 100644 --- a/moo/lib/moo-cfg.h.in +++ b/moo/lib/moo-cfg.h.in @@ -57,9 +57,6 @@ /* Define to 1 if you have the `closedir' function. */ #undef HAVE_CLOSEDIR -/* computed gotos */ -#undef HAVE_COMPUTED_GOTO - /* Define to 1 if you have the `coshq' function. */ #undef HAVE_COSHQ @@ -133,6 +130,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H +/* labels as values */ +#undef HAVE_LABELS_AS_VALUES + /* Define if you have the libdl library or equivalent. */ #undef HAVE_LIBDL diff --git a/moo/lib/moo.h b/moo/lib/moo.h index 7a33f4a..5c984f4 100644 --- a/moo/lib/moo.h +++ b/moo/lib/moo.h @@ -748,10 +748,14 @@ struct moo_context_t typedef struct moo_process_t moo_process_t; typedef struct moo_process_t* moo_oop_process_t; -#define MOO_SEMAPHORE_NAMED_INSTVARS 9 +#define MOO_SEMAPHORE_NAMED_INSTVARS 10 typedef struct moo_semaphore_t moo_semaphore_t; typedef struct moo_semaphore_t* moo_oop_semaphore_t; +#define MOO_SEMAPHORE_GROUP_NAMED_INSTVARS 5 +typedef struct moo_semaphore_group_t moo_semaphore_group_t; +typedef struct moo_semaphore_group_t* moo_oop_semaphore_group_t; + struct moo_process_t { MOO_OBJ_HEADER; @@ -774,9 +778,9 @@ struct moo_process_t moo_oop_process_t next; } sem_wait; /* links to use with a semaphore */ - moo_oop_semaphore_t sem; - moo_oop_t perr; /* last error set by a primitive function */ - moo_oop_t perrmsg; + moo_oop_t sem; /* nil, semaphore, or semaphore group */ + moo_oop_t perr; /* last error set by a primitive function */ + moo_oop_t perrmsg; /* == variable indexed part == */ moo_oop_t slot[1]; /* process stack */ @@ -791,13 +795,14 @@ struct moo_semaphore_t { MOO_OBJ_HEADER; - moo_oop_t count; /* SmallInteger */ - + /* [IMPORTANT] make sure that the position of 'waiting' in moo_semaphore_t + * must be exactly the same as its position in moo_semaphore_group_t */ struct { moo_oop_process_t first; moo_oop_process_t last; } waiting; /* list of processes waiting on this semaphore */ + moo_oop_t count; /* SmallInteger */ moo_oop_t heap_index; /* index to the heap */ moo_oop_t heap_ftime_sec; /* firing time */ @@ -806,15 +811,24 @@ struct moo_semaphore_t moo_oop_t io_index; moo_oop_t io_handle; moo_oop_t io_mask; /* SmallInteger */ + + moo_oop_semaphore_group_t group; /* nil or belonging semaphore group */ }; -#define MOO_SEMAPHORE_GROUP_NAMED_INSTVARS 2 -typedef struct moo_semaphore_group_t moo_semaphore_group_t; -typedef struct moo_semaphore_group_t* moo_oop_semaphore_group_t; struct moo_semaphore_group_t { MOO_OBJ_HEADER; + + /* [IMPORTANT] make sure that the position of 'waiting' in moo_semaphore_group_t + * must be exactly the same as its position in moo_semaphore_t */ + struct + { + moo_oop_process_t first; + moo_oop_process_t last; /* list of processes waiting on this semaphore group */ + } waiting; + moo_oop_t size; /* SmallInteger */ + moo_oop_t pos; /* current processing position */ moo_oop_oop_t semarr; /* Array of Semaphores */ };