1806 lines
50 KiB
C
1806 lines
50 KiB
C
/*
|
|
* $Id$
|
|
*
|
|
Copyright (c) 2014-2015 Chung, Hyung-Hwan. All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions
|
|
are met:
|
|
1. Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
2. Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
|
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WAfRRANTIES
|
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "stix-prv.h"
|
|
|
|
/* TODO: context's stack overflow check in various part of this file */
|
|
/* TOOD: determine the right stack size */
|
|
#define CONTEXT_STACK_SIZE 96
|
|
|
|
#define LOAD_IP(stix, v_ctx) ((stix)->ip = STIX_OOP_TO_SMINT((v_ctx)->ip))
|
|
#define STORE_IP(stix, v_ctx) ((v_ctx)->ip = STIX_OOP_FROM_SMINT((stix)->ip))
|
|
|
|
#define LOAD_SP(stix, v_ctx) ((stix)->sp = STIX_OOP_TO_SMINT((v_ctx)->sp))
|
|
#define STORE_SP(stix, v_ctx) ((v_ctx)->sp = STIX_OOP_FROM_SMINT((stix)->sp))
|
|
|
|
#define LOAD_ACTIVE_IP(stix) LOAD_IP(stix, (stix)->active_context)
|
|
#define STORE_ACTIVE_IP(stix) STORE_IP(stix, (stix)->active_context)
|
|
|
|
#define LOAD_ACTIVE_SP(stix) LOAD_SP(stix, (stix)->active_context)
|
|
#define STORE_ACTIVE_SP(stix) STORE_SP(stix, (stix)->active_context)
|
|
|
|
|
|
#define ACTIVE_STACK_PUSH(stix,v) \
|
|
do { \
|
|
(stix)->sp = (stix)->sp + 1; \
|
|
(stix)->active_context->slot[(stix)->sp] = v; \
|
|
} while (0)
|
|
|
|
#define ACTIVE_STACK_POP(stix) ((stix)->sp = (stix)->sp - 1)
|
|
#define ACTIVE_STACK_UNPOP(stix) ((stix)->sp = (stix)->sp + 1)
|
|
#define ACTIVE_STACK_POPS(stix,count) ((stix)->sp = (stix)->sp - (count))
|
|
|
|
#define ACTIVE_STACK_GET(stix,v_sp) ((stix)->active_context->slot[v_sp])
|
|
#define ACTIVE_STACK_SET(stix,v_sp,v_obj) ((stix)->active_context->slot[v_sp] = v_obj)
|
|
#define ACTIVE_STACK_GETTOP(stix) ACTIVE_STACK_GET(stix, (stix)->sp)
|
|
#define ACTIVE_STACK_SETTOP(stix,v_obj) ACTIVE_STACK_SET(stix, (stix)->sp, v_obj)
|
|
|
|
#define ACTIVE_STACK_ISEMPTY(stix) ((stix)->sp <= -1)
|
|
|
|
#define SWITCH_ACTIVE_CONTEXT(stix,v_ctx) \
|
|
do \
|
|
{ \
|
|
STORE_ACTIVE_IP (stix); \
|
|
STORE_ACTIVE_SP (stix); \
|
|
(stix)->active_context = (v_ctx); \
|
|
(stix)->active_method = (stix_oop_method_t)(stix)->active_context->origin->method_or_nargs; \
|
|
SET_ACTIVE_METHOD_CODE(stix); \
|
|
LOAD_ACTIVE_IP (stix); \
|
|
LOAD_ACTIVE_SP (stix); \
|
|
} while (0) \
|
|
|
|
#define FETCH_BYTE_CODE(stix) ((stix)->active_code[(stix)->ip++])
|
|
#define FETCH_BYTE_CODE_TO(stix, v_ooi) (v_ooi = FETCH_BYTE_CODE(stix))
|
|
#if (STIX_BCODE_LONG_PARAM_SIZE == 2)
|
|
#define FETCH_PARAM_CODE_TO(stix, v_ooi) \
|
|
do { \
|
|
v_ooi = FETCH_BYTE_CODE(stix); \
|
|
v_ooi = (v_ooi << 8) | FETCH_BYTE_CODE(stix); \
|
|
} while (0)
|
|
#else
|
|
#define FETCH_PARAM_CODE_TO(stix, v_ooi) (v_ooi = FETCH_BYTE_CODE(stix))
|
|
#endif
|
|
|
|
|
|
static STIX_INLINE int activate_new_method (stix_t* stix, stix_oop_method_t mth)
|
|
{
|
|
stix_oop_context_t ctx;
|
|
stix_ooi_t i;
|
|
stix_ooi_t ntmprs, nargs;
|
|
|
|
/* message sending requires a receiver to be pushed.
|
|
* the stack pointer of the sending context cannot be -1.
|
|
* if one-argumented message is invoked the stack of the
|
|
* sending context looks like this.
|
|
*
|
|
* Sending Context
|
|
*
|
|
* +---------------------+
|
|
* | fixed part |
|
|
* | |
|
|
* | |
|
|
* | |
|
|
* +---------------------+
|
|
* | .... | slot[0]
|
|
* | .... | slot[..]
|
|
* | .... | slot[..]
|
|
* | receiver | slot[..] <-- sp - nargs(1)
|
|
* | arg1 | slot[..] <-- sp
|
|
* | .... | slot[..]
|
|
* | | slot[stack_size - 1]
|
|
* +---------------------+
|
|
*/
|
|
ntmprs = STIX_OOP_TO_SMINT(mth->tmpr_count);
|
|
nargs = STIX_OOP_TO_SMINT(mth->tmpr_nargs);
|
|
|
|
STIX_ASSERT (ntmprs >= 0);
|
|
STIX_ASSERT (nargs <= ntmprs);
|
|
STIX_ASSERT (stix->sp >= 0);
|
|
STIX_ASSERT (stix->sp >= nargs);
|
|
|
|
stix_pushtmp (stix, (stix_oop_t*)&mth);
|
|
ctx = (stix_oop_context_t)stix_instantiate (stix, stix->_method_context, STIX_NULL, CONTEXT_STACK_SIZE);
|
|
stix_poptmp (stix);
|
|
if (!ctx) return -1;
|
|
|
|
ctx->sender = (stix_oop_t)stix->active_context;
|
|
ctx->ip = STIX_OOP_FROM_SMINT(0);
|
|
/* the stack front has temporary variables including arguments.
|
|
*
|
|
* New Context
|
|
*
|
|
* +---------------------+
|
|
* | fixed part |
|
|
* | |
|
|
* | |
|
|
* | |
|
|
* +---------------------+
|
|
* | tmp1 (arg1) | slot[0]
|
|
* | tmp2 (arg2) | slot[1]
|
|
* | .... | slot[..]
|
|
* | tmpX | slot[..] <-- initial sp
|
|
* | | slot[..]
|
|
* | | slot[..]
|
|
* | | slot[stack_size - 2]
|
|
* | | slot[stack_size - 1]
|
|
* +---------------------+
|
|
*
|
|
* if no temporaries exist, the initial sp is -1.
|
|
*/
|
|
ctx->sp = STIX_OOP_FROM_SMINT(ntmprs - 1);
|
|
ctx->ntmprs = STIX_OOP_FROM_SMINT(ntmprs);
|
|
ctx->method_or_nargs = (stix_oop_t)mth;
|
|
/* the 'home' field of a method context is always stix->_nil.
|
|
ctx->home = stix->_nil;*/
|
|
ctx->origin = ctx; /* point to self */
|
|
|
|
/*
|
|
* Assume this message sending expression:
|
|
* obj1 do: #this with: #that with: #it
|
|
*
|
|
* It would be compiled to these logical byte-code sequences shown below:
|
|
* push obj1
|
|
* push #this
|
|
* push #that
|
|
* push #it
|
|
* send #do:with:
|
|
*
|
|
* After three pushes, the stack looks like this.
|
|
*
|
|
* | #it | <- sp
|
|
* | #that | sp - 1
|
|
* | #this | sp - 2
|
|
* | obj1 | sp - nargs
|
|
*
|
|
* Since the number of arguments is 3, stack[sp - 3] points to
|
|
* the receiver. When the stack is empty, sp is -1.
|
|
*/
|
|
for (i = nargs; i > 0; )
|
|
{
|
|
/* copy argument */
|
|
ctx->slot[--i] = ACTIVE_STACK_GETTOP (stix);
|
|
ACTIVE_STACK_POP (stix);
|
|
}
|
|
/* copy receiver */
|
|
ctx->receiver_or_source = ACTIVE_STACK_GETTOP (stix);
|
|
ACTIVE_STACK_POP (stix);
|
|
|
|
STIX_ASSERT (stix->sp >= -1);
|
|
|
|
/* swtich the active context */
|
|
SWITCH_ACTIVE_CONTEXT (stix, ctx);
|
|
|
|
printf ("<<ENTERING>> SP=%d\n", (int)stix->sp);
|
|
return 0;
|
|
|
|
#if 0
|
|
reuse_context:
|
|
/* force the class to become a method context */
|
|
ctx->_class = stix->_method_context;
|
|
|
|
ctx->receiver_or_source = ACTIVE_STACK_GET(stix, stix->sp - nargs);
|
|
printf ("####### REUSING CONTEXT INSTEAD OF <<ENTERING>> WITH RECEIVER ");
|
|
print_object (stix, ctx->receiver_or_source);
|
|
printf ("\n");
|
|
|
|
for (i = 0; i < nargs; i++)
|
|
{
|
|
ctx->slot[i] = ACTIVE_STACK_GET (stix, stix->sp - nargs + i + 1);
|
|
printf ("REUSING ARGUMENT %d - ", (int)i);
|
|
print_object (stix, ctx->slot[i]);
|
|
printf ("\n");
|
|
}
|
|
for (; i <= stix->sp; i++) ctx->slot[i] = stix->_nil;
|
|
/* keep the sender
|
|
ctx->sender =
|
|
*/
|
|
|
|
ctx->ntmprs = STIX_OOP_FROM_SMINT(ntmprs);
|
|
ctx->method_or_nargs = (stix_oop_t)mth;
|
|
ctx->home = stix->_nil;
|
|
ctx->origin = ctx;
|
|
|
|
/* let SWITCH_ACTIVE_CONTEXT() fill 'ctx->ip' and 'ctx->sp' by putting
|
|
* the values to stix->ip and stix->sp */
|
|
stix->ip = 0;
|
|
stix->sp = ntmprs - 1;
|
|
SWITCH_ACTIVE_CONTEXT (stix, ctx);
|
|
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
static stix_oop_method_t find_method (stix_t* stix, stix_oop_t receiver, const stix_ucs_t* message, int super)
|
|
{
|
|
stix_oop_class_t cls;
|
|
stix_oop_association_t ass;
|
|
stix_oop_t c;
|
|
stix_oop_set_t mthdic;
|
|
int dic_no;
|
|
/* TODO: implement method lookup cache */
|
|
|
|
printf ("==== FINDING METHOD FOR %p [", receiver);
|
|
print_ucs (message);
|
|
printf ("] in ");
|
|
|
|
cls = (stix_oop_class_t)STIX_CLASSOF(stix, receiver);
|
|
if ((stix_oop_t)cls == stix->_class)
|
|
{
|
|
/* receiver is a class object */
|
|
c = receiver;
|
|
dic_no = STIX_CLASS_MTHDIC_CLASS;
|
|
printf ("class method dictioanry of ");
|
|
print_object(stix, (stix_oop_t)((stix_oop_class_t)c)->name);
|
|
printf ("\n");
|
|
}
|
|
else
|
|
{
|
|
c = (stix_oop_t)cls;
|
|
dic_no = STIX_CLASS_MTHDIC_INSTANCE;
|
|
printf ("instance method dictioanry of ");
|
|
print_object(stix, (stix_oop_t)((stix_oop_class_t)c)->name);
|
|
printf ("\n");
|
|
}
|
|
|
|
|
|
if (c != stix->_nil)
|
|
{
|
|
if (super)
|
|
{
|
|
c = ((stix_oop_class_t)c)->superclass;
|
|
if (c == stix->_nil) goto not_found;
|
|
}
|
|
|
|
do
|
|
{
|
|
mthdic = ((stix_oop_class_t)c)->mthdic[dic_no];
|
|
STIX_ASSERT ((stix_oop_t)mthdic != stix->_nil);
|
|
STIX_ASSERT (STIX_CLASSOF(stix, mthdic) == stix->_method_dictionary);
|
|
|
|
/*dump_dictionary (stix, mthdic, "Method dictionary");*/
|
|
ass = (stix_oop_association_t)stix_lookupdic (stix, mthdic, message);
|
|
if (ass)
|
|
{
|
|
STIX_ASSERT (STIX_CLASSOF(stix, ass->value) == stix->_method);
|
|
return (stix_oop_method_t)ass->value;
|
|
}
|
|
c = ((stix_oop_class_t)c)->superclass;
|
|
}
|
|
while (c != stix->_nil);
|
|
}
|
|
|
|
not_found:
|
|
stix->errnum = STIX_ENOENT;
|
|
return STIX_NULL;
|
|
}
|
|
|
|
static int activate_initial_context (stix_t* stix, const stix_ucs_t* objname, const stix_ucs_t* mthname)
|
|
{
|
|
/* the initial context is a fake context. if objname is 'Stix' and
|
|
* mthname is 'main', this function emulates message sending 'Stix main'.
|
|
* it should emulate the following logical byte-code sequences:
|
|
*
|
|
* push Stix
|
|
* send #main
|
|
*/
|
|
|
|
stix_oop_context_t ctx;
|
|
stix_oop_association_t ass;
|
|
stix_oop_method_t mth;
|
|
|
|
/* create a fake initial context */
|
|
ctx = (stix_oop_context_t)stix_instantiate (stix, stix->_method_context, STIX_NULL, 1);
|
|
if (!ctx) return -1;
|
|
|
|
ass = stix_lookupsysdic (stix, objname);
|
|
if (!ass) return -1;
|
|
|
|
mth = find_method (stix, ass->value, mthname, 0);
|
|
if (!mth) return -1;
|
|
|
|
if (STIX_OOP_TO_SMINT(mth->tmpr_nargs) > 0)
|
|
{
|
|
/* this method expects more than 0 arguments.
|
|
* i can't use it as a start-up method.
|
|
TODO: overcome this problem
|
|
*/
|
|
stix->errnum = STIX_EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
/* TODO: handle preamble */
|
|
|
|
/* the initial context starts the life of the entire VM
|
|
* and is not really worked on except that it is used to call the
|
|
* initial method. so it doesn't really require any extra stack space.
|
|
* TODO: verify my theory above is true */
|
|
stix->ip = 0;
|
|
stix->sp = -1;
|
|
|
|
ctx->origin = ctx;
|
|
ctx->method_or_nargs = (stix_oop_t)mth; /* fake. help SWITCH_ACTIVE_CONTEXT() not fail*/
|
|
/* receiver, sender of ctx are nils */
|
|
|
|
STIX_ASSERT (stix->active_context == STIX_NULL);
|
|
/* i can't use SWITCH_ACTIVE_CONTEXT() macro as there is no active context before switching */
|
|
stix->active_context = ctx;
|
|
ACTIVE_STACK_PUSH (stix, ass->value); /* push the receiver */
|
|
|
|
STORE_ACTIVE_IP (stix);
|
|
STORE_ACTIVE_SP (stix);
|
|
|
|
return activate_new_method (stix, mth);
|
|
}
|
|
|
|
static int primitive_dump (stix_t* stix, stix_ooi_t nargs)
|
|
{
|
|
stix_ooi_t i;
|
|
|
|
STIX_ASSERT (nargs >= 0);
|
|
|
|
printf ("RECEIVER: ");
|
|
print_object (stix, ACTIVE_STACK_GET(stix, stix->sp - nargs));
|
|
printf ("\n");
|
|
for (i = nargs; i > 0; )
|
|
{
|
|
--i;
|
|
printf ("ARGUMENT: ");
|
|
print_object (stix, ACTIVE_STACK_GET(stix, stix->sp - i));
|
|
printf ("\n");
|
|
}
|
|
|
|
ACTIVE_STACK_POPS (stix, nargs);
|
|
return 1; /* success */
|
|
}
|
|
|
|
static int primitive_new (stix_t* stix, stix_ooi_t nargs)
|
|
{
|
|
stix_oop_t rcv, obj;
|
|
|
|
STIX_ASSERT (nargs == 0);
|
|
|
|
rcv = ACTIVE_STACK_GETTOP (stix);
|
|
|
|
if (STIX_CLASSOF(stix, rcv) != stix->_class)
|
|
{
|
|
/* the receiver is not a class object */
|
|
return 0;
|
|
}
|
|
|
|
obj = stix_instantiate (stix, rcv, STIX_NULL, 0);
|
|
if (!obj) return -1;
|
|
|
|
/* emulate 'pop receiver' and 'push result' */
|
|
ACTIVE_STACK_SETTOP (stix, obj);
|
|
return 1; /* success */
|
|
}
|
|
|
|
static int primitive_new_with_size (stix_t* stix, stix_ooi_t nargs)
|
|
{
|
|
stix_oop_t rcv, szoop, obj;
|
|
stix_oow_t size;
|
|
|
|
STIX_ASSERT (nargs == 1);
|
|
|
|
rcv = ACTIVE_STACK_GET(stix, stix->sp - 1);
|
|
if (STIX_CLASSOF(stix, rcv) != stix->_class)
|
|
{
|
|
/* the receiver is not a class object */
|
|
return 0;
|
|
}
|
|
|
|
szoop = ACTIVE_STACK_GET(stix, stix->sp);
|
|
if (STIX_OOP_IS_SMINT(szoop))
|
|
{
|
|
size = STIX_OOP_TO_SMINT(szoop);
|
|
}
|
|
/* TODO: support LargeInteger */
|
|
else
|
|
{
|
|
/* size is not a proper numeric object */
|
|
return 0;
|
|
}
|
|
|
|
/* stix_instantiate() ignores size if the instance specification
|
|
* disallows indexed(variable) parts. */
|
|
/* TODO: should i check the specification before calling
|
|
* stix_instantiate()? */
|
|
obj = stix_instantiate (stix, rcv, STIX_NULL, size);
|
|
if (!obj)
|
|
{
|
|
return -1; /* hard failure */
|
|
}
|
|
|
|
/* remove the argument and replace the receiver with a new object
|
|
* instantiated */
|
|
ACTIVE_STACK_POP (stix);
|
|
ACTIVE_STACK_SETTOP (stix, obj);
|
|
|
|
return 1; /* success */
|
|
}
|
|
|
|
static int primitive_basic_size (stix_t* stix, stix_ooi_t nargs)
|
|
{
|
|
stix_oop_t rcv;
|
|
STIX_ASSERT (nargs == 0);
|
|
|
|
|
|
rcv = ACTIVE_STACK_GETTOP(stix);
|
|
ACTIVE_STACK_SETTOP(stix, STIX_OOP_FROM_SMINT(STIX_OBJ_GET_SIZE(rcv)));
|
|
/* TODO: use LargeInteger if the size is very big */
|
|
return 1;
|
|
}
|
|
|
|
static int primitive_basic_at (stix_t* stix, stix_ooi_t nargs)
|
|
{
|
|
stix_oop_t rcv, pos, v;
|
|
stix_ooi_t idx;
|
|
|
|
STIX_ASSERT (nargs == 1);
|
|
|
|
rcv = ACTIVE_STACK_GET(stix, stix->sp - 1);
|
|
if (!STIX_OOP_IS_POINTER(rcv))
|
|
{
|
|
/* the receiver is a special numeric object, not a normal pointer */
|
|
return 0;
|
|
}
|
|
|
|
pos = ACTIVE_STACK_GET(stix, stix->sp);
|
|
if (!STIX_OOP_IS_SMINT(pos))
|
|
{
|
|
/* TODO: handle LargeInteger */
|
|
/* the position must be an integer */
|
|
return 0;
|
|
}
|
|
|
|
idx = STIX_OOP_TO_SMINT(pos);
|
|
if (idx < 1 || idx > STIX_OBJ_GET_SIZE(rcv))
|
|
{
|
|
/* index out of range */
|
|
return 0;
|
|
}
|
|
|
|
/* [NOTE] basicAt: and basicAt:put: used a 1-based index. */
|
|
idx = idx - 1;
|
|
|
|
switch (STIX_OBJ_GET_FLAGS_TYPE(rcv))
|
|
{
|
|
case STIX_OBJ_TYPE_BYTE:
|
|
v = STIX_OOP_FROM_SMINT(((stix_oop_byte_t)rcv)->slot[idx]);
|
|
break;
|
|
|
|
case STIX_OBJ_TYPE_CHAR:
|
|
v = STIX_OOP_FROM_CHAR(((stix_oop_char_t)rcv)->slot[idx]);
|
|
break;
|
|
|
|
case STIX_OBJ_TYPE_WORD:
|
|
/* TODO: largeINteger if the word is too large */
|
|
v = STIX_OOP_FROM_SMINT(((stix_oop_word_t)rcv)->slot[idx]);
|
|
break;
|
|
|
|
case STIX_OBJ_TYPE_OOP:
|
|
v = ((stix_oop_oop_t)rcv)->slot[idx];
|
|
break;
|
|
|
|
default:
|
|
stix->errnum = STIX_EINTERN;
|
|
return -1;
|
|
}
|
|
|
|
ACTIVE_STACK_POP (stix);
|
|
ACTIVE_STACK_SETTOP (stix, v);
|
|
return 1;
|
|
}
|
|
|
|
static int primitive_basic_at_put (stix_t* stix, stix_ooi_t nargs)
|
|
{
|
|
stix_oop_t rcv, pos, val;
|
|
stix_ooi_t idx;
|
|
|
|
STIX_ASSERT (nargs == 2);
|
|
|
|
/* TODO: disallow change of some key kernel objects */
|
|
|
|
rcv = ACTIVE_STACK_GET(stix, stix->sp - 2);
|
|
if (!STIX_OOP_IS_POINTER(rcv))
|
|
{
|
|
/* the receiver is a special numeric object, not a normal pointer */
|
|
return 0;
|
|
}
|
|
|
|
pos = ACTIVE_STACK_GET(stix, stix->sp - 1);
|
|
if (!STIX_OOP_IS_SMINT(pos))
|
|
{
|
|
/* TODO: handle LargeInteger */
|
|
/* the position must be an integer */
|
|
return 0;
|
|
}
|
|
|
|
val = ACTIVE_STACK_GET(stix, stix->sp);
|
|
|
|
idx = STIX_OOP_TO_SMINT(pos);
|
|
if (idx < 1 || idx > STIX_OBJ_GET_SIZE(rcv))
|
|
{
|
|
/* index out of range */
|
|
return 0;
|
|
}
|
|
|
|
/* [NOTE] basicAt: and basicAt:put: used a 1-based index. */
|
|
idx = idx - 1;
|
|
|
|
switch (STIX_OBJ_GET_FLAGS_TYPE(rcv))
|
|
{
|
|
case STIX_OBJ_TYPE_BYTE:
|
|
if (!STIX_OOP_IS_SMINT(val))
|
|
{
|
|
/* the value is not a character */
|
|
return 0;
|
|
}
|
|
/* TOOD: must I check the range of the value? */
|
|
((stix_oop_char_t)rcv)->slot[idx] = STIX_OOP_TO_SMINT(val);
|
|
break;
|
|
|
|
|
|
case STIX_OBJ_TYPE_CHAR:
|
|
if (!STIX_OOP_IS_CHAR(val))
|
|
{
|
|
/* the value is not a character */
|
|
return 0;
|
|
}
|
|
((stix_oop_char_t)rcv)->slot[idx] = STIX_OOP_TO_CHAR(val);
|
|
break;
|
|
|
|
case STIX_OBJ_TYPE_WORD:
|
|
/* TODO: handle largeINteger */
|
|
if (!STIX_OOP_IS_SMINT(val))
|
|
{
|
|
/* the value is not a character */
|
|
return 0;
|
|
}
|
|
((stix_oop_char_t)rcv)->slot[idx] = STIX_OOP_TO_SMINT(val);
|
|
break;
|
|
|
|
case STIX_OBJ_TYPE_OOP:
|
|
((stix_oop_oop_t)rcv)->slot[idx] = val;
|
|
break;
|
|
|
|
default:
|
|
stix->errnum = STIX_EINTERN;
|
|
return -1;
|
|
}
|
|
|
|
ACTIVE_STACK_POPS (stix, 2);
|
|
/* TODO: return receiver or value? */
|
|
ACTIVE_STACK_SETTOP (stix, val);
|
|
return 1;
|
|
}
|
|
|
|
static int primitive_block_context_value (stix_t* stix, stix_ooi_t nargs)
|
|
{
|
|
stix_oop_context_t blkctx, org_blkctx;
|
|
stix_ooi_t local_ntmprs, i;
|
|
|
|
/* TODO: find a better way to support a reentrant block context. */
|
|
|
|
/* | sum |
|
|
* sum := [ :n | (n < 2) ifTrue: [1] ifFalse: [ n + (sum value: (n - 1))] ].
|
|
* (sum value: 10).
|
|
*
|
|
* For the code above, sum is a block context and it is sent value: inside
|
|
* itself. Let me simply clone a block context to allow reentrancy like this
|
|
* while the block context is active
|
|
*/
|
|
org_blkctx = (stix_oop_context_t)ACTIVE_STACK_GET(stix, stix->sp - nargs);
|
|
if (STIX_CLASSOF(stix, org_blkctx) != stix->_block_context)
|
|
{
|
|
printf ("PRIMITVE VALUE RECEIVER IS NOT A BLOCK CONTEXT\n");
|
|
return 0;
|
|
}
|
|
|
|
if (org_blkctx->receiver_or_source != stix->_nil)
|
|
{
|
|
/* the 'source' field is not nil.
|
|
* this block context has already been activated once.
|
|
* you can't send 'value' again to reactivate it.
|
|
* For example, [thisContext value] value.
|
|
*/
|
|
STIX_ASSERT (STIX_OBJ_GET_SIZE(org_blkctx) > STIX_CONTEXT_NAMED_INSTVARS);
|
|
printf ("PRIM REVALUING AN BLOCKCONTEXT\n");
|
|
return 0;
|
|
}
|
|
STIX_ASSERT (STIX_OBJ_GET_SIZE(org_blkctx) == STIX_CONTEXT_NAMED_INSTVARS);
|
|
|
|
if (STIX_OOP_TO_SMINT(org_blkctx->method_or_nargs) != nargs)
|
|
{
|
|
/* the number of argument doesn't match */
|
|
/* TODO: better handling of primitive failure */
|
|
printf ("PRIM BlockContext value FAIL - NARGS MISMATCH\n");
|
|
return 0;
|
|
}
|
|
|
|
/* create a new block context to clone org_blkctx */
|
|
blkctx = (stix_oop_context_t) stix_instantiate (stix, stix->_block_context, STIX_NULL, CONTEXT_STACK_SIZE);
|
|
if (!blkctx) return -1;
|
|
|
|
/* getting org_blkctx again to be GC-safe for stix_instantiate() above */
|
|
org_blkctx = (stix_oop_context_t)ACTIVE_STACK_GET(stix, stix->sp - nargs);
|
|
STIX_ASSERT (STIX_CLASSOF(stix, org_blkctx) == stix->_block_context);
|
|
|
|
#if 0
|
|
/* shallow-copy the named part including home, origin, etc. */
|
|
for (i = 0; i < STIX_CONTEXT_NAMED_INSTVARS; i++)
|
|
{
|
|
((stix_oop_oop_t)blkctx)->slot[i] = ((stix_oop_oop_t)org_blkctx)->slot[i];
|
|
}
|
|
#else
|
|
blkctx->ip = org_blkctx->ip;
|
|
blkctx->ntmprs = org_blkctx->ntmprs;
|
|
blkctx->method_or_nargs = org_blkctx->method_or_nargs;
|
|
blkctx->receiver_or_source = (stix_oop_t)org_blkctx;
|
|
blkctx->home = org_blkctx->home;
|
|
blkctx->origin = org_blkctx->origin;
|
|
printf ("~~~~~~~~~~ BLOCK VALUING %p TO NEW BLOCK %p\n", org_blkctx, blkctx);
|
|
#endif
|
|
|
|
/* TODO: check the stack size of a block context to see if it's large enough to hold arguments */
|
|
/* copy the arguments to the stack */
|
|
for (i = 0; i < nargs; i++)
|
|
{
|
|
blkctx->slot[i] = ACTIVE_STACK_GET(stix, stix->sp - nargs + i + 1);
|
|
}
|
|
ACTIVE_STACK_POPS (stix, nargs + 1); /* pop arguments and receiver */
|
|
|
|
STIX_ASSERT (blkctx->home != stix->_nil);
|
|
|
|
/* the number of temporaries stored in the block context
|
|
* accumulates the number of temporaries starting from the origin.
|
|
* simple calculation is needed to find the number of local temporaries */
|
|
local_ntmprs = STIX_OOP_TO_SMINT(blkctx->ntmprs) -
|
|
STIX_OOP_TO_SMINT(((stix_oop_context_t)blkctx->home)->ntmprs);
|
|
STIX_ASSERT (local_ntmprs >= nargs);
|
|
|
|
blkctx->sp = STIX_OOP_FROM_SMINT(local_ntmprs);
|
|
blkctx->sender = (stix_oop_t)stix->active_context;
|
|
|
|
printf ("<<ENTERING BLOCK>>\n");
|
|
SWITCH_ACTIVE_CONTEXT (stix, (stix_oop_context_t)blkctx);
|
|
return 1;
|
|
}
|
|
|
|
static int primitive_integer_add (stix_t* stix, stix_ooi_t nargs)
|
|
{
|
|
stix_ooi_t tmp;
|
|
stix_oop_t rcv, arg;
|
|
|
|
STIX_ASSERT (nargs == 1);
|
|
|
|
rcv = ACTIVE_STACK_GET(stix, stix->sp - 1);
|
|
arg = ACTIVE_STACK_GET(stix, stix->sp);
|
|
|
|
if (STIX_OOP_IS_SMINT(rcv) && STIX_OOP_IS_SMINT(arg))
|
|
{
|
|
tmp = STIX_OOP_TO_SMINT(rcv) + STIX_OOP_TO_SMINT(arg);
|
|
/* TODO: check overflow. if so convert it to LargeInteger */
|
|
|
|
ACTIVE_STACK_POP (stix);
|
|
ACTIVE_STACK_SETTOP (stix, STIX_OOP_FROM_SMINT(tmp));
|
|
return 1;
|
|
}
|
|
|
|
/* TODO: handle LargeInteger */
|
|
return 0;
|
|
}
|
|
|
|
static int primitive_integer_sub (stix_t* stix, stix_ooi_t nargs)
|
|
{
|
|
stix_ooi_t tmp;
|
|
stix_oop_t rcv, arg;
|
|
|
|
STIX_ASSERT (nargs == 1);
|
|
|
|
rcv = ACTIVE_STACK_GET(stix, stix->sp - 1);
|
|
arg = ACTIVE_STACK_GET(stix, stix->sp);
|
|
|
|
if (STIX_OOP_IS_SMINT(rcv) && STIX_OOP_IS_SMINT(arg))
|
|
{
|
|
tmp = STIX_OOP_TO_SMINT(rcv) - STIX_OOP_TO_SMINT(arg);
|
|
/* TODO: check overflow. if so convert it to LargeInteger */
|
|
|
|
ACTIVE_STACK_POP (stix);
|
|
ACTIVE_STACK_SETTOP (stix, STIX_OOP_FROM_SMINT(tmp));
|
|
return 1;
|
|
}
|
|
|
|
/* TODO: handle LargeInteger */
|
|
return 0;
|
|
}
|
|
|
|
static int primitive_integer_mul (stix_t* stix, stix_ooi_t nargs)
|
|
{
|
|
stix_ooi_t tmp;
|
|
stix_oop_t rcv, arg;
|
|
|
|
STIX_ASSERT (nargs == 1);
|
|
|
|
rcv = ACTIVE_STACK_GET(stix, stix->sp - 1);
|
|
arg = ACTIVE_STACK_GET(stix, stix->sp);
|
|
|
|
if (STIX_OOP_IS_SMINT(rcv) && STIX_OOP_IS_SMINT(arg))
|
|
{
|
|
tmp = STIX_OOP_TO_SMINT(rcv) * STIX_OOP_TO_SMINT(arg);
|
|
/* TODO: check overflow. if so convert it to LargeInteger */
|
|
|
|
ACTIVE_STACK_POP (stix);
|
|
ACTIVE_STACK_SETTOP (stix, STIX_OOP_FROM_SMINT(tmp));
|
|
return 1;
|
|
}
|
|
|
|
/* TODO: handle LargeInteger */
|
|
return 0;
|
|
}
|
|
|
|
static int primitive_integer_eq (stix_t* stix, stix_ooi_t nargs)
|
|
{
|
|
stix_oop_t rcv, arg;
|
|
|
|
STIX_ASSERT (nargs == 1);
|
|
|
|
rcv = ACTIVE_STACK_GET(stix, stix->sp - 1);
|
|
arg = ACTIVE_STACK_GET(stix, stix->sp);
|
|
|
|
if (STIX_OOP_IS_SMINT(rcv) && STIX_OOP_IS_SMINT(arg))
|
|
{
|
|
ACTIVE_STACK_POP (stix);
|
|
if (STIX_OOP_TO_SMINT(rcv) < STIX_OOP_TO_SMINT(arg))
|
|
{
|
|
ACTIVE_STACK_SETTOP (stix, stix->_true);
|
|
}
|
|
else
|
|
{
|
|
ACTIVE_STACK_SETTOP (stix, stix->_false);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* TODO: handle LargeInteger */
|
|
return 0;
|
|
}
|
|
|
|
static int primitive_integer_lt (stix_t* stix, stix_ooi_t nargs)
|
|
{
|
|
stix_oop_t rcv, arg;
|
|
|
|
STIX_ASSERT (nargs == 1);
|
|
|
|
rcv = ACTIVE_STACK_GET(stix, stix->sp - 1);
|
|
arg = ACTIVE_STACK_GET(stix, stix->sp);
|
|
|
|
if (STIX_OOP_IS_SMINT(rcv) && STIX_OOP_IS_SMINT(arg))
|
|
{
|
|
ACTIVE_STACK_POP (stix);
|
|
if (STIX_OOP_TO_SMINT(rcv) < STIX_OOP_TO_SMINT(arg))
|
|
{
|
|
ACTIVE_STACK_SETTOP (stix, stix->_true);
|
|
}
|
|
else
|
|
{
|
|
ACTIVE_STACK_SETTOP (stix, stix->_false);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* TODO: handle LargeInteger */
|
|
return 0;
|
|
}
|
|
|
|
static int primitive_integer_gt (stix_t* stix, stix_ooi_t nargs)
|
|
{
|
|
stix_oop_t rcv, arg;
|
|
|
|
STIX_ASSERT (nargs == 1);
|
|
|
|
rcv = ACTIVE_STACK_GET(stix, stix->sp - 1);
|
|
arg = ACTIVE_STACK_GET(stix, stix->sp);
|
|
|
|
if (STIX_OOP_IS_SMINT(rcv) && STIX_OOP_IS_SMINT(arg))
|
|
{
|
|
ACTIVE_STACK_POP (stix);
|
|
if (STIX_OOP_TO_SMINT(rcv) > STIX_OOP_TO_SMINT(arg))
|
|
{
|
|
ACTIVE_STACK_SETTOP (stix, stix->_true);
|
|
}
|
|
else
|
|
{
|
|
ACTIVE_STACK_SETTOP (stix, stix->_false);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* TODO: handle LargeInteger */
|
|
return 0;
|
|
}
|
|
|
|
|
|
typedef int (*primitive_handler_t) (stix_t* stix, stix_ooi_t nargs);
|
|
|
|
struct primitive_t
|
|
{
|
|
stix_ooi_t nargs; /* expected number of arguments */
|
|
primitive_handler_t handler;
|
|
};
|
|
typedef struct primitive_t primitive_t;
|
|
|
|
static primitive_t primitives[] =
|
|
{
|
|
{ -1, primitive_dump }, /* 0 */
|
|
{ 0, primitive_new }, /* 1 */
|
|
{ 1, primitive_new_with_size }, /* 2 */
|
|
{ 0, primitive_basic_size }, /* 3 */
|
|
{ 1, primitive_basic_at }, /* 4 */
|
|
{ 2, primitive_basic_at_put }, /* 5 */
|
|
{ -1, primitive_block_context_value }, /* 6 */
|
|
{ 1, primitive_integer_add }, /* 7 */
|
|
{ 1, primitive_integer_sub }, /* 8 */
|
|
{ 1, primitive_integer_mul }, /* 9 */
|
|
{ 1, primitive_integer_eq }, /* 10 */
|
|
{ 1, primitive_integer_lt }, /* 11 */
|
|
{ 1, primitive_integer_gt } /* 12 */
|
|
};
|
|
|
|
int stix_execute (stix_t* stix)
|
|
{
|
|
stix_byte_t bcode;
|
|
stix_ooi_t b1, b2;
|
|
stix_oop_t return_value;
|
|
|
|
stix_size_t inst_counter;
|
|
|
|
STIX_ASSERT (stix->active_context != STIX_NULL);
|
|
|
|
inst_counter = 0;
|
|
|
|
while (1)
|
|
{
|
|
|
|
#if 0
|
|
printf ("IP => %d ", (int)stix->ip);
|
|
#endif
|
|
FETCH_BYTE_CODE_TO (stix, bcode);
|
|
/*while (bcode == BCODE_NOOP) FETCH_BYTE_CODE_TO (stix, bcode);*/
|
|
|
|
#if 0
|
|
printf ("BCODE = %x\n", bcode);
|
|
#endif
|
|
inst_counter++;
|
|
|
|
switch (bcode)
|
|
{
|
|
/* ------------------------------------------------- */
|
|
|
|
case BCODE_PUSH_INSTVAR_X:
|
|
FETCH_PARAM_CODE_TO (stix, 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:
|
|
printf ("PUSH_INSTVAR %d\n", (int)b1);
|
|
STIX_ASSERT (STIX_OBJ_GET_FLAGS_TYPE(stix->active_context->origin->receiver_or_source) == STIX_OBJ_TYPE_OOP);
|
|
ACTIVE_STACK_PUSH (stix, ((stix_oop_oop_t)stix->active_context->origin->receiver_or_source)->slot[b1]);
|
|
break;
|
|
|
|
/* ------------------------------------------------- */
|
|
|
|
case BCODE_STORE_INTO_INSTVAR_X:
|
|
FETCH_PARAM_CODE_TO (stix, 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:
|
|
printf ("STORE_INTO_INSTVAR %d\n", (int)b1);
|
|
STIX_ASSERT (STIX_OBJ_GET_FLAGS_TYPE(stix->active_context->receiver_or_source) == STIX_OBJ_TYPE_OOP);
|
|
((stix_oop_oop_t)stix->active_context->origin->receiver_or_source)->slot[b1] = ACTIVE_STACK_GETTOP(stix);
|
|
break;
|
|
|
|
/* ------------------------------------------------- */
|
|
case BCODE_POP_INTO_INSTVAR_X:
|
|
FETCH_PARAM_CODE_TO (stix, 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:
|
|
printf ("POP_INTO_INSTVAR %d\n", (int)b1);
|
|
STIX_ASSERT (STIX_OBJ_GET_FLAGS_TYPE(stix->active_context->receiver_or_source) == STIX_OBJ_TYPE_OOP);
|
|
((stix_oop_oop_t)stix->active_context->origin->receiver_or_source)->slot[b1] = ACTIVE_STACK_GETTOP(stix);
|
|
ACTIVE_STACK_POP (stix);
|
|
break;
|
|
|
|
/* ------------------------------------------------- */
|
|
case BCODE_PUSH_TEMPVAR_X:
|
|
case BCODE_STORE_INTO_TEMPVAR_X:
|
|
case BCODE_POP_INTO_TEMPVAR_X:
|
|
FETCH_PARAM_CODE_TO (stix, 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:
|
|
{
|
|
stix_oop_context_t ctx;
|
|
stix_ooi_t bx;
|
|
|
|
b1 = bcode & 0x7; /* low 3 bits */
|
|
handle_tempvar:
|
|
|
|
#if defined(STIX_USE_CTXTEMPVAR)
|
|
ctx = stix->active_context->origin;
|
|
bx = b1;
|
|
#else
|
|
if (stix->active_context->home != stix->_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' */
|
|
stix_oop_t home;
|
|
stix_ooi_t home_ntmprs;
|
|
|
|
ctx = stix->active_context;
|
|
home = ctx->home;
|
|
|
|
do
|
|
{
|
|
home_ntmprs = STIX_OOP_TO_SMINT(((stix_oop_context_t)home)->ntmprs);
|
|
if (b1 >= home_ntmprs) break;
|
|
|
|
ctx = (stix_oop_context_t)home;
|
|
home = ((stix_oop_context_t)home)->home;
|
|
if (home == stix->_nil)
|
|
{
|
|
home_ntmprs = 0;
|
|
break;
|
|
}
|
|
}
|
|
while (1);
|
|
|
|
bx = b1 - home_ntmprs;
|
|
}
|
|
else
|
|
{
|
|
ctx = stix->active_context;
|
|
bx = b1;
|
|
}
|
|
#endif
|
|
|
|
if ((bcode >> 4) & 1)
|
|
{
|
|
/* push - bit 4 on*/
|
|
printf ("PUSH_TEMPVAR %d - ", (int)b1);
|
|
|
|
ACTIVE_STACK_PUSH (stix, ctx->slot[bx]);
|
|
}
|
|
else
|
|
{
|
|
/* store or pop - bit 5 off */
|
|
ctx->slot[bx] = ACTIVE_STACK_GETTOP(stix);
|
|
|
|
if ((bcode >> 3) & 1)
|
|
{
|
|
/* pop - bit 3 on */
|
|
ACTIVE_STACK_POP (stix);
|
|
printf ("POP_INTO_TEMPVAR %d - ", (int)b1);
|
|
|
|
}
|
|
else
|
|
{
|
|
printf ("STORE_INTO_TEMPVAR %d - ", (int)b1);
|
|
}
|
|
}
|
|
|
|
print_object (stix, ctx->slot[bx]);
|
|
printf ("\n");
|
|
break;
|
|
}
|
|
|
|
/* ------------------------------------------------- */
|
|
case BCODE_PUSH_LITERAL_X:
|
|
FETCH_PARAM_CODE_TO (stix, 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:
|
|
printf ("PUSH_LITERAL idx=%d - ", (int)b1);
|
|
print_object (stix, stix->active_method->slot[b1]);
|
|
printf ("\n");
|
|
ACTIVE_STACK_PUSH (stix, stix->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 (stix, 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:
|
|
{
|
|
stix_oop_association_t ass;
|
|
|
|
b1 = bcode & 0x3; /* low 2 bits */
|
|
handle_object:
|
|
ass = (stix_oop_association_t)stix->active_method->slot[b1];
|
|
STIX_ASSERT (STIX_CLASSOF(stix, ass) == stix->_association);
|
|
|
|
if ((bcode >> 3) & 1)
|
|
{
|
|
/* store or pop */
|
|
ass->value = ACTIVE_STACK_GETTOP(stix);
|
|
|
|
if ((bcode >> 2) & 1)
|
|
{
|
|
/* pop */
|
|
ACTIVE_STACK_POP (stix);
|
|
printf ("POP_INTO_OBJECT %d - ", (int)b1);
|
|
}
|
|
else
|
|
{
|
|
printf ("STORE_INTO_OBJECT %d - ", (int)b1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* push */
|
|
ACTIVE_STACK_PUSH (stix, ass->value);
|
|
printf ("PUSH_OBJECT %d - ", (int)b1);
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* -------------------------------------------------------- */
|
|
|
|
case BCODE_JUMP_FORWARD_X:
|
|
FETCH_PARAM_CODE_TO (stix, b1);
|
|
printf ("JUMP_FORWARD %d\n", (int)b1);
|
|
stix->ip += b1;
|
|
break;
|
|
|
|
case BCODE_JUMP_FORWARD_0:
|
|
case BCODE_JUMP_FORWARD_1:
|
|
case BCODE_JUMP_FORWARD_2:
|
|
case BCODE_JUMP_FORWARD_3:
|
|
printf ("JUMP_FORWARD %d\n", (int)(bcode & 0x3));
|
|
stix->ip += (bcode & 0x3); /* low 2 bits */
|
|
break;
|
|
|
|
case BCODE_JUMP_BACKWARD_X:
|
|
FETCH_PARAM_CODE_TO (stix, b1);
|
|
printf ("JUMP_BACKWARD %d\n", (int)b1);
|
|
stix->ip += b1;
|
|
break;
|
|
|
|
case BCODE_JUMP_BACKWARD_0:
|
|
case BCODE_JUMP_BACKWARD_1:
|
|
case BCODE_JUMP_BACKWARD_2:
|
|
case BCODE_JUMP_BACKWARD_3:
|
|
printf ("JUMP_BACKWARD %d\n", (int)(bcode & 0x3));
|
|
stix->ip -= (bcode & 0x3); /* low 2 bits */
|
|
break;
|
|
|
|
case BCODE_JUMP_IF_TRUE_X:
|
|
case BCODE_JUMP_IF_FALSE_X:
|
|
case BCODE_JUMP_IF_TRUE_0:
|
|
case BCODE_JUMP_IF_TRUE_1:
|
|
case BCODE_JUMP_IF_TRUE_2:
|
|
case BCODE_JUMP_IF_TRUE_3:
|
|
case BCODE_JUMP_IF_FALSE_0:
|
|
case BCODE_JUMP_IF_FALSE_1:
|
|
case BCODE_JUMP_IF_FALSE_2:
|
|
case BCODE_JUMP_IF_FALSE_3:
|
|
printf ("<<<<<<<<<<<<<< JUMP NOT IMPLEMENTED YET >>>>>>>>>>>> \n");
|
|
stix->errnum = STIX_ENOIMPL;
|
|
return -1;
|
|
|
|
case BCODE_JUMP2_FORWARD:
|
|
FETCH_PARAM_CODE_TO (stix, b1);
|
|
printf ("JUMP2_FORWARD %d\n", (int)b1);
|
|
stix->ip += MAX_CODE_JUMP + b1;
|
|
break;
|
|
break;
|
|
|
|
case BCODE_JUMP2_BACKWARD:
|
|
FETCH_PARAM_CODE_TO (stix, b1);
|
|
printf ("JUMP2_BACKWARD %d\n", (int)b1);
|
|
stix->ip -= MAX_CODE_JUMP + b1;
|
|
break;
|
|
|
|
/* -------------------------------------------------------- */
|
|
|
|
case BCODE_PUSH_CTXTEMPVAR_X:
|
|
case BCODE_STORE_INTO_CTXTEMPVAR_X:
|
|
case BCODE_POP_INTO_CTXTEMPVAR_X:
|
|
FETCH_PARAM_CODE_TO (stix, b1);
|
|
FETCH_PARAM_CODE_TO (stix, 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:
|
|
{
|
|
stix_ooi_t i;
|
|
stix_oop_context_t ctx;
|
|
|
|
b1 = bcode & 0x3; /* low 2 bits */
|
|
FETCH_BYTE_CODE_TO (stix, b2);
|
|
|
|
handle_ctxtempvar:
|
|
|
|
ctx = stix->active_context;
|
|
STIX_ASSERT ((stix_oop_t)ctx != stix->_nil);
|
|
for (i = 0; i < b1; i++)
|
|
{
|
|
ctx = (stix_oop_context_t)ctx->home;
|
|
}
|
|
|
|
if ((bcode >> 3) & 1)
|
|
{
|
|
/* store or pop */
|
|
ctx->slot[b2] = ACTIVE_STACK_GETTOP(stix);
|
|
|
|
if ((bcode >> 2) & 1)
|
|
{
|
|
/* pop */
|
|
ACTIVE_STACK_POP (stix);
|
|
printf ("POP_INTO_CTXTEMPVAR %d %d - ", (int)b1, (int)b2);
|
|
}
|
|
else
|
|
{
|
|
printf ("STORE_INTO_CTXTEMPVAR %d %d - ", (int)b1, (int)b2);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* push */
|
|
ACTIVE_STACK_PUSH (stix, ctx->slot[b2]);
|
|
printf ("PUSH_CTXTEMPVAR %d %d - ", (int)b1, (int)b2);
|
|
}
|
|
|
|
print_object (stix, ctx->slot[b2]);
|
|
printf ("\n");
|
|
break;
|
|
}
|
|
/* -------------------------------------------------------- */
|
|
|
|
case BCODE_PUSH_OBJVAR_X:
|
|
case BCODE_STORE_INTO_OBJVAR_X:
|
|
case BCODE_POP_INTO_OBJVAR_X:
|
|
FETCH_PARAM_CODE_TO (stix, b1);
|
|
FETCH_PARAM_CODE_TO (stix, 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:
|
|
{
|
|
stix_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 (stix, b2);
|
|
|
|
handle_objvar:
|
|
t = (stix_oop_oop_t)stix->active_method->slot[b2];
|
|
STIX_ASSERT (STIX_OBJ_GET_FLAGS_TYPE(t) == STIX_OBJ_TYPE_OOP);
|
|
STIX_ASSERT (b1 < STIX_OBJ_GET_SIZE(t));
|
|
|
|
if ((bcode >> 3) & 1)
|
|
{
|
|
/* store or pop */
|
|
|
|
t->slot[b1] = ACTIVE_STACK_GETTOP(stix);
|
|
|
|
if ((bcode >> 2) & 1)
|
|
{
|
|
/* pop */
|
|
ACTIVE_STACK_POP (stix);
|
|
printf ("POP_INTO_OBJVAR %d %d - ", (int)b1, (int)b2);
|
|
}
|
|
else
|
|
{
|
|
printf ("STORE_INTO_OBJVAR %d %d - ", (int)b1, (int)b2);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* push */
|
|
printf ("PUSH_OBJVAR %d %d - ", (int)b1, (int)b2);
|
|
ACTIVE_STACK_PUSH (stix, t->slot[b1]);
|
|
}
|
|
|
|
print_object (stix, t->slot[b1]);
|
|
printf ("\n");
|
|
break;
|
|
}
|
|
|
|
/* -------------------------------------------------------- */
|
|
case BCODE_SEND_MESSAGE_X:
|
|
case BCODE_SEND_MESSAGE_TO_SUPER_X:
|
|
FETCH_PARAM_CODE_TO (stix, b1);
|
|
FETCH_PARAM_CODE_TO (stix, 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:
|
|
{
|
|
/* b1 -> number of arguments
|
|
* b2 -> index to the selector stored in the literal frame
|
|
*/
|
|
stix_ucs_t mthname;
|
|
stix_oop_t newrcv;
|
|
stix_oop_method_t newmth;
|
|
stix_oop_char_t selector;
|
|
stix_ooi_t preamble, preamble_code;
|
|
|
|
|
|
handle_send_message:
|
|
b1 = bcode & 0x3; /* low 2 bits */
|
|
FETCH_BYTE_CODE_TO (stix, b2);
|
|
|
|
/* get the selector from the literal frame */
|
|
selector = (stix_oop_char_t)stix->active_method->slot[b2];
|
|
|
|
|
|
printf ("SEND_MESSAGE%s TO RECEIVER AT STACKPOS=%d NARGS=%d SELECTOR=", (((bcode >> 2) & 1)? "_TO_SUPER": ""), (int)(stix->sp - b1), (int)b1);
|
|
print_object (stix, (stix_oop_t)selector);
|
|
fflush (stdout);
|
|
STIX_ASSERT (STIX_CLASSOF(stix, selector) == stix->_symbol);
|
|
|
|
newrcv = ACTIVE_STACK_GET(stix, stix->sp - b1);
|
|
printf (" RECEIVER = ");
|
|
print_object(stix, newrcv);
|
|
printf ("\n");
|
|
mthname.ptr = selector->slot;
|
|
mthname.len = STIX_OBJ_GET_SIZE(selector);
|
|
newmth = find_method (stix, newrcv, &mthname, ((bcode >> 2) & 1));
|
|
if (!newmth)
|
|
{
|
|
/* TODO: implement doesNotUnderstand: XXXXX instead of returning -1. */
|
|
printf ("no such method .........[");
|
|
print_ucs (&mthname);
|
|
printf ("]\n");
|
|
goto oops;
|
|
}
|
|
|
|
STIX_ASSERT (STIX_OOP_TO_SMINT(newmth->tmpr_nargs) == b1);
|
|
|
|
preamble = STIX_OOP_TO_SMINT(newmth->preamble);
|
|
preamble_code = STIX_METHOD_GET_PREAMBLE_CODE(preamble);
|
|
switch (preamble_code)
|
|
{
|
|
case STIX_METHOD_PREAMBLE_RETURN_RECEIVER:
|
|
printf ("RETURN RECEIVER AT PREAMBLE\n");
|
|
ACTIVE_STACK_POPS (stix, b1); /* pop arguments only*/
|
|
break;
|
|
|
|
case STIX_METHOD_PREAMBLE_RETURN_NIL:
|
|
printf ("RETURN NIL AT PREAMBLE\n");
|
|
ACTIVE_STACK_POPS (stix, b1);
|
|
ACTIVE_STACK_SETTOP (stix, stix->_nil);
|
|
break;
|
|
|
|
case STIX_METHOD_PREAMBLE_RETURN_TRUE:
|
|
printf ("RETURN TRUE AT PREAMBLE\n");
|
|
ACTIVE_STACK_POPS (stix, b1);
|
|
ACTIVE_STACK_SETTOP (stix, stix->_true);
|
|
break;
|
|
|
|
case STIX_METHOD_PREAMBLE_RETURN_FALSE:
|
|
printf ("RETURN FALSE AT PREAMBLE\n");
|
|
ACTIVE_STACK_POPS (stix, b1);
|
|
ACTIVE_STACK_SETTOP (stix, stix->_false);
|
|
break;
|
|
|
|
case STIX_METHOD_PREAMBLE_RETURN_INDEX:
|
|
printf ("RETURN %d AT PREAMBLE\n", (int)STIX_METHOD_GET_PREAMBLE_INDEX(preamble));
|
|
ACTIVE_STACK_POPS (stix, b1);
|
|
ACTIVE_STACK_SETTOP (stix, STIX_OOP_FROM_SMINT(STIX_METHOD_GET_PREAMBLE_INDEX(preamble)));
|
|
break;
|
|
|
|
case STIX_METHOD_PREAMBLE_RETURN_NEGINDEX:
|
|
printf ("RETURN %d AT PREAMBLE\n", (int)-STIX_METHOD_GET_PREAMBLE_INDEX(preamble));
|
|
ACTIVE_STACK_POPS (stix, b1);
|
|
ACTIVE_STACK_SETTOP (stix, STIX_OOP_FROM_SMINT(-STIX_METHOD_GET_PREAMBLE_INDEX(preamble)));
|
|
break;
|
|
|
|
case STIX_METHOD_PREAMBLE_RETURN_INSTVAR:
|
|
{
|
|
stix_oop_oop_t rcv;
|
|
|
|
ACTIVE_STACK_POPS (stix, b1); /* pop arguments only */
|
|
|
|
printf ("RETURN INSTVAR AT PREAMBLE index %d\n", (int)STIX_METHOD_GET_PREAMBLE_INDEX(preamble));
|
|
/* replace the receiver by an instance variable of the receiver */
|
|
rcv = (stix_oop_oop_t)ACTIVE_STACK_GETTOP(stix);
|
|
STIX_ASSERT (STIX_OBJ_GET_FLAGS_TYPE(rcv) == STIX_OBJ_TYPE_OOP);
|
|
STIX_ASSERT (STIX_OBJ_GET_SIZE(rcv) > STIX_METHOD_GET_PREAMBLE_INDEX(preamble));
|
|
|
|
if (rcv == (stix_oop_oop_t)stix->active_context)
|
|
{
|
|
/* the active context object doesn't keep
|
|
* the most up-to-date information in the
|
|
* 'ip' and 'sp' field. commit these fields
|
|
* when the object to be accessed is
|
|
* the active context. this manual commit
|
|
* is required because this premable handling
|
|
* skips activation of a new method context
|
|
* that would commit these fields.
|
|
*/
|
|
STORE_ACTIVE_IP (stix);
|
|
STORE_ACTIVE_SP (stix);
|
|
}
|
|
|
|
/* this accesses the instance variable of the receiver */
|
|
ACTIVE_STACK_SET (stix, stix->sp, rcv->slot[STIX_METHOD_GET_PREAMBLE_INDEX(preamble)]);
|
|
break;
|
|
}
|
|
|
|
case STIX_METHOD_PREAMBLE_PRIMITIVE:
|
|
{
|
|
stix_ooi_t prim_no;
|
|
|
|
prim_no = STIX_METHOD_GET_PREAMBLE_INDEX(preamble);
|
|
if (prim_no >= 0 && prim_no < STIX_COUNTOF(primitives) &&
|
|
(primitives[prim_no].nargs < 0 || primitives[prim_no].nargs == b1))
|
|
{
|
|
int n;
|
|
|
|
stix_pushtmp (stix, (stix_oop_t*)&newmth);
|
|
n = primitives[prim_no].handler (stix, b1);
|
|
stix_poptmp (stix);
|
|
if (n <= -1) goto oops;
|
|
if (n >= 1) break;
|
|
}
|
|
|
|
/* primitive failed. fall through */
|
|
}
|
|
|
|
default:
|
|
if (activate_new_method (stix, newmth) <= -1) goto oops;
|
|
break;
|
|
}
|
|
|
|
break; /* CMD_SEND_MESSAGE */
|
|
}
|
|
|
|
/* -------------------------------------------------------- */
|
|
|
|
case BCODE_PUSH_RECEIVER:
|
|
printf ("PUSH_RECEIVER %p TO STACK INDEX %d\n", stix->active_context->origin->receiver_or_source, (int)stix->sp);
|
|
ACTIVE_STACK_PUSH (stix, stix->active_context->origin->receiver_or_source);
|
|
break;
|
|
|
|
case BCODE_PUSH_NIL:
|
|
printf ("PUSH_NIL\n");
|
|
ACTIVE_STACK_PUSH (stix, stix->_nil);
|
|
break;
|
|
|
|
case BCODE_PUSH_TRUE:
|
|
printf ("PUSH_TRUE\n");
|
|
ACTIVE_STACK_PUSH (stix, stix->_true);
|
|
break;
|
|
|
|
case BCODE_PUSH_FALSE:
|
|
printf ("PUSH_FALSE\n");
|
|
ACTIVE_STACK_PUSH (stix, stix->_false);
|
|
break;
|
|
|
|
case BCODE_PUSH_CONTEXT:
|
|
printf ("PUSH_CONTEXT\n");
|
|
ACTIVE_STACK_PUSH (stix, (stix_oop_t)stix->active_context);
|
|
break;
|
|
|
|
case BCODE_PUSH_NEGONE:
|
|
printf ("PUSH_NEGONE\n");
|
|
ACTIVE_STACK_PUSH (stix, STIX_OOP_FROM_SMINT(-1));
|
|
break;
|
|
|
|
case BCODE_PUSH_ZERO:
|
|
printf ("PUSH_ZERO\n");
|
|
ACTIVE_STACK_PUSH (stix, STIX_OOP_FROM_SMINT(0));
|
|
break;
|
|
|
|
case BCODE_PUSH_ONE:
|
|
printf ("PUSH_ONE\n");
|
|
ACTIVE_STACK_PUSH (stix, STIX_OOP_FROM_SMINT(1));
|
|
break;
|
|
|
|
case BCODE_PUSH_TWO:
|
|
printf ("PUSH_TWO\n");
|
|
ACTIVE_STACK_PUSH (stix, STIX_OOP_FROM_SMINT(2));
|
|
break;
|
|
|
|
case BCODE_PUSH_INTLIT:
|
|
FETCH_PARAM_CODE_TO (stix, b1);
|
|
ACTIVE_STACK_PUSH (stix, STIX_OOP_FROM_SMINT(b1));
|
|
printf ("PUSH_INTLIT %d\n", (int)b1);
|
|
break;
|
|
|
|
case BCODE_PUSH_NEGINTLIT:
|
|
FETCH_PARAM_CODE_TO (stix, b1);
|
|
ACTIVE_STACK_PUSH (stix, STIX_OOP_FROM_SMINT(-b1));
|
|
printf ("PUSH_NEGINTLIT %d\n", (int)-b1);
|
|
break;
|
|
|
|
/* -------------------------------------------------------- */
|
|
|
|
case BCODE_DUP_STACKTOP:
|
|
{
|
|
stix_oop_t t;
|
|
printf ("DUP_STACKTOP SP=%d\n", (int)stix->sp);
|
|
STIX_ASSERT (!ACTIVE_STACK_ISEMPTY(stix));
|
|
t = ACTIVE_STACK_GETTOP(stix);
|
|
ACTIVE_STACK_PUSH (stix, t);
|
|
break;
|
|
}
|
|
|
|
case BCODE_POP_STACKTOP:
|
|
printf ("POP_STACKTOP\n");
|
|
STIX_ASSERT (!ACTIVE_STACK_ISEMPTY(stix));
|
|
ACTIVE_STACK_POP (stix);
|
|
break;
|
|
|
|
case BCODE_RETURN_STACKTOP:
|
|
printf ("RETURN_STACKTOP\n");
|
|
return_value = ACTIVE_STACK_GETTOP(stix);
|
|
ACTIVE_STACK_POP (stix);
|
|
goto handle_return;
|
|
|
|
case BCODE_RETURN_RECEIVER:
|
|
printf ("RETURN_RECEIVER\n");
|
|
return_value = stix->active_context->origin->receiver_or_source;
|
|
handle_return:
|
|
printf ("<<LEAVING>> SP=%d\n", (int)stix->sp);
|
|
|
|
/* 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
|
|
*
|
|
*/
|
|
stix->ip--;
|
|
|
|
SWITCH_ACTIVE_CONTEXT (stix, (stix_oop_context_t)stix->active_context->origin->sender);
|
|
|
|
/* push the return value to the stack of the new active context */
|
|
ACTIVE_STACK_PUSH (stix, return_value);
|
|
|
|
if (stix->active_context->sender == stix->_nil)
|
|
{
|
|
/* the sending context of the intial context has been set to nil.
|
|
* use this fact to tell an initial context from a normal context. */
|
|
STIX_ASSERT (stix->active_context->receiver_or_source == stix->_nil);
|
|
|
|
printf ("<<<RETURNIGN TO THE INITIAL CONTEXT>>>\n");
|
|
STIX_ASSERT (stix->sp == 0);
|
|
goto done;
|
|
}
|
|
|
|
break;
|
|
|
|
case BCODE_RETURN_FROM_BLOCK:
|
|
printf ("LEAVING_BLOCK\n");
|
|
STIX_ASSERT(STIX_CLASSOF(stix, stix->active_context) == stix->_block_context);
|
|
|
|
return_value = ACTIVE_STACK_GETTOP(stix);
|
|
SWITCH_ACTIVE_CONTEXT (stix, (stix_oop_context_t)stix->active_context->sender);
|
|
ACTIVE_STACK_PUSH (stix, return_value);
|
|
break;
|
|
|
|
case BCODE_MAKE_BLOCK:
|
|
{
|
|
stix_oop_context_t blkctx;
|
|
|
|
/* b1 - number of block arguments
|
|
* b2 - number of block temporaries */
|
|
FETCH_PARAM_CODE_TO (stix, b1);
|
|
FETCH_PARAM_CODE_TO (stix, b2);
|
|
|
|
printf ("MAKE_BLOCK %d %d\n", (int)b1, (int)b2);
|
|
|
|
STIX_ASSERT (b1 >= 0);
|
|
STIX_ASSERT (b2 >= b1);
|
|
|
|
/* the block context object created here is used
|
|
* as a base object for block context activation.
|
|
* primitive_block_context_value() clones a block
|
|
* context and activates the cloned context.
|
|
* this base block context is created with no
|
|
* stack for this reason. */
|
|
blkctx = (stix_oop_context_t)stix_instantiate (stix, stix->_block_context, STIX_NULL, 0);
|
|
if (!blkctx) return -1;
|
|
|
|
/* the long forward jump instruction has the format of
|
|
* 11000100 KKKKKKKK or 11000100 KKKKKKKK KKKKKKKK
|
|
* depending on STIX_BCODE_LONG_PARAM_SIZE. change 'ip' to point to
|
|
* the instruction after the jump. */
|
|
blkctx->ip = STIX_OOP_FROM_SMINT(stix->ip + STIX_BCODE_LONG_PARAM_SIZE + 1);
|
|
/* stack pointer below the bottom. this block context has an
|
|
* empty stack anyway. */
|
|
blkctx->sp = STIX_OOP_FROM_SMINT(-1);
|
|
/* the number of arguments for a block context is local to the block */
|
|
blkctx->method_or_nargs = STIX_OOP_FROM_SMINT(b1);
|
|
/* the number of temporaries here is an accumulated count including
|
|
* the number of temporaries of a home context */
|
|
blkctx->ntmprs = STIX_OOP_FROM_SMINT(b2);
|
|
|
|
blkctx->home = (stix_oop_t)stix->active_context;
|
|
blkctx->receiver_or_source = stix->_nil; /* no source */
|
|
|
|
blkctx->origin = stix->active_context->origin;
|
|
ACTIVE_STACK_PUSH (stix, (stix_oop_t)blkctx);
|
|
break;
|
|
}
|
|
|
|
case BCODE_SEND_BLOCK_COPY:
|
|
{
|
|
stix_ooi_t nargs, ntmprs;
|
|
stix_oop_context_t rctx;
|
|
stix_oop_context_t blkctx;
|
|
printf ("SEND_BLOCK_COPY\n");
|
|
/* it emulates thisContext blockCopy: nargs ofTmprCount: ntmprs */
|
|
STIX_ASSERT (stix->sp >= 2);
|
|
|
|
STIX_ASSERT (STIX_CLASSOF(stix, ACTIVE_STACK_GETTOP(stix)) == stix->_small_integer);
|
|
ntmprs = STIX_OOP_TO_SMINT(ACTIVE_STACK_GETTOP(stix));
|
|
ACTIVE_STACK_POP (stix);
|
|
|
|
STIX_ASSERT (STIX_CLASSOF(stix, ACTIVE_STACK_GETTOP(stix)) == stix->_small_integer);
|
|
nargs = STIX_OOP_TO_SMINT(ACTIVE_STACK_GETTOP(stix));
|
|
ACTIVE_STACK_POP (stix);
|
|
|
|
STIX_ASSERT (nargs >= 0);
|
|
STIX_ASSERT (ntmprs >= nargs);
|
|
|
|
/* the block context object created here is used
|
|
* as a base object for block context activation.
|
|
* primitive_block_context_value() clones a block
|
|
* context and activates the cloned context.
|
|
* this base block context is created with no
|
|
* stack for this reason. */
|
|
blkctx = (stix_oop_context_t)stix_instantiate (stix, stix->_block_context, STIX_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 = (stix_oop_context_t)ACTIVE_STACK_GETTOP(stix);
|
|
STIX_ASSERT (rctx == stix->active_context);
|
|
|
|
/* [NOTE]
|
|
* blkctx->caller is left to nil. it is set to the
|
|
* active context before it gets activated. see
|
|
* primitive_block_context_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:.
|
|
* TODO: devise a new byte code to eliminate stack pushing.
|
|
*
|
|
* 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 STIX_BCODE_LONG_PARAM_SIZE. change 'ip' to point to
|
|
* the instruction after the jump. */
|
|
blkctx->ip = STIX_OOP_FROM_SMINT(stix->ip + STIX_BCODE_LONG_PARAM_SIZE + 1);
|
|
blkctx->sp = STIX_OOP_FROM_SMINT(-1);
|
|
/* the number of arguments for a block context is local to the block */
|
|
blkctx->method_or_nargs = STIX_OOP_FROM_SMINT(nargs);
|
|
/* the number of temporaries here is an accumulated count including
|
|
* the number of temporaries of a home context */
|
|
blkctx->ntmprs = STIX_OOP_FROM_SMINT(ntmprs);
|
|
|
|
blkctx->home = (stix_oop_t)rctx;
|
|
blkctx->receiver_or_source = stix->_nil;
|
|
|
|
#if 0
|
|
if (rctx->home == stix->_nil)
|
|
{
|
|
/* the context that receives the blockCopy message is a method context */
|
|
STIX_ASSERT (STIX_CLASSOF(stix, rctx) == stix->_method_context);
|
|
STIX_ASSERT (rctx == (stix_oop_t)stix->active_context);
|
|
blkctx->origin = (stix_oop_context_t)rctx;
|
|
}
|
|
else
|
|
{
|
|
/* a block context is active */
|
|
STIX_ASSERT (STIX_CLASSOF(stix, rctx) == stix->_block_context);
|
|
blkctx->origin = ((stix_oop_block_context_t)rctx)->origin;
|
|
}
|
|
#else
|
|
|
|
/* [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.
|
|
*/
|
|
blkctx->origin = rctx->origin;
|
|
#endif
|
|
|
|
ACTIVE_STACK_SETTOP (stix, (stix_oop_t)blkctx);
|
|
break;
|
|
}
|
|
|
|
case BCODE_NOOP:
|
|
/* do nothing */
|
|
break;
|
|
|
|
|
|
default:
|
|
printf ("UNKNOWN BYTE CODE ENCOUNTERED %x\n", (int)bcode);
|
|
stix->errnum = STIX_EINTERN;
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
done:
|
|
printf ("TOTAL_INST_COUTNER = %lu\n", (unsigned long int)inst_counter);
|
|
return 0;
|
|
|
|
|
|
oops:
|
|
/* TODO: anything to do here? */
|
|
return -1;
|
|
}
|
|
|
|
int stix_invoke (stix_t* stix, const stix_ucs_t* objname, const stix_ucs_t* mthname)
|
|
{
|
|
if (activate_initial_context (stix, objname, mthname) <= -1) return -1;
|
|
return stix_execute (stix);
|
|
}
|