2 * The LittleSmalltalk byte code interpreter
4 * ---------------------------------------------------------------
5 * Little Smalltalk, Version 5
7 * Copyright (C) 1987-2005 by Timothy A. Budd
8 * Copyright (C) 2007 by Charles R. Childers
9 * Copyright (C) 2005-2007 by Danny Reinhold
10 * Copyright (C) 2010 by Ketmar // Vampire Avalon
12 * ============================================================================
13 * This license applies to the virtual machine and to the initial image of
14 * the Little Smalltalk system and to all files in the Little Smalltalk
15 * packages except the files explicitly licensed with another license(s).
16 * ============================================================================
17 * Permission is hereby granted, free of charge, to any person obtaining a copy
18 * of this software and associated documentation files (the "Software"), to deal
19 * in the Software without restriction, including without limitation the rights
20 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
21 * copies of the Software, and to permit persons to whom the Software is
22 * furnished to do so, subject to the following conditions:
24 * The above copyright notice and this permission notice shall be included in
25 * all copies or substantial portions of the Software.
27 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
30 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
32 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
33 * DEALINGS IN THE SOFTWARE.
37 * bytecode interpreter module
39 * given a process object, execute bytecodes in a tight loop.
41 * performs subroutine calls for
42 * a) garbage collection
43 * b) finding a non-cached method
44 * c) executing a primitive
45 * d) creating an integer
47 * otherwise simply loops until time slice has ended
57 #include "primlib/lst_primitives.h"
61 lstObject
*lstPrimCtx
= NULL
;
66 #define COLLECT_METHOD_STATISTICS
69 #define MARKARG_INLINER_CHECK
70 #define INLINER_ACTIVE
72 #define INLINE_SOME_METHODS
79 // windoze msvcrt.dll is idiotic
82 # define PRINTF_LLD "%ld"
84 # define PRINTF_LLD "%lld"
87 # define PRINTF_LLD "%I64d"
90 #define BETTER_CACHE_CONTROL
92 LstEventCheckFn lstEventCheck
= NULL
;
94 int lstExecUserBreak
= 0;
96 unsigned int lstDebugFlag
= 0;
98 unsigned int lstInfoCacheHit
= 0;
99 unsigned int lstInfoCacheMiss
= 0;
101 unsigned int lstInfoLiteralHit
= 0;
102 unsigned int lstInfoIVarHit
= 0;
104 static int lstSuspended
= 0;
107 #define FNV_PRIME 16777619
108 #define FNV_OFFSET_BASIS 2166136261uL
111 static uint32_t fnvHash (const void *str
, int slen
) {
112 const unsigned char *buf
= (const unsigned char *)str
;
113 uint32_t acc
= FNV_OFFSET_BASIS
;
114 if (slen
> SIZE_TH
) {
115 int left
= slen
-SIZE_TH
;
116 if (left
> SIZE_TH
) left
= SIZE_TH
;
117 const unsigned char *buf1
= (const unsigned char *)str
;
118 buf1
= buf1
+slen
-left
;
119 while (slen
-- > 0) { acc
*= FNV_PRIME
; acc
^= *buf
++; }
120 while (left
-- > 0) { acc
*= FNV_PRIME
; acc
^= *buf1
++; }
122 while (slen
-- > 0) { acc
*= FNV_PRIME
; acc
^= *buf
++; }
128 static inline int LST_RSTACK_NSP (void) {
129 if (lstRootTop
>= LST_ROOTSTACK_LIMIT
) lstFatal("out of root stack", 0);
134 /* The following are roots for the file out */
135 lstObject
*lstNilObj
= NULL
;
136 lstObject
*lstTrueObj
= NULL
;
137 lstObject
*lstFalseObj
= NULL
;
138 lstObject
*lstBooleanClass
= NULL
;
139 lstObject
*lstSmallIntClass
= NULL
;
140 lstObject
*lstCharClass
= NULL
;
141 lstObject
*lstArrayClass
= NULL
;
142 lstObject
*lstBlockClass
= NULL
;
143 lstObject
*lstContextClass
= NULL
;
144 lstObject
*lstProcessClass
= NULL
;
145 lstObject
*lstStringClass
= NULL
;
146 lstObject
*lstSymbolClass
= NULL
;
147 lstObject
*lstByteArrayClass
= NULL
;
148 lstObject
*lstByteCodeClass
= NULL
;
149 lstObject
*lstMethodClass
= NULL
;
150 lstObject
*lstGlobalObj
= NULL
;
151 lstObject
*lstBinMsgs
[LST_MAX_BIN_MSG
] = { NULL
};
152 lstObject
*lstIntegerClass
= NULL
;
153 lstObject
*lstFloatClass
= NULL
;
154 lstObject
*lstNumberClass
= NULL
;
155 lstObject
*lstBadMethodSym
= NULL
;
156 lstObject
*lstInitMethod
= NULL
;
157 lstObject
*lstLoadMethod
= NULL
;
158 lstObject
*lstDoStrMethod
= NULL
;
159 lstObject
*lstReplMethod
= NULL
;
160 lstObject
*lstNewSymMethod
= NULL
;
161 lstObject
*lstSetGlobMethod
= NULL
;
164 #ifdef INLINE_SOME_METHODS
165 static lstObject
*lstMetaCharClass
= NULL
;
167 static lstObject
*lstArrayAtMethod
= NULL
;
168 static lstObject
*lstArraySizeMethod
= NULL
;
169 static lstObject
*lstMetaCharNewMethod
= NULL
;
170 static lstObject
*lstStringAtIfAbsentMethod
= NULL
;
171 static lstObject
*lstStringAtMethod
= NULL
;
172 static lstObject
*lstStringBasicAtPutMethod
= NULL
;
173 static lstObject
*lstStringPrintStringMethod
= NULL
;
174 static lstObject
*lstSymbolPrintStringMethod
= NULL
;
175 static lstObject
*lstBlockValue1Method
= NULL
;
182 } lstInlineMethodList
[] = {
183 {2, "at:", &lstArrayClass
, &lstArrayAtMethod
},
184 {1, "size", &lstArrayClass
, &lstArraySizeMethod
},
185 {2, "at:", &lstStringClass
, &lstStringAtMethod
},
186 {1, "printString", &lstStringClass
, &lstStringPrintStringMethod
},
187 {1, "printString", &lstSymbolClass
, &lstSymbolPrintStringMethod
},
188 {3, "basicAt:put:", &lstStringClass
, &lstStringBasicAtPutMethod
},
189 {2, "new:", &lstMetaCharClass
, &lstMetaCharNewMethod
},
190 {3, "at:ifAbsent:", &lstStringClass
, &lstStringAtIfAbsentMethod
},
191 {2, "value:", &lstBlockClass
, &lstBlockValue1Method
},
197 #define DBGCHAN stderr
203 static void indent (lstObject
*ctx
) {
204 static int oldlev
= 0;
206 while (ctx
&& (ctx
!= lstNilObj
)) {
209 ctx
= ctx
->data
[lstIVpreviousContextInContext
];
211 /* this lets you use your editor's brace matching to match up opening and closing indentation levels */
215 for (x = lev; x < oldlev; ++x) fputc('}', DBGCHAN);
216 } else if (lev > oldlev) {
218 for (x = oldlev; x < lev; ++x) fputc('{', DBGCHAN);
225 # define PC (curIP-1)
226 # define DBG0(msg) if (lstDebugFlag) {indent(context); fprintf(DBGCHAN, "%d: %s\n", PC, msg);}
227 # define DBG1(msg, arg) if (lstDebugFlag) {indent(context); fprintf(DBGCHAN, "%d: %s %d\n", PC, msg, arg);}
228 # define DBG2(msg, arg, arg1) if (lstDebugFlag) {indent(context); fprintf(DBGCHAN, "%d: %s %d %d\n", PC, msg, arg, arg1);}
229 # define DBGS(msg, cl, sel) \
230 if (lstDebugFlag) { \
232 char clnm[1024], selnm[1024]; \
233 lstGetString(clnm, sizeof(clnm), (lstObject *) cl); \
234 lstGetString(selnm, sizeof(selnm), (lstObject *) sel); \
235 fprintf(DBGCHAN, "%d: %s %s %s\n", PC, msg, clnm, selnm); }
238 # define DBG1(msg, arg)
239 # define DBG2(msg, arg, arg1)
240 # define DBGS(msg, cl, sel)
245 # define dprintf(...) fprintf(DBGCHAN, __VA_ARGS__)
247 # define dprintf(...)
251 # define iprintf(...) fprintf(DBGCHAN, __VA_ARGS__)
253 # define iprintf(...)
257 static int symbolcomp (lstObject
*left
, lstObject
*right
) {
258 int leftsize
= LST_SIZE(left
);
259 int rightsize
= LST_SIZE(right
);
260 int minsize
= leftsize
;
262 if (rightsize
< minsize
) minsize
= rightsize
;
263 /* use faster comparison */
265 if ((i
= memcmp(lstBytePtr(left
), lstBytePtr(right
), minsize
))) return i
;
267 return leftsize
-rightsize
;
271 /* method lookup routine, used when cache miss occurs */
272 static lstObject
*lookupMethod (lstObject
*selector
, lstObject
*stclass
) {
273 lstObject
*dict
, *keys
, *vals
, *val
;
274 LstInt low
, high
, mid
;
275 /* scan upward through the class hierarchy */
276 for (; stclass
!= lstNilObj
; stclass
= stclass
->data
[lstIVparentClassInClass
]) {
277 /* consider the Dictionary of methods for this Class */
278 #if 0 & defined(DEBUG)
280 static char tb
[1024];
281 fprintf(stderr
, "st=%p; u=%p; sz=%d\n", stclass
, lstNilObj
, LST_SIZE(stclass
));
282 lstGetString(tb
, sizeof(tb
), stclass
->data
[lstIVnameInClass
]);
283 fprintf(stderr
, " [%s]\n", tb
);
287 if (LST_IS_SMALLINT(stclass
)) lstFatal("lookupMethod: looking in SmallInt instance", 0);
288 if (LST_IS_BYTES(stclass
)) lstFatal("lookupMethod: looking in binary object", 0);
289 if (LST_SIZE(stclass
) < lstClassSize
) lstFatal("lookupMethod: looking in non-class object", 0);
291 dict
= stclass
->data
[lstIVmethodsInClass
];
293 if (!dict
) lstFatal("lookupMethod: NULL dictionary", 0);
294 if (LST_IS_SMALLINT(dict
)) lstFatal("lookupMethod: SmallInt dictionary", 0);
295 if (dict
->stclass
!= lstFindGlobal("Dictionary")) lstFatal("lookupMethod: method list is not a dictionary", 0);
297 keys
= dict
->data
[0];
299 high
= LST_SIZE(keys
);
300 /* do a binary search through its keys, which are Symbol's. */
303 val
= keys
->data
[mid
];
304 /* if we find the selector, return the method lstObject. */
305 if (val
== selector
) {
306 vals
= dict
->data
[1];
307 return vals
->data
[mid
];
309 /* otherwise continue the binary search */
310 if (symbolcomp(selector
, val
) < 0) high
= mid
; else low
= mid
+1;
313 /* sorry, couldn't find a method */
318 /* method cache for speeding method lookup */
319 /* why 703? we have two primes: 701, 709, 719; let's try 719 */
320 #define MTD_CACHE_SIZE 719
321 #define MTD_CACHE_EXTRA 4
322 #define MTD_BAD_HIT_MAX 16
327 int badHits
; /* after MTD_BAD_HIT_MAX this cache item will be cleared */
330 lstObject
*mConst
; /* constant for methods returning constant */
331 int ivarNum
; /* ivar number for methods returning ivar */
332 } cache
[MTD_CACHE_SIZE
+MTD_CACHE_EXTRA
];
335 /* flush dynamic methods when GC occurs */
336 void lstFlushMethodCache (void) {
337 memset(cache
, 0, sizeof(cache
));
342 typedef struct LstRunContext LstRunContext
;
343 struct LstRunContext
{
344 /* ticks and locks fields will be filled only on process suspension */
348 LstRunContext
*prev
; /* previous process in group */
351 typedef struct LstRunGroup LstRunGroup
;
353 LstRunGroup
*prev
; /* prev group */
354 LstRunGroup
*next
; /* next group */
355 LstRunContext
*group
; /* next group */
356 int ticks
; /* for the whole group; used on sheduling */
357 int ewait
; /* >0: normal process waiting for the event */
361 static LstRunContext
*rsFree
= NULL
; /*TODO: free when too many*/
362 static LstRunGroup
*runGroups
= NULL
; /* list of all process groups */
363 static LstRunGroup
*curGroup
= NULL
; /* current run group */
364 static int finGroupCount
= 0;
365 static int runOnlyFins
= 0;
367 /* allocate new run context in the current group */
368 static LstRunContext
*allocRunContext (void) {
369 LstRunContext
*res
= rsFree
;
373 res
= calloc(1, sizeof(LstRunContext
));
375 res
->prev
= curGroup
->group
;
376 curGroup
->group
= res
;
381 /* release top context in the current group; return previous one */
382 static LstRunContext
*releaseRunContext (void) {
383 LstRunContext
*c
= curGroup
->group
;
385 curGroup
->group
= c
->prev
;
389 return curGroup
->group
;
393 static void lstCreateFinalizePGroup (lstObject
*prc
) {
394 LstRunGroup
*g
= calloc(1, sizeof(LstRunGroup
)), *p
= curGroup
?curGroup
:runGroups
;
395 LstRunContext
*c
= calloc(1, sizeof(LstRunContext
));
398 p
->next
= g
; /* can't be first group anyway */
399 if (g
->next
) g
->next
->prev
= g
;
401 /* note that we can't allocate objects here, 'cause this thing will be called from inside GC */
402 c
->ticksLeft
= 10000;
410 * note that process locks locks all groups now;
411 * this MUST be changed: we have to use fine-grained locks,
412 * mutexes and other cool things
416 typedef struct LstEventHandler LstEventHandler
;
417 struct LstEventHandler
{
418 LstEventHandler
*next
;
419 /*lstObject *process;*/
423 static LstEventHandler
*ehList
= NULL
;
426 static LstRunGroup
*findEventHandler (int eid
) {
427 LstEventHandler
*cur
, *prev
;
428 for (cur
= ehList
, prev
= NULL
; cur
; prev
= cur
, cur
= cur
->next
) {
429 if (cur
->eid
== eid
) {
430 LstRunGroup
*grp
= cur
->grp
;
431 /* remove from the list */
432 if (prev
) prev
->next
= cur
->next
; else ehList
= cur
->next
;
441 static void addOneShotEventHandler (int eid
, LstRunGroup
*grp
) {
442 LstEventHandler
*cur
= calloc(1, sizeof(LstEventHandler
));
450 #include "lst_memory.c"
453 static int groupHasProcess (const LstRunGroup
*g
, const lstObject
*prc
) {
454 const LstRunContext
*c
;
455 for (c
= g
->group
; c
; c
= c
->prev
) if (c
->process
== prc
) return 1;
463 # define POPIT (stack->data[--stackTop])
464 # define PUSHIT(n) if (stackTop >= LST_SIZE(stack)) { lstBackTrace(context); lstFatal("method stack overflow", curIP); } else stack->data[stackTop++] = (n)
466 # define POPIT (stack->data[--stackTop])
467 # define PUSHIT(n) stack->data[stackTop++] = (n)
471 /* Code locations are extracted as VAL's */
472 #define VAL (bp[curIP] | (bp[curIP+1] << 8))
473 /*#define VALSIZE 2*/
476 #define XRETURN(value) { LST_LEAVE_BLOCK(); return (value); }
478 #define GET_BCODE_OP(ip) \
479 low = (high = bp[ip++])&0x0F; high >>= 4; \
480 if (high == lstBCExtended) { high = low; low = bp[ip++]; }
483 #define CALC_CACHE_HASH(sel, cls) \
484 (LstUInt)((intptr_t)(sel)+(intptr_t)(cls))%MTD_CACHE_SIZE;
486 int lstEvtCheckLeft
= 1000;
488 static int resetEvtCheckLeft
= 0;
489 void lstResetEvtCheckLeft (void) { resetEvtCheckLeft
= 1; }
491 static int lastFailedPrim
= 0;
492 static int lastCalledPrim
= 0;
494 static int lstExecuteInternal (lstObject
*aProcess
, int ticks
, int locked
) {
498 lstObject
*retValue
= lstNilObj
;
499 lstObject
*context
= NULL
;
500 lstObject
*method
= NULL
;
501 lstObject
*stack
= NULL
;
502 lstObject
*arguments
= NULL
;
503 lstObject
*temporaries
= NULL
;
504 lstObject
*instanceVariables
= NULL
;
505 lstObject
*literals
= NULL
;
506 lstObject
*ptemp
= NULL
;
507 lstObject
*ptemp1
= NULL
;
508 lstObject
*messageSelector
;
509 lstObject
*receiverClass
;
511 int lockCount
= locked
>0;
512 const unsigned char *bp
;
518 int evtCheckLeft
= lstEvtCheckLeft
;
519 int oTicks
= curGroup
->ticks
;
520 int wasRunInWaits
= 1;
521 int grpTicks
= 10000;
524 /* reload all the necessary vars from the current context */
525 void reloadFromCtx (void) {
526 method
= context
->data
[lstIVmethodInContext
];
527 stack
= context
->data
[lstIVstackInContext
];
528 temporaries
= context
->data
[lstIVtemporariesInContext
];
529 arguments
= context
->data
[lstIVargumentsInContext
];
530 literals
= method
->data
[lstIVliteralsInMethod
];
531 instanceVariables
= arguments
->data
[lstIVreceiverInArguments
];
532 curIP
= lstIntValue(context
->data
[lstIVbytePointerInContext
]);
533 stackTop
= lstIntValue(context
->data
[lstIVstackTopInContext
]);
536 /* reloca current group state */
537 void reloadFromGroup (void) {
538 LstRunContext
*rc
= curGroup
->group
; /* current context */
539 aProcess
= rc
->process
;
540 ticks
= rc
->ticksLeft
;
541 lockCount
= rc
->lockCount
;
542 context
= aProcess
->data
[lstIVcontextInProcess
];
544 if (curGroup
->ewait
> 0) { lockCount
= 0; evtCheckLeft
= 1; } /* force event query */
547 /* load new process to the current group */
548 int loadNewProcess (lstObject
*newProc
) {
549 if (!newProc
|| newProc
== lstNilObj
) return lstReturnError
;
550 if (newProc
->data
[lstIVrunningInProcess
] != lstNilObj
) return lstReturnError
; /* already running/suspended */
551 /* get current context information */
552 context
= newProc
->data
[lstIVcontextInProcess
];
553 if (!context
|| context
== lstNilObj
) return lstReturnError
; /* terminated */
554 method
= context
->data
[lstIVmethodInContext
];
555 if (!method
|| method
== lstNilObj
) return lstReturnError
; /* the thing that should not be */
558 newProc
->data
[lstIVrunningInProcess
] = lstTrueObj
;
559 /* now create new runnint context */
560 LstRunContext
*rc
= allocRunContext();
561 rc
->process
= newProc
;
562 rc
->lockCount
= lockCount
;
563 rc
->ticksLeft
= ticks
;
567 /* fix process and context info */
568 void saveCurrentProcess (void) {
569 if (!curGroup
->group
) return;
570 if (curGroup
->ewait
<= 0) {
571 aProcess
->data
[lstIVresultInProcess
] = lstNilObj
;
572 aProcess
->data
[lstIVcontextInProcess
] = context
;
573 if (context
!= lstNilObj
) {
574 context
->data
[lstIVbytePointerInContext
] = lstNewInt(curIP
);
575 context
->data
[lstIVstackTopInContext
] = lstNewInt(stackTop
);
576 LstRunContext
*rc
= curGroup
->group
; /* current context */
577 rc
->ticksLeft
= ticks
;
578 rc
->lockCount
= lockCount
;
583 /* switch to next group and reload it */
584 void nextGroup (int skipIt
) {
586 if (skipIt
&& curGroup
) { saveCurrentProcess(); curGroup
= curGroup
->next
; }
587 if (!curGroup
) curGroup
= runGroups
;
590 dprintf("rof: cg=%p\n", curGroup
);
591 for (f
= 2; f
> 0; --f
) {
593 while (curGroup
&& !curGroup
->group
) curGroup
= curGroup
->next
;
594 } while (curGroup
&& !curGroup
->finalizer
);
596 curGroup
= runGroups
;
598 if (!curGroup
) lstFatal("internal error in finalizing stage", 0);
599 dprintf("rof: found cg=%p\n", curGroup
);
600 dprintf("rof: ctx=%p\n", curGroup
->group
);
601 dprintf("rof: fin=%d\n", curGroup
->finalizer
);
602 dprintf("rof: fin left=%d\n", finGroupCount
);
603 if (curGroup
&& !curGroup
->group
) {
604 if (finGroupCount
> 0) lstFatal("internal error in finalizing stage", 1);
608 for (f
= 2; f
> 0; --f
) {
609 while (curGroup
&& curGroup
->ewait
) curGroup
= curGroup
->next
;
611 curGroup
= runGroups
;
613 if (!curGroup
) curGroup
= runGroups
;
618 /* curGroup can be NULL after returning */
619 /* result==NULL: trying to kill main group */
620 LstRunGroup
*removeCurrentGroup (void) {
621 if (curGroup
== runGroups
) return NULL
;
622 /* exclude from the list */
623 curGroup
->prev
->next
= curGroup
->next
; /* it's safe, 'cause we can't remove the first (main) group */
624 if (curGroup
->next
) curGroup
->next
->prev
= curGroup
->prev
;
625 LstRunGroup
*pg
= curGroup
;
626 if (!(curGroup
= curGroup
->next
)) curGroup
= runGroups
;
630 /* return from process */
631 /* on return: low is the result; tmp!=0: switched to suspended context */
632 int doReturn (int res
) {
634 saveCurrentProcess();
635 LstRunContext
*rc
= curGroup
->group
; /* current context */
636 /*saveCurrentProcess();*/
638 aProcess
->data
[lstIVrunningInProcess
] = lstNilObj
;
639 aProcess
->data
[lstIVresultInProcess
] = retValue
;
640 if (res
== lstReturnReturned
) aProcess
->data
[lstIVcontextInProcess
] = lstNilObj
;
641 if ((rc
= releaseRunContext())) {
643 aProcess
= rc
->process
;
645 tmp
= (curGroup
->ewait
!= 0);
646 return 0; /* ok, the show must go on */
648 /* group is out of bussines now; exit if this is the main group */
649 if (curGroup
== runGroups
) {
650 /* 'main group': so get out of here */
651 runGroups
->ticks
= oTicks
;
654 /* remove empty group */
655 if (curGroup
->finalizer
) --finGroupCount
;
657 LstRunGroup
*pg
= removeCurrentGroup();
660 tmp
= (curGroup
->ewait
!= 0);
662 dprintf("return-switched from %p to %p\n", pg
, curGroup
);
664 dprintf("ctx=%p; mth=%p; ip=%d; tmp=%d\n", context
, method
, curIP
, tmp
);
667 return 0; /* don't stop at the top */
671 lstExecUserBreak
= 0;
673 assert(runGroups
->group
== NULL
);
683 curGroup
= runGroups
; /* switch to 'main' */
684 runGroups
->ticks
= ticks
;
685 if (loadNewProcess(aProcess
) != 0) {
686 releaseRunContext(); /* drop dummy context */
687 curGroup
= NULL
; /* restore old group */
688 return lstReturnError
; /* barf */
699 LST_TEMP(temporaries
);
700 LST_TEMP(instanceVariables
);
708 if (curGroup
->ewait
< 0) {
709 /* new waiting process */
710 saveCurrentProcess();
711 curGroup
->ewait
= -curGroup
->ewait
;
712 /*dprintf("%p: suspend for %d: ip=%d; sp=%d\n", curGroup, curGroup->ewait, curIP, stackTop);*/
713 evtCheckLeft
= 1; lockCount
= 0;
717 if (finGroupCount
< 1) {
718 runOnlyFins
= finGroupCount
= 0;
719 XRETURN(lstReturnAPISuspended
);
721 if (!curGroup
->finalizer
) {
725 if (curGroup
->ewait
> 0) {
726 curGroup
->finalizer
= 0;
733 if (evtCheckLeft
> 0 && (--evtCheckLeft
== 0)) {
734 evtCheckLeft
= lstEvtCheckLeft
;
735 if (lstExecUserBreak
) {
736 /* C API break; get out of here */
737 saveCurrentProcess();
740 fprintf(stderr
, "FUCK! SUSPEND!\n");
741 if (curGroup
== runGroups
) fprintf(stderr
, "SUSPEND IN MAIN GROUP!\n");
743 XRETURN(lstReturnAPISuspended
);
747 if ((id
= lstEventCheck(&ticks
)) > 0) {
748 LstRunGroup
*grp
= findEventHandler(id
);
750 /* save current process */
751 if (curGroup
->ewait
== 0) saveCurrentProcess();
752 /* wake up suspended process */
753 /*dprintf("found process group for %d\n", id);*/
754 /* switch to this context */
755 assert(grp
->ewait
== id
);
756 grp
->ewait
= 0; /* not waiting anymore */
759 /*dprintf("%p: resume: ip=%d; sp=%d\n", curGroup, curIP, stackTop);*/
760 goto doAllAgain
; /* continue with the next bytecode */
764 /* other shedulers */
765 if (curGroup
->ewait
== 0) {
766 /* process group sheduling */
767 if (grpTicks
> 0 && (--grpTicks
== 0)) {
769 if (runGroups
->next
) {
770 dprintf("GRPSHEDULE!\n");
771 LstRunGroup
*og
= curGroup
;
773 if (og
!= curGroup
) goto doAllAgain
; /* go on with the new process */
776 /* if we're running against a CPU tick count, shedule execution when we expire the given number of ticks */
777 if (ticks
> 0 && (--ticks
== 0)) {
779 /* locked; no sheduling */
780 ticks
= 1; /* this will slow down the process, but locks shouldn't be held for the long time */
782 dprintf("TimeExpired: lockCount=%d\n", lockCount
);
783 int rr
= doReturn(lstReturnTimeExpired
);
785 if (tmp
|| retGSwitch
) goto doAllAgain
;
791 if (curGroup
->ewait
> 0) {
792 /* this process is in the wait state */
793 /*dprintf("process are waiting for: %d\n", curGroup->ewait);*/
794 LstRunGroup
*og
= curGroup
;
797 if (og
!= curGroup
) dprintf("switched from %p to %p\n", og
, curGroup
);
799 if (og
== curGroup
|| !wasRunInWaits
) {
800 /*dprintf(" releasing time slice\n");*/
801 usleep(1); /* release timeslice */
809 /* decode the instruction */
810 bp
= (const unsigned char *)lstBytePtr(method
->data
[lstIVbyteCodesInMethod
]);
814 case lstBCPushInstance
:
815 DBG1("PushInstance", low
);
816 PUSHIT(instanceVariables
->data
[low
]);
818 case lstBCPushArgument
:
819 DBG1("PushArgument", low
);
820 PUSHIT(arguments
->data
[low
]);
822 case lstBCPushTemporary
:
823 DBG1("PushTemporary", low
);
824 PUSHIT(temporaries
->data
[low
]);
826 case lstBCPushLiteral
:
827 DBG1("PushLiteral", low
);
828 PUSHIT(literals
->data
[low
]);
830 case lstBCPushConstant
:
833 DBG0("PushConstant nil");
837 DBG0("PushConstant true");
840 case lstBLFalseConst
:
841 DBG0("PushConstant false");
846 DBG1("PushConstant", low
);
847 PUSHIT(lstNewInt(low
));
851 case lstBCAssignInstance
:
852 DBG1("AssignInstance", low
);
853 /* don't pop stack, leave result there */
854 lstWriteBarrier(&instanceVariables
->data
[low
], stack
->data
[stackTop
-1]);
856 case lstBCAssignArgument
:
857 DBG1("AssignArgument", low
);
858 /* don't pop stack, leave result there */
859 arguments
->data
[low
] = stack
->data
[stackTop
-1];
861 case lstBCAssignTemporary
:
862 DBG1("AssignTemporary", low
);
863 /* don't pop stack, leave result there */
864 temporaries
->data
[low
] = stack
->data
[stackTop
-1];
866 case lstBCMarkArguments
:
867 DBG1("MarkArguments", low
);
868 #ifdef MARKARG_INLINER_CHECK
869 if (ticks
!= 1 && low
> 1 && low
<= 3) {
870 /* check if next opcode is SendMessage */
871 switch (bp
[curIP
]/16) {
872 case lstBCSendMessage
:
876 messageSelector
= literals
->data
[l0
];
877 receiverClass
= stack
->data
[stackTop
-low
];
878 /*iprintf("stackTop: %d; low: %d; rc: %p\n", stackTop, low, receiverClass);*/
879 receiverClass
= LST_CLASS(receiverClass
);
880 tmp
= CALC_CACHE_HASH(messageSelector
, receiverClass
);
881 if (cache
[tmp
].name
== messageSelector
&& cache
[tmp
].stclass
== receiverClass
) {
882 checkForInlineCacheHit
:
883 # ifdef INLINE_SOME_METHODS
884 { int f
; op
= cache
[tmp
].method
;
885 for (f
= 0; lstInlineMethodList
[f
].name
; ++f
) {
886 if (low
== lstInlineMethodList
[f
].argc
&& *(lstInlineMethodList
[f
].method
) == op
) {
887 op
= stack
->data
[stackTop
-low
]; /* self */
888 if (LST_IS_SMALLINT(op
)) break; /* invalid object */
890 case 0: /* Array>>at: */
891 /*fprintf(stderr, "Array>>at: hit!\n");*/
892 if (LST_IS_BYTES(op
)) break;
893 op1
= stack
->data
[stackTop
-1]; /* index */
894 if (LST_IS_SMALLINT(op1
)) {
895 l0
= lstIntValue(op1
)-1;
896 if (l0
< 0 || l0
>= LST_SIZE(op
)) break; /* out of range */
898 retValue
= op
->data
[l0
];
900 goto markArgsInlined
;
903 case 1: /* Array>>size */
904 /*fprintf(stderr, "Array>>size hit!\n");*/
907 retValue
= lstNewInt(l0
);
909 goto markArgsInlined
;
910 case 2: /* String>>at: */
911 if (!LST_IS_BYTES(op
)) break; /* not a string */
912 op1
= stack
->data
[stackTop
-1]; /* index */
913 if (LST_IS_SMALLINT(op1
)) {
914 l0
= lstIntValue(op1
)-1;
915 if (l0
< 0 || l0
>= LST_SIZE(op
)) break; /* out of range */
917 l0
= lstBytePtr(op
)[l0
];
918 retValue
= lstCharClass
->data
[lstIVcharsInMetaChar
]->data
[l0
];
920 goto markArgsInlined
;
923 case 3: /* String>>printString */
924 /*fprintf(stderr, "String>>printString hit!\n");*/
925 if (op
->stclass
== lstSymbolClass
) {
927 l0
= LST_SIZE(ptemp
);
928 retValue
= (lstObject
*)lstMemAllocBin(l0
);
929 retValue
->stclass
= lstStringClass
;
930 if (l0
> 0) memcpy(lstBytePtr(retValue
), lstBytePtr(ptemp
), l0
);
932 } else if (op
->stclass
== lstStringClass
) {
939 goto markArgsInlined
;
940 case 4: /* Symbol>>printString */
941 /*fprintf(stderr, "Symbol>>printString hit!\n");*/
942 if (op
->stclass
== lstSymbolClass
) {
944 l0
= LST_SIZE(ptemp
);
945 retValue
= (lstObject
*)lstMemAllocBin(l0
);
946 retValue
->stclass
= lstStringClass
;
947 if (l0
> 0) memcpy(lstBytePtr(retValue
), lstBytePtr(ptemp
), l0
);
949 } else if (op
->stclass
== lstStringClass
) {
954 goto markArgsInlined
;
955 case 5: /* String>>basicAt:put: */
956 /*fprintf(stderr, "String>>basicAt:put: hit!\n");*/
957 if (!LST_IS_BYTES(op
)) break; /* not a string */
958 op1
= stack
->data
[stackTop
-2]; /* index */
959 if (LST_IS_SMALLINT(op1
)) {
960 l0
= lstIntValue(op1
)-1;
961 if (l0
< 0 || l0
>= LST_SIZE(op
)) break; /* out of range */
962 op1
= stack
->data
[stackTop
-1]; /* value */
963 if (LST_IS_SMALLINT(op1
)) {
965 lstBytePtr(op
)[l0
] = lstIntValue(op1
);
968 goto markArgsInlined
;
972 case 6: /* MetaChar>>new: */
973 /*fprintf(stderr, "MetaChar>>new: hit!\n");*/
974 op1
= stack
->data
[stackTop
-1]; /* value */
975 if (LST_IS_SMALLINT(op1
)) {
976 l0
= lstIntValue(op1
);
977 if (l0
< 0 || l0
>= 257) break; /* out of range */
979 retValue
= lstCharClass
->data
[lstIVcharsInMetaChar
]->data
[l0
];
981 goto markArgsInlined
;
984 case 7: /* String>>at:ifAbsent: */
985 /*fprintf(stderr, "String>>at:ifAbsent: hit!\n");*/
986 if (!LST_IS_BYTES(op
)) break; /* not a string */
987 op1
= stack
->data
[stackTop
-2]; /* index */
988 if (LST_IS_SMALLINT(op1
)) {
989 l0
= lstIntValue(op1
)-1;
990 if (l0
< 0 || l0
>= LST_SIZE(op
)) break; /* out of range */
992 l0
= lstBytePtr(op
)[l0
];
993 retValue
= lstCharClass
->data
[lstIVcharsInMetaChar
]->data
[l0
];
995 goto markArgsInlined
;
998 case 8: /* Block>>value: */
999 /*fprintf(stderr, "Block>>value: hit!\n");*/
1001 /* swap argumnets */
1002 op1
= stack
->data
[stackTop
-1];
1003 stack
->data
[stackTop
-1] = op
;
1004 stack
->data
[stackTop
-2] = op1
;
1005 ptemp
= lstNilObj
; /* flag */
1006 context
->data
[lstIVbytePointerInContext
] = lstNewInt(curIP
);
1007 context
->data
[lstIVstackTopInContext
] = lstNewInt(stackTop
-2);
1008 goto doBlockInvocation
;
1010 fprintf(stderr
, "ready to inline: %s\n", lstInlineMethodList
[f
].name
);
1017 if (low
!= 1 && low
!= 2) goto markArgsNoInlining
;
1019 if (cache
[tmp
].analyzed
<= 0) break;
1020 /*stackTop -= low;*/ /* remove all args */
1021 /* do inline, omit argument array creation */
1023 cache
[tmp
].badHits
= 0;
1024 l0
= bp
[curIP
= l1
]; /* skip SendMessage */
1026 case lstBCDoSpecial
*16+lstBXStackReturn
:
1027 context
= context
->data
[lstIVpreviousContextInContext
];
1029 case lstBCDoSpecial
*16+lstBXBlockReturn
:
1030 context
= context
->data
[lstIVcreatingContextInBlock
]->data
[lstIVpreviousContextInContext
];
1036 # ifdef INLINE_SOME_METHODS
1038 if (l0
) goto doReturn2
;
1039 stack
->data
[stackTop
++] = retValue
;
1040 goto markArgsCompleteNoPush
;
1043 /* execute inline code */
1044 if ((l1
= cache
[tmp
].ivarNum
) >= 0) {
1045 /* instance variable */
1046 if (cache
[tmp
].analyzed
== 1) {
1047 iprintf("ANALYZER: pushing ivar %d\n", l1
);
1048 retValue
= stack
->data
[stackTop
-1]->data
[l1
];
1050 iprintf("ANALYZER: setting ivar %d\n", l1
);
1052 (retValue
= stack
->data
[stackTop
-2])->data
[l1
] = stack
->data
[stackTop
-1];
1053 --stackTop
; /* drop argument, return self */
1054 if (l0
) { ++lstInfoIVarHit
; goto doReturn2
; }
1055 goto markArgsCompleteNoPush
;
1060 iprintf("ANALYZER: pushing constant/literal\n");
1061 ++lstInfoLiteralHit
;
1062 retValue
= cache
[tmp
].mConst
;
1064 if (l0
) goto doReturn2
;
1065 stack
->data
[stackTop
-1] = retValue
;
1066 goto markArgsCompleteNoPush
;
1067 } else if (cache
[tmp
+1].name
== messageSelector
&& cache
[tmp
+1].stclass
== receiverClass
) {
1068 /*++cache[tmp++].badHits;*/
1070 goto checkForInlineCacheHit
;
1074 if (bp
[curIP
]%16 == lstBCSendMessage
) {
1077 goto checkForInline
;
1082 # ifdef INLINE_SOME_METHODS
1087 op
= lstMemAlloc(low
);
1088 op
->stclass
= lstArrayClass
;
1089 /* now load new argument array */
1090 while (--low
>= 0) op
->data
[low
] = POPIT
;
1092 markArgsCompleteNoPush
:
1094 case lstBCPushBlock
:
1096 /* create a block object; low is arg location; next word is goto value; next byte is argCount */
1099 tmp
= bp
[curIP
++]; /* argCount */
1100 ptemp
= lstNewArray(lstIntValue(method
->data
[lstIVstackSizeInMethod
]));
1101 op
= lstMemAlloc(lstBlockSize
);
1102 op
->stclass
= lstBlockClass
;
1103 /*op = lstAllocInstance(lstBlockSize, lstBlockClass);*/
1104 op
->data
[lstIVbytePointerInContext
] = op
->data
[lstIVstackTopInBlock
] = lstNewInt(0);
1105 op
->data
[lstIVpreviousContextInBlock
] = lstNilObj
;
1106 op
->data
[lstIVbytePointerInBlock
] = lstNewInt(curIP
);
1107 op
->data
[lstIVargumentLocationInBlock
] = lstNewInt(low
);
1108 op
->data
[lstIVstackInBlock
] = ptemp
;
1109 op
->data
[lstIVargCountInBlock
] = lstNewInt(tmp
);
1110 op
->data
[lstIVcreatingContextInBlock
] =
1111 context
->stclass
==lstBlockClass
? context
->data
[lstIVcreatingContextInBlock
] : context
;
1112 op
->data
[lstIVprocOwnerInBlock
] = aProcess
;
1113 op
->data
[lstIVmethodInBlock
] = method
;
1114 op
->data
[lstIVargumentsInBlock
] = arguments
;
1115 op
->data
[lstIVtemporariesInBlock
] = temporaries
;
1121 case lstBCSendUnary
: /* optimize certain unary messages */
1122 DBG1("SendUnary", low
);
1126 retValue
= op
==lstNilObj
? lstTrueObj
: lstFalseObj
;
1128 case 1: /* notNil */
1129 retValue
= op
==lstNilObj
? lstFalseObj
: lstTrueObj
;
1132 lstFatal("unimplemented SendUnary", low
);
1136 case lstBCSendBinary
: /* optimize certain binary messages */
1137 DBG1("SendBinary", low
);
1142 retValue
= ptemp
==ptemp1
? lstTrueObj
: lstFalseObj
;
1144 ptemp
= ptemp1
= NULL
;
1147 /* small integers */
1148 if (LST_IS_SMALLINT(ptemp
) && LST_IS_SMALLINT(ptemp1
)) {
1149 int i
= lstIntValue(ptemp
);
1150 int j
= lstIntValue(ptemp1
);
1153 retValue
= i
<j
? lstTrueObj
: lstFalseObj
;
1156 retValue
= i
<=j
? lstTrueObj
: lstFalseObj
;
1159 itmp
= (int64_t)i
+j
;
1160 retValue
= lstNewInteger(itmp
);
1163 itmp
= (int64_t)i
-j
;
1164 retValue
= lstNewInteger(itmp
);
1167 itmp
= (int64_t)i
*j
;
1168 retValue
= lstNewInteger(itmp
);
1171 if (j
== 0) goto binoptfailed
;
1172 retValue
= lstNewInt(i
/j
);
1175 if (j
== 0) goto binoptfailed
;
1176 retValue
= lstNewInt(i
%j
);
1179 retValue
= i
>j
? lstTrueObj
: lstFalseObj
;
1182 retValue
= i
>=j
? lstTrueObj
: lstFalseObj
;
1185 retValue
= i
!=j
? lstTrueObj
: lstFalseObj
;
1188 retValue
= i
==j
? lstTrueObj
: lstFalseObj
;
1190 default: goto binoptfailed
;
1193 ptemp
= ptemp1
= NULL
;
1197 if (LST_CLASS(ptemp
) == lstCharClass
&& LST_CLASS(ptemp1
) == lstCharClass
) {
1198 int i
= lstIntValue(ptemp
->data
[0]);
1199 int j
= lstIntValue(ptemp1
->data
[0]);
1202 retValue
= i
<j
? lstTrueObj
: lstFalseObj
;
1205 retValue
= i
<=j
? lstTrueObj
: lstFalseObj
;
1208 retValue
= i
>j
? lstTrueObj
: lstFalseObj
;
1211 retValue
= i
>=j
? lstTrueObj
: lstFalseObj
;
1214 retValue
= i
!=j
? lstTrueObj
: lstFalseObj
;
1217 retValue
= i
==j
? lstTrueObj
: lstFalseObj
;
1219 default: goto binoptfailed
;
1222 ptemp
= ptemp1
= NULL
;
1226 if (ptemp
== lstTrueObj
|| ptemp
== lstFalseObj
) {
1227 /* can only do operations that won't trigger garbage collection */
1230 retValue
= ptemp
==lstTrueObj
? ptemp1
: lstFalseObj
;
1233 retValue
= ptemp
==lstTrueObj
? lstTrueObj
: ptemp1
;
1239 ptemp
= ptemp1
= NULL
;
1243 if (ptemp
== lstNilObj
) {
1244 /* can only do operations that won't trigger garbage collection */
1247 retValue
= lstFalseObj
;
1256 ptemp
= ptemp1
= NULL
;
1259 /* logics, not bool, not nil */
1260 if (LST_IS_SMALLINT(ptemp
) || ptemp
->stclass
!= lstBooleanClass
) {
1272 ptemp
= ptemp1
= NULL
;
1276 if (LST_IS_BYTES(ptemp
) && LST_IS_BYTES(ptemp1
)) {
1279 retValue
= symbolcomp(ptemp
, ptemp1
)<0 ? lstTrueObj
: lstFalseObj
;
1282 retValue
= symbolcomp(ptemp
, ptemp1
)<=0 ? lstTrueObj
: lstFalseObj
;
1285 if (ptemp
->stclass
== ptemp1
->stclass
&&
1286 (ptemp
->stclass
== lstStringClass
|| ptemp
->stclass
== lstByteArrayClass
||
1287 ptemp
->stclass
== lstByteCodeClass
)) {
1288 /* string concatenation */
1289 retValue
= (lstObject
*)lstMemAllocBin(LST_SIZE(ptemp
)+LST_SIZE(ptemp1
));
1290 retValue
->stclass
= ptemp
->stclass
;
1291 tmp
= LST_SIZE(ptemp
);
1292 if (tmp
) memcpy(lstBytePtr(retValue
), lstBytePtr(ptemp
), tmp
);
1293 l0
= LST_SIZE(ptemp1
);
1294 if (l0
) memcpy(lstBytePtr(retValue
)+tmp
, lstBytePtr(ptemp1
), l0
);
1299 retValue
= symbolcomp(ptemp
, ptemp1
)>0 ? lstTrueObj
: lstFalseObj
;
1302 retValue
= symbolcomp(ptemp
, ptemp1
)>=0 ? lstTrueObj
: lstFalseObj
;
1305 retValue
= symbolcomp(ptemp
, ptemp1
)!=0 ? lstTrueObj
: lstFalseObj
;
1308 retValue
= symbolcomp(ptemp
, ptemp1
)==0 ? lstTrueObj
: lstFalseObj
;
1310 default: goto binoptfailed
;
1313 ptemp
= ptemp1
= NULL
;
1316 /* do message send */
1318 arguments
= lstMemAlloc(2);
1319 arguments
->stclass
= lstArrayClass
;
1320 /* now load new argument array */
1321 arguments
->data
[0] = ptemp
;
1322 arguments
->data
[1] = ptemp1
;
1323 /* now go send message */
1324 messageSelector
= lstBinMsgs
[low
];
1325 ptemp
= ptemp1
= NULL
;
1326 goto findMethodFromSymbol
;
1327 case lstBCSendMessage
:
1328 /*DBG1("SendMessage, literal", low);*/
1329 messageSelector
= literals
->data
[low
];
1331 findMethodFromSymbol
:
1332 /* see if we can optimize tail call */
1333 if (ticks
== 1) l0
= 0;
1335 switch (bp
[curIP
]) {
1336 case lstBCDoSpecial
*16+lstBXStackReturn
: l0
= 1; break;
1337 case lstBCDoSpecial
*16+lstBXBlockReturn
: l0
= 2; break;
1338 default: l0
= 0; break;
1341 findMethodFromSymbol1
:
1342 receiverClass
= LST_CLASS(arguments
->data
[lstIVreceiverInArguments
]);
1343 assert(LST_CLASS(messageSelector
) == lstSymbolClass
);
1344 DBGS("SendMessage", receiverClass
->data
[lstIVnameInClass
], messageSelector
);
1346 assert(LST_CLASS(messageSelector
) == lstSymbolClass
);
1349 char clnm
[256], selnm
[256];
1350 lstGetString(clnm
, sizeof(clnm
), (lstObject
*)LST_CLASS(receiverClass
)->data
[lstIVnameInClass
]);
1351 lstGetString(selnm
, sizeof(selnm
), (lstObject
*)messageSelector
);
1352 fprintf(stderr
, "%04d: searching: %s>>%s\n", PC
, clnm
, selnm
);
1355 tmp
= CALC_CACHE_HASH(messageSelector
, receiverClass
);
1356 /*tmp = (LstUInt)((intptr_t)messageSelector+(intptr_t)receiverClass)%MTD_CACHE_SIZE;*/
1357 if (cache
[tmp
].name
== messageSelector
&& cache
[tmp
].stclass
== receiverClass
) {
1359 } else if (cache
[tmp
+1].name
== messageSelector
&& cache
[tmp
+1].stclass
== receiverClass
) {
1360 ++cache
[tmp
++].badHits
;
1361 cacheHit
: method
= cache
[tmp
].method
;
1365 if (++cache
[tmp
].badHits
>= MTD_BAD_HIT_MAX
) cache
[tmp
].name
= NULL
; /* clear this cache item */
1366 if (++cache
[tmp
+1].badHits
>= MTD_BAD_HIT_MAX
) cache
[tmp
+1].name
= NULL
; /* clear this cache item */
1367 method
= lookupMethod(messageSelector
, receiverClass
);
1369 /* send 'doesNotUnderstand:args:' */
1370 if (messageSelector
== lstBadMethodSym
) lstFatal("doesNotUnderstand:args: missing", 0);
1371 /* we can reach this code only once */
1372 ptemp
= receiverClass
;
1373 ptemp1
= messageSelector
;
1374 op
= lstMemAlloc(3);
1375 op
->stclass
= lstArrayClass
;
1376 op
->data
[lstIVreceiverInArguments
] = arguments
->data
[lstIVreceiverInArguments
];
1377 op
->data
[1] = ptemp1
; /* selector */
1378 op
->data
[2] = arguments
;
1380 receiverClass
= ptemp
; /* restore selector */
1381 ptemp
= ptemp1
= NULL
;
1382 messageSelector
= lstBadMethodSym
;
1383 goto findMethodFromSymbol1
;
1385 if (cache
[tmp
].name
&& cache
[tmp
].badHits
<= MTD_BAD_HIT_MAX
/2) ++tmp
;
1386 /*if (cache[tmp].name) ++tmp;*/
1387 cache
[tmp
].name
= messageSelector
;
1388 cache
[tmp
].stclass
= receiverClass
;
1389 cache
[tmp
].method
= method
;
1390 cache
[tmp
].goodHits
= 0; /* perfectly good cache */
1391 /*cache[tmp].analyzed = (LST_SIZE(arguments) != 1) ? -1 : 0*/;
1392 #ifdef INLINER_ACTIVE
1393 if ((op
= method
->data
[lstIVoptimDoneInMethod
]) != lstNilObj
) {
1394 if (op
== lstFalseObj
) {
1395 cache
[tmp
].analyzed
= -1; /* should not be analyzed */
1397 cache
[tmp
].analyzed
= 1; /* already analyzed */
1398 if (LST_IS_SMALLINT(op
)) {
1400 int f
= lstIntValue(op
);
1402 cache
[tmp
].analyzed
= 2;
1404 iprintf("ANALYZER: already analyzed setter; ivar %d\n", f
);
1406 iprintf("ANALYZER: already analyzed; ivar %d\n", f
);
1408 cache
[tmp
].ivarNum
= f
;
1410 cache
[tmp
].mConst
= method
->data
[lstIVretResInMethod
];
1411 cache
[tmp
].ivarNum
= -1;
1412 iprintf("ANALYZER: already analyzed; constant\n");
1416 op
= method
->data
[lstIVargCountInMethod
];
1417 if (LST_IS_SMALLINT(op
) && (lstIntValue(op
) == 1 || lstIntValue(op
) == 2)) {
1418 iprintf("ANALYZER: to be analyzed (argc=%d)\n", lstIntValue(op
));
1419 cache
[tmp
].analyzed
= 0; /* analyze it in the future */
1421 iprintf("ANALYZER: never be analyzed; argc=%d\n", LST_IS_SMALLINT(op
) ? lstIntValue(op
) : -666);
1422 cache
[tmp
].analyzed
= -1; /* never */
1423 method
->data
[lstIVoptimDoneInMethod
] = lstFalseObj
; /* 'never' flag */
1428 cache
[tmp
].badHits
= 0; /* good cache */
1429 #ifdef INLINER_ACTIVE
1430 if (cache
[tmp
].analyzed
> 0) {
1432 if (ticks
== 1) goto analyzerJustDoIt
;
1435 case 1: context
= context
->data
[lstIVpreviousContextInContext
]; break;
1436 case 2: context
= context
->data
[lstIVcreatingContextInBlock
]->data
[lstIVpreviousContextInContext
]; break;
1437 default: l0
= 0; break;
1440 if ((l1
= cache
[tmp
].ivarNum
) >= 0) {
1441 /* instance variable */
1442 if (cache
[tmp
].analyzed
== 1) {
1443 iprintf("ANALYZER!: pushing ivar %d\n", l1
);
1444 retValue
= arguments
->data
[lstIVreceiverInArguments
]->data
[l1
];
1446 iprintf("ANALYZER!: setting ivar %d\n", l1
);
1447 assert(cache
[tmp
].analyzed
== 2);
1448 assert(LST_SIZE(arguments
) == 2);
1449 (retValue
= arguments
->data
[lstIVreceiverInArguments
])->data
[l1
] = arguments
->data
[1];
1454 iprintf("ANALYZER!: pushing constant/literal\n");
1455 retValue
= cache
[tmp
].mConst
;
1456 ++lstInfoLiteralHit
;
1458 /* restore changed vars */
1459 if (l0
) goto doReturn2
;
1460 method
= context
->data
[lstIVmethodInContext
];
1461 arguments
= context
->data
[lstIVargumentsInContext
];
1464 } else if (!cache
[tmp
].analyzed
) {
1465 if (++cache
[tmp
].goodHits
> 3) {
1466 /* analyze method */
1467 bp
= (const unsigned char *)lstBytePtr(method
->data
[lstIVbyteCodesInMethod
]);
1468 op
= method
->data
[lstIVargCountInMethod
];
1469 if (lstIntValue(op
) == 1) {
1472 case lstBCPushInstance
:
1473 if (bp
[1] != lstBCDoSpecial
*16+lstBXStackReturn
) goto analyzeFailed
;
1474 cache
[tmp
].ivarNum
= bp
[0]%16;
1476 case lstBCPushLiteral
:
1477 if (bp
[1] != lstBCDoSpecial
*16+lstBXStackReturn
) goto analyzeFailed
;
1478 cache
[tmp
].mConst
= method
->data
[lstIVliteralsInMethod
]->data
[bp
[0]%16];
1479 cache
[tmp
].ivarNum
= -1;
1481 case lstBCPushConstant
:
1482 if (bp
[1] != lstBCDoSpecial
*16+lstBXStackReturn
) goto analyzeFailed
;
1483 cache
[tmp
].ivarNum
= -1;
1485 case lstBLNilConst
: cache
[tmp
].mConst
= lstNilObj
; break;
1486 case lstBLTrueConst
: cache
[tmp
].mConst
= lstTrueObj
; break;
1487 case lstBLFalseConst
: cache
[tmp
].mConst
= lstFalseObj
; break;
1488 default: l1
= (bp
[0]%16)-3; cache
[tmp
].mConst
= lstNewInt(l1
); break;
1493 case lstBCPushInstance
:
1494 if (bp
[2] != lstBCDoSpecial
*16+lstBXStackReturn
) goto analyzeFailed
;
1495 cache
[tmp
].ivarNum
= bp
[1];
1497 case lstBCPushLiteral
:
1498 if (bp
[2] != lstBCDoSpecial
*16+lstBXStackReturn
) goto analyzeFailed
;
1499 cache
[tmp
].mConst
= method
->data
[lstIVliteralsInMethod
]->data
[bp
[1]];
1500 cache
[tmp
].ivarNum
= -1;
1502 case lstBCPushConstant
:
1503 if (bp
[2] != lstBCDoSpecial
*16+lstBXStackReturn
) goto analyzeFailed
;
1504 cache
[tmp
].ivarNum
= -1;
1506 case lstBLNilConst
: cache
[tmp
].mConst
= lstNilObj
; break;
1507 case lstBLTrueConst
: cache
[tmp
].mConst
= lstTrueObj
; break;
1508 case lstBLFalseConst
: cache
[tmp
].mConst
= lstFalseObj
; break;
1509 default: l1
= bp
[1]-3; cache
[tmp
].mConst
= lstNewInt(l1
); break;
1512 default: goto analyzeFailed
;
1515 default: goto analyzeFailed
;
1517 iprintf("ANALYZER: succeed; ivar=%d\n", cache
[tmp
].ivarNum
);
1518 cache
[tmp
].analyzed
= 1;
1520 assert(lstIntValue(op
) == 2);
1523 0000: PushArgument 1
1524 0001: AssignInstance n
1528 /*TODO: parse extended lstBCAssignInstance*/
1529 if (bp
[0] == lstBCPushArgument
*16+1 && bp
[1]/16 == lstBCAssignInstance
&&
1530 bp
[2] == lstBCDoSpecial
*16+lstBXPopTop
&& bp
[3] == lstBCDoSpecial
*16+lstBXSelfReturn
) {
1531 /*goto analyzeFailed;*/
1532 iprintf("ANALYZER: setter found; ivar=%d\n", bp
[1]%16);
1533 cache
[tmp
].analyzed
= 2;
1534 cache
[tmp
].ivarNum
= bp
[1]%16;
1539 /* setup method info, so we can omit analyze stage in future */
1540 if (cache
[tmp
].ivarNum
>= 0) {
1541 int f
= cache
[tmp
].ivarNum
;
1542 if (cache
[tmp
].analyzed
== 2) f
= -(f
+1);
1543 method
->data
[lstIVoptimDoneInMethod
] = lstNewInt(f
);
1545 method
->data
[lstIVoptimDoneInMethod
] = lstTrueObj
;
1546 method
->data
[lstIVretResInMethod
] = cache
[tmp
].mConst
;
1548 goto analyzeSucceed
;
1550 cache
[tmp
].analyzed
= -1;
1551 method
->data
[lstIVoptimDoneInMethod
] = lstFalseObj
;
1556 #ifdef COLLECT_METHOD_STATISTICS
1557 l1
= lstIntValue(method
->data
[lstIVinvokeCountInMethod
])+1;
1558 if (LST_64FITS_SMALLINT(l1
)) method
->data
[lstIVinvokeCountInMethod
] = lstNewInt(l1
);
1561 /* save current IP and SP */
1562 context
->data
[lstIVstackTopInContext
] = lstNewInt(stackTop
);
1563 context
->data
[lstIVbytePointerInContext
] = lstNewInt(curIP
);
1564 /*context->data[lstIVprocOwnerInContext] = aProcess;*/
1565 /* build environment for new context */
1566 low
= lstIntValue(method
->data
[lstIVtemporarySizeInMethod
]);
1567 stack
= lstNewArray(lstIntValue(method
->data
[lstIVstackSizeInMethod
]));
1568 temporaries
= low
>0 ? lstNewArray(low
) : lstNilObj
;
1569 /* build the new context */
1570 context
= lstMemAlloc(lstContextSize
);
1571 context
->stclass
= lstContextClass
;
1572 /*context = lstAllocInstance(lstContextSize, lstContextClass);*/
1573 /*context->data[lstIVpreviousContextInContext] = ptemp;*/
1576 context
->data
[lstIVpreviousContextInContext
] = ptemp
->data
[lstIVpreviousContextInContext
];
1579 context
->data
[lstIVpreviousContextInContext
] =
1580 ptemp
->data
[lstIVcreatingContextInBlock
]->data
[lstIVpreviousContextInContext
];
1583 context
->data
[lstIVpreviousContextInContext
] = ptemp
;
1587 context
->data
[lstIVprocOwnerInContext
] = aProcess
;
1588 context
->data
[lstIVtemporariesInContext
] = temporaries
;
1589 context
->data
[lstIVstackInContext
] = stack
;
1590 context
->data
[lstIVstackTopInContext
] =
1591 context
->data
[lstIVbytePointerInContext
] = lstNewInt(0);
1592 context
->data
[lstIVmethodInContext
] = method
;
1593 context
->data
[lstIVargumentsInContext
] = arguments
;
1594 literals
= method
->data
[lstIVliteralsInMethod
];
1595 instanceVariables
= arguments
->data
[lstIVreceiverInArguments
];
1598 /* now go execute new method */
1600 /* execute primitive */
1601 case lstBCDoPrimitive
:
1602 /* low is argument count; next byte is primitive number */
1603 high
= bp
[curIP
++]; /* primitive number */
1605 /*DBG2("DoPrimitive", high, low);*/
1607 const char *pn
= lstFindPrimitiveName(high
);
1609 sprintf(tmsg
, "DoPrimitive %s; argc=%d", pn
, low
);
1613 lastCalledPrim
= high
;
1615 case 1: /* NewObject class size */
1616 if (low
!= 2) goto failPrimitiveArgs
;
1617 op
= POPIT
; /* size */
1618 op1
= POPIT
; /* class */
1619 if (!LST_IS_SMALLINT(op
)) goto failPrimitive
;
1620 tmp
= lstIntValue(op
); /* size */
1621 if (tmp
< 0) goto failPrimitive
;
1622 retValue
= lstAllocInstance(tmp
, op1
);
1624 case 2: /* NewByteArray class size */
1625 if (low
!= 2) goto failPrimitiveArgs
;
1626 op
= POPIT
; /* size */
1627 op1
= POPIT
; /* class */
1628 if (!LST_IS_SMALLINT(op
)) goto failPrimitive
;
1629 tmp
= lstIntValue(op
); /* size */
1630 if (tmp
< 0) goto failPrimitive
;
1631 retValue
= (lstObject
*)lstMemAllocBin(tmp
);
1632 retValue
->stclass
= op1
;
1633 if (tmp
> 0) memset(lstBytePtr(retValue
), 0, tmp
);
1635 case 3: /* ObjectIdentity */
1636 if (low
!= 2) goto failPrimitiveArgs
;
1639 retValue
= op
==op1
? lstTrueObj
: lstFalseObj
;
1641 case 4: /* ObjectClass */
1642 if (low
!= 1) goto failPrimitiveArgs
;
1644 retValue
= LST_CLASS(op
);
1646 case 5: /* ObjectSize */
1647 if (low
!= 1) goto failPrimitiveArgs
;
1649 tmp
= LST_IS_SMALLINT(op
) ? 0 : LST_SIZE(op
); /* SmallInt has no size at all; it's ok */
1650 retValue
= lstNewInt(tmp
);
1652 case 6: /* Array#at: obj index */
1653 if (low
!= 2) goto failPrimitiveArgs
;
1654 op
= POPIT
; /* index */
1655 op1
= POPIT
; /* obj */
1656 if (!LST_IS_SMALLINT(op
) || LST_IS_SMALLINT(op1
) || LST_IS_BYTES(op1
)) goto failPrimitive
;
1657 tmp
= lstIntValue(op
)-1;
1659 if (tmp
< 0 || (LstUInt
)tmp
>= LST_SIZE(op1
)) goto failPrimitive
;
1660 if (LST_IS_SMALLINT(op1
) || LST_IS_BYTES(op1
)) goto failPrimitive
;
1661 retValue
= op1
->data
[tmp
];
1663 case 7: /* Array#at:put: value obj index */
1664 if (low
!= 3) goto failPrimitiveArgs
;
1665 op
= POPIT
; /* index */
1666 retValue
= POPIT
; /* obj */
1667 op1
= POPIT
; /* value */
1668 if (!LST_IS_SMALLINT(op
) || LST_IS_SMALLINT(retValue
) || LST_IS_BYTES(retValue
)) goto failPrimitive
;
1669 tmp
= lstIntValue(op
)-1;
1671 if (tmp
< 0 || (LstUInt
)tmp
>= LST_SIZE(retValue
)) goto failPrimitive
;
1672 if (LST_IS_SMALLINT(retValue
) || LST_IS_BYTES(retValue
)) goto failPrimitive
;
1673 lstWriteBarrier(&retValue
->data
[tmp
], op1
);
1675 case 8: /* String#at: */
1676 if (low
!= 2) goto failPrimitiveArgs
;
1677 op
= POPIT
; /* index */
1678 op1
= POPIT
; /* object */
1679 if (!LST_IS_SMALLINT(op
) || !LST_IS_BYTES_EX(op1
)) goto failPrimitive
;
1680 tmp
= lstIntValue(op
)-1;
1682 if (tmp
< 0 || (LstUInt
)tmp
>= LST_SIZE(op1
)) goto failPrimitive
;
1683 tmp
= lstBytePtr(op1
)[tmp
];
1684 retValue
= lstNewInt(tmp
);
1686 case 9: /* String#at:put: value obj index */
1687 if (low
!= 3) goto failPrimitiveArgs
;
1688 op
= POPIT
; /* index */
1689 retValue
= POPIT
; /* obj */
1690 op1
= POPIT
; /* value */
1691 if (!LST_IS_SMALLINT(op
) || !LST_IS_BYTES_EX(retValue
) || !LST_IS_SMALLINT(op1
)) goto failPrimitive
;
1692 tmp
= lstIntValue(op
)-1;
1694 if (tmp
< 0 || (LstUInt
)tmp
>= LST_SIZE(retValue
)) goto failPrimitive
;
1695 lstBytePtr(retValue
)[tmp
] = lstIntValue(op1
);
1697 case 10: /* String#clone: what class */
1698 if (low
!= 2) goto failPrimitiveArgs
;
1699 /*TODO: check args */
1700 ptemp
= POPIT
; /* class */
1701 ptemp1
= POPIT
; /* obj */
1702 if (!LST_IS_BYTES_EX(ptemp1
)) { ptemp
= ptemp1
= NULL
; goto failPrimitive
; }
1703 tmp
= LST_SIZE(ptemp1
);
1704 retValue
= (lstObject
*)lstMemAllocBin(tmp
);
1705 retValue
->stclass
= ptemp
;
1706 if (tmp
> 0) memcpy(lstBytePtr(retValue
), lstBytePtr(ptemp1
), tmp
);
1707 ptemp
= ptemp1
= NULL
;
1709 case 11: /* String#Position: aString from: pos; match substring in a string; return index of substring or nil */
1710 case 12: /* String#LastPosition: aString from: pos; match substring in a string; return index of substring or nil */
1711 if (low
!= 3) goto failPrimitiveArgs
;
1714 if (LST_IS_SMALLINT(op
)) tmp
= lstIntValue(op
);
1715 else if (op
->stclass
== lstIntegerClass
) tmp
= lstLIntValue(op
);
1716 else { stackTop
-= 2; goto failPrimitive
; }
1717 if (tmp
< 1) tmp
= 1;
1721 if (!LST_IS_BYTES_EX(op1
)) {
1723 if (LST_IS_SMALLINT(op1
)) {
1724 x
= lstIntValue(op1
);
1725 } else if (op1
->stclass
== lstCharClass
) {
1727 if (LST_IS_SMALLINT(op1
)) x
= lstIntValue(op1
);
1729 if (x
< 0 || x
> 255) { --stackTop
; goto failPrimitive
; }
1730 sbuf
[0] = x
; sbuf
[1] = '\0';
1735 if (!LST_IS_BYTES_EX(op
)) goto failPrimitive
;
1737 l1
= op1
? LST_SIZE(op1
) : strlen(sbuf
);
1738 /*FIXME: tmp can be too big and cause the overflow*/
1739 retValue
= lstNilObj
;
1740 if (tmp
>= l0
|| l0
< 1 || l1
< 1 || l1
> l0
-tmp
) {
1741 /* can't be found, do nothing */
1743 const unsigned char *s0
= lstBytePtr(op
);
1744 const unsigned char *s1
= op1
? (const unsigned char *)lstBytePtr(op1
) : (const unsigned char *)sbuf
;
1745 s0
+= tmp
; l0
-= tmp
;
1746 /*FIXME: this can be faster, especially for LastPosition; rewrite it! */
1747 for (; l0
>= l1
; l0
--, s0
++, tmp
++) {
1748 if (memcmp(s0
, s1
, l1
) == 0) {
1749 retValue
= lstNewInt(tmp
+1);
1750 if (high
== 11) break; /* early exit for Position */
1755 case 13: /* StringCopyFromTo */
1756 if (low
!= 3) goto failPrimitiveArgs
;
1759 if (LST_IS_SMALLINT(op
)) tmp
= lstIntValue(op
);
1760 else if (op
->stclass
== lstIntegerClass
) tmp
= lstLIntValue(op
);
1761 else if (op
->stclass
== lstFloatClass
) tmp
= lstFloatValue(op
);
1762 else { stackTop
-= 2; goto failPrimitive
; }
1763 if (tmp
< 1) { stackTop
-= 2; goto failPrimitive
; }
1766 if (LST_IS_SMALLINT(op
)) x
= lstIntValue(op
);
1767 else if (op
->stclass
== lstIntegerClass
) x
= lstLIntValue(op
);
1768 else if (op
->stclass
== lstFloatClass
) x
= lstFloatValue(op
);
1769 else { --stackTop
; goto failPrimitive
; }
1770 if (x
< 1) { --stackTop
; goto failPrimitive
; }
1773 if (!LST_IS_BYTES_EX(op
)) goto failPrimitive
;
1775 /*printf("size=%d; from=%d; to=%d\n", low, x, tmp);*/
1777 if (tmp
< x
|| x
>= low
) low
= 0;
1781 low
= tmp
<low
? tmp
: low
;
1784 retValue
= (lstObject
*)lstMemAllocBin(low
);
1786 retValue
->stclass
= op
->stclass
;
1787 /*printf("copying from %d, %d bytes\n", x, low);*/
1788 if (low
> 0) memcpy(lstBytePtr(retValue
), lstBytePtr(op
)+x
, low
);
1790 case 14: /* BulkObjectExchange */
1791 if (low
!= 2) goto failPrimitiveArgs
;
1793 if (op
->stclass
!= lstArrayClass
) { --stackTop
; goto failPrimitive
; }
1795 if (retValue
->stclass
!= lstArrayClass
) goto failPrimitive
;
1796 if (LST_SIZE(op
) != LST_SIZE(retValue
)) goto failPrimitive
;
1797 lstSwapObjects(op
, retValue
, LST_SIZE(op
));
1799 case 15: { /* replaceFrom:... */ /* <replaceFrom:to:with:startingAt: start stop replacement repStart self> */
1800 if (low
!= 5) goto failPrimitiveArgs
;
1801 /*TODO: check args */
1802 retValue
= POPIT
; /* object */
1803 lstObject
*tmpRepStart
= POPIT
; /* startingAt */
1804 lstObject
*tmpSrc
= POPIT
; /* with */
1805 lstObject
*tmpStop
= POPIT
; /* to */
1806 lstObject
*tmpStart
= POPIT
; /* from */
1807 if (lstBulkReplace(retValue
, tmpStart
, tmpStop
, tmpSrc
, tmpRepStart
)) goto failPrimitive
;
1810 case 16: /* BlockInvocation: (args)* block */
1811 if (ptemp
!= NULL
) abort();
1813 if (low
< 1) goto failPrimitiveArgs
;
1814 /* low holds number of arguments */
1815 op
= POPIT
; /* block */
1817 /*if (op->data[lstIVbytePointerInContext] != lstNilObj) fprintf(stderr, "CALLING ALREADY CALLED BLOCK!\n");*/
1818 if (LST_IS_SMALLINT(op
) || LST_IS_BYTES(op
)) goto failPrimitiveArgs
;
1819 if (op
->stclass
!= lstBlockClass
&& !lstIsKindOf(op
, lstBlockClass
)) goto failPrimitiveArgs
;
1820 /*if (op->stclass != lstBlockClass) { stackTop -= (low-1); goto failPrimitiveArgs; }*/
1821 /* put arguments in place */
1822 /* get arguments location (tmp) */
1823 op1
= op
->data
[lstIVargumentLocationInBlock
];
1824 if (!LST_IS_SMALLINT(op1
)) goto failPrimitiveArgs
;
1825 tmp
= lstIntValue(op1
);
1826 /* get max argument count (l0) */
1827 op1
= op
->data
[lstIVargCountInBlock
];
1828 if (!LST_IS_SMALLINT(op1
)) goto failPrimitiveArgs
;
1829 l0
= lstIntValue(op1
);
1830 /* setup arguments */
1831 temporaries
= op
->data
[lstIVtemporariesInBlock
];
1832 /* do not barf if there are too many args; just ignore */
1833 /*fprintf(stderr, "block: args=%d; passed=%d\n", l0, low);*/
1834 if (low
> l0
) { stackTop
-= (low
-l0
); low
= l0
; } /* drop extra args */
1835 for (l1
= low
; l1
< l0
; ++l1
) temporaries
->data
[tmp
+l1
] = lstNilObj
;
1836 while (--low
>= 0) temporaries
->data
[tmp
+low
] = POPIT
;
1837 for (; low
>= 0; --low
) temporaries
->data
[tmp
+low
] = POPIT
;
1839 op
->data
[lstIVpreviousContextInBlock
] = context
->data
[lstIVpreviousContextInContext
];
1842 op
->data
[lstIVpreviousContextInBlock
] = context
;
1844 context
= /*aProcess->data[lstIVcontextInProcess] =*/ op
;
1845 context
->data
[lstIVtemporariesInContext
] = temporaries
;
1848 curIP
= lstIntValue(context
->data
[lstIVbytePointerInBlock
]);
1851 case 17: /* flush method cache; invalidate cache for class */
1853 * <#FlushMethodCache>: flush everything
1854 * <#FlushMethodCache oldclass>: flush the cache for the given class
1855 * <#FlushMethodCache oldmethod true>: flush the cache for the given method
1857 #ifdef BETTER_CACHE_CONTROL
1859 case 1: /* for class */
1860 dprintf("FLUSHCLASSCACHE\n");
1861 op
= POPIT
; /* old class */
1862 for (l0
= MTD_CACHE_SIZE
+MTD_CACHE_EXTRA
-1; l0
>= 0; --l0
) {
1863 if (cache
[l0
].name
&& cache
[l0
].stclass
== op
) cache
[l0
].name
= NULL
;
1866 case 2: /* for method */
1867 dprintf("FLUSHMETHODCACHE\n");
1868 --stackTop
; /* drop flag */
1869 op
= POPIT
; /* old method */
1870 for (l0
= MTD_CACHE_SIZE
+MTD_CACHE_EXTRA
-1; l0
>= 0; --l0
) {
1871 if (cache
[l0
].name
&& cache
[l0
].method
== op
) cache
[l0
].name
= NULL
;
1875 dprintf("FLUSHCACHE\n");
1877 lstFlushMethodCache();
1881 /*if (low == 1 || low > 3) { stackTop -= low; low = 0; }*/
1883 lstFlushMethodCache();
1887 case 18: /* SmallIntToInteger */
1888 if (low
!= 1) goto failPrimitiveArgs
;
1890 if (LST_IS_SMALLINT(op
)) retValue
= lstNewLongInt(lstIntValue(op
));
1891 else if (op
->stclass
== lstIntegerClass
) retValue
= lstNewLongInt(lstLIntValue(op
));
1892 else goto failPrimitive
;
1894 case 19: /* NumberToFloat */
1895 if (low
!= 1) goto failPrimitiveArgs
;
1897 if (LST_IS_SMALLINT(op
)) retValue
= lstNewFloat(lstIntValue(op
));
1898 else if (op
->stclass
== lstIntegerClass
) retValue
= lstNewFloat(lstLIntValue(op
));
1899 else if (op
->stclass
== lstFloatClass
) retValue
= lstNewFloat(lstFloatValue(op
));
1900 else goto failPrimitive
;
1902 case 20: /* FloatToInteger */
1903 if (low
< 1 || low
> 2) goto failPrimitiveArgs
;
1904 op
= POPIT
; /* float */
1906 op1
= POPIT
; /* opcode */
1907 if (!LST_IS_SMALLINT(op1
)) goto failPrimitive
;
1908 if (!LST_IS_BYTES_EX(op
) || op
->stclass
!= lstFloatClass
) goto failPrimitive
;
1909 fop0
= lstFloatValue(op
);
1910 switch (lstIntValue(op1
)) {
1911 case 1: fop0
= trunc(fop0
); break;
1912 case 2: fop0
= round(fop0
); break;
1913 case 3: fop0
= floor(fop0
); break;
1914 case 4: fop0
= ceil(fop0
); break;
1915 default: goto failPrimitive
;
1918 retValue
= lstNewInteger(ll0
);
1920 if (LST_IS_SMALLINT(op
)) retValue
= lstNewLongInt(lstIntValue(op
));
1921 else if (op
->stclass
== lstIntegerClass
) retValue
= lstNewLongInt(lstLIntValue(op
));
1922 else if (op
->stclass
== lstFloatClass
) retValue
= lstNewLongInt((LstLInt
)lstFloatValue(op
));
1923 else goto failPrimitive
;
1926 case 21: /* IntegerToSmallInt (low order of Integer -> SmallInt) */
1927 if (low
!= 1) goto failPrimitiveArgs
;
1929 if (LST_IS_SMALLINT(op
)) ll0
= lstIntValue(op
);
1930 else if (op
->stclass
== lstIntegerClass
) ll0
= lstLIntValue(op
);
1931 else goto failPrimitive
;
1933 if (!LST_64FITS_SMALLINT(tmp
)) goto failPrimitive
;
1934 retValue
= lstNewInt(tmp
);
1936 case 22: /* IntegerToSmallIntTrunc */
1937 if (low
!= 1) goto failPrimitiveArgs
;
1939 if (LST_IS_SMALLINT(op
)) retValue
= op
;
1940 else if (op
->stclass
== lstIntegerClass
) {
1941 ll0
= lstLIntValue(op
);
1943 retValue
= lstNewInt(tmp
);
1944 } else if (op
->stclass
== lstFloatClass
) {
1945 ll0
= (LstLInt
)(lstFloatValue(op
));
1947 retValue
= lstNewInt(tmp
);
1948 } else goto failPrimitive
;
1951 case 23: /* bit2op: bitOr: bitAnd: bitXor: */
1952 if (low
!= 3) goto failPrimitiveArgs
;
1953 /* operation type */
1955 if (!LST_IS_SMALLINT(op
)) { stackTop
-= 2; goto failPrimitive
; }
1956 tmp
= lstIntValue(op
); /* operation */
1959 if (LST_IS_SMALLINT(op
)) ll1
= lstIntValue(op
);
1960 else if (op
->stclass
== lstIntegerClass
) ll1
= lstLIntValue(op
);
1961 else { --stackTop
; goto failPrimitive
; }
1964 if (LST_IS_SMALLINT(op
)) ll0
= lstIntValue(op
);
1965 else if (op
->stclass
== lstIntegerClass
) ll0
= lstLIntValue(op
);
1966 else goto failPrimitive
;
1968 case 0: ll0
= ll0
| ll1
; break;
1969 case 1: ll0
= ll0
& ll1
; break;
1970 case 2: ll0
= ll0
^ ll1
; break;
1971 default: goto failPrimitive
;
1973 retValue
= lstNewInteger(ll0
);
1975 case 24: /* bitNot */
1976 if (low
!= 1) goto failPrimitiveArgs
;
1978 if (LST_IS_SMALLINT(op
)) ll0
= lstIntValue(op
);
1979 else if (op
->stclass
== lstIntegerClass
) ll0
= lstLIntValue(op
);
1980 else goto failPrimitive
;
1981 retValue
= lstNewInteger(~ll0
);
1983 case 25: /* bitShift: */
1984 if (low
!= 2) goto failPrimitiveArgs
;
1987 if (!LST_IS_SMALLINT(op
)) { --stackTop
; goto failPrimitive
; }
1988 tmp
= lstIntValue(op
); /* shift count */
1991 if (LST_IS_SMALLINT(op
)) ll0
= lstIntValue(op
);
1992 else if (op
->stclass
== lstIntegerClass
) ll0
= lstLIntValue(op
);
1993 else goto failPrimitive
;
1995 /* negative means shift right */
2001 retValue
= lstNewInteger(ll0
);
2004 case 26: /* SmallIntAdd */
2005 case 27: /* SmallIntSub */
2006 case 28: /* SmallIntMul */
2007 case 29: /* SmallIntDiv */
2008 case 30: /* SmallIntMod */
2009 case 31: /* SmallIntLess */
2010 case 32: /* SmallLessEqu */
2011 case 33: /* SmallIntGreat */
2012 case 34: /* SmallIntGreatEqu */
2013 case 35: /* SmallIntEqu */
2014 case 36: /* SmallIntNotEqu */
2015 if (low
!= 2) goto failPrimitiveArgs
;
2018 if (!LST_IS_SMALLINT(op
) || !LST_IS_SMALLINT(op1
)) goto failPrimitive
;
2019 l1
= lstIntValue(op1
);
2020 l0
= lstIntValue(op
);
2023 case 26: itmp
= (int64_t)l0
+l1
; retValue
= lstNewInteger(itmp
); break;
2024 case 27: itmp
= (int64_t)l0
-l1
; retValue
= lstNewInteger(itmp
); break;
2025 case 28: itmp
= (int64_t)l0
*l1
; retValue
= lstNewInteger(itmp
); break;
2026 case 29: if (l1
== 0) goto failPrimitive
; l0
/= l1
; retValue
= lstNewInt(l0
); break;
2027 case 30: if (l1
== 0) goto failPrimitive
; l0
%= l1
; retValue
= lstNewInt(l0
); break;
2031 case 31: retValue
= l0
<l1
? lstTrueObj
: lstFalseObj
; break;
2032 case 32: retValue
= l0
<=l1
? lstTrueObj
: lstFalseObj
; break;
2033 case 33: retValue
= l0
>l1
? lstTrueObj
: lstFalseObj
; break;
2034 case 34: retValue
= l0
>=l1
? lstTrueObj
: lstFalseObj
; break;
2035 case 35: retValue
= l0
==l1
? lstTrueObj
: lstFalseObj
; break;
2036 case 36: retValue
= l0
!=l1
? lstTrueObj
: lstFalseObj
; break;
2040 case 37: /* IntegerAdd */
2041 case 38: /* IntegerSub */
2042 case 39: /* IntegerMul */
2043 case 40: /* IntegerDiv */
2044 case 41: /* IntegerMod */
2045 case 42: /* IntegerLess */
2046 case 43: /* IntegerLessEqu */
2047 case 44: /* IntegerGreat */
2048 case 45: /* IntegerGreatEqu */
2049 case 46: /* IntegerEqu */
2050 case 47: /* IntegerNotEqu */
2051 if (low
!= 2) goto failPrimitiveArgs
;
2054 if (LST_IS_SMALLINT(op1
)) ll1
= lstIntValue(op1
);
2055 else if (op1
->stclass
== lstIntegerClass
) ll1
= lstLIntValue(op1
);
2056 else goto failPrimitive
;
2057 if (LST_IS_SMALLINT(op
)) ll0
= lstIntValue(op
);
2058 else if (op
->stclass
== lstIntegerClass
) ll0
= lstLIntValue(op
);
2059 else goto failPrimitive
;
2061 case 37: retValue
= lstNewLongInt(ll0
+ll1
); break;
2062 case 38: retValue
= lstNewLongInt(ll0
-ll1
); break;
2063 case 39: retValue
= lstNewLongInt(ll0
*ll1
); break;
2064 case 40: if (ll1
== 0) goto failPrimitive
; retValue
= lstNewLongInt(ll0
/ll1
); break;
2065 case 41: if (ll1
== 0) goto failPrimitive
; retValue
= lstNewLongInt(ll0
%ll1
); break;
2066 case 42: retValue
= ll0
<ll1
? lstTrueObj
: lstFalseObj
; break;
2067 case 43: retValue
= ll0
<=ll1
? lstTrueObj
: lstFalseObj
; break;
2068 case 44: retValue
= ll0
>ll1
? lstTrueObj
: lstFalseObj
; break;
2069 case 45: retValue
= ll0
>=ll1
? lstTrueObj
: lstFalseObj
; break;
2070 case 46: retValue
= ll0
==ll1
? lstTrueObj
: lstFalseObj
; break;
2071 case 47: retValue
= ll0
!=ll1
? lstTrueObj
: lstFalseObj
; break;
2074 case 48: /* FloatAdd */
2075 case 49: /* FloatSub */
2076 case 50: /* FloatMul */
2077 case 51: /* FloatDiv */
2078 case 52: /* FloatLess */
2079 case 53: /* FloatLessEqu */
2080 case 54: /* FloatGreat */
2081 case 55: /* FloatGreatEqu */
2082 case 56: /* FloatEqu */
2083 case 57: /* FloatNotEqu */
2084 if (low
!= 2) goto failPrimitiveArgs
;
2087 if (LST_IS_SMALLINT(op
)) fop1
= (LstFloat
)lstIntValue(op
);
2088 else if (op
->stclass
== lstIntegerClass
) fop1
= (LstFloat
)lstLIntValue(op
);
2089 else if (op
->stclass
== lstFloatClass
) fop1
= lstFloatValue(op
);
2090 else { --stackTop
; goto failPrimitive
; }
2093 if (LST_IS_SMALLINT(op
)) fop0
= (LstFloat
)lstIntValue(op
);
2094 else if (op
->stclass
== lstIntegerClass
) fop0
= (LstFloat
)lstLIntValue(op
);
2095 else if (op
->stclass
== lstFloatClass
) fop0
= lstFloatValue(op
);
2096 else goto failPrimitive
;
2098 case 48: retValue
= lstNewFloat(fop0
+fop1
); break;
2099 case 49: retValue
= lstNewFloat(fop0
-fop1
); break;
2100 case 50: retValue
= lstNewFloat(fop0
*fop1
); break;
2101 case 51: if (fop0
== 0.0) goto failPrimitive
; retValue
= lstNewFloat(fop0
/fop1
); break;
2102 case 52: retValue
= fop0
<fop1
? lstTrueObj
: lstFalseObj
; break;
2103 case 53: retValue
= fop0
<=fop1
? lstTrueObj
: lstFalseObj
; break;
2104 case 54: retValue
= fop0
>fop1
? lstTrueObj
: lstFalseObj
; break;
2105 case 55: retValue
= fop0
>=fop1
? lstTrueObj
: lstFalseObj
; break;
2106 case 56: retValue
= fop0
==fop1
? lstTrueObj
: lstFalseObj
; break;
2107 case 57: retValue
= fop0
!=fop1
? lstTrueObj
: lstFalseObj
; break;
2110 case 58: /* FloatToString */
2111 if (low
!= 1) goto failPrimitiveArgs
;
2113 if (LST_IS_SMALLINT(op
)) sprintf(sbuf
, "%d", lstIntValue(op
));
2114 else if (op
->stclass
== lstIntegerClass
) sprintf(sbuf
, PRINTF_LLD
, lstLIntValue(op
));
2115 else if (op
->stclass
== lstFloatClass
) sprintf(sbuf
, "%.15g", lstFloatValue(op
));
2116 else goto failPrimitive
;
2117 retValue
= lstNewString(sbuf
);
2119 case 59: /* FloatNegate */
2120 if (low
!= 1) goto failPrimitiveArgs
;
2122 if (LST_IS_SMALLINT(op
)) fop0
= lstIntValue(op
);
2123 else if (op
->stclass
== lstIntegerClass
) fop0
= lstLIntValue(op
);
2124 else if (op
->stclass
== lstFloatClass
) fop0
= lstFloatValue(op
);
2125 else goto failPrimitive
;
2126 retValue
= lstNewFloat(-fop0
);
2129 case 60: /* PrimIdxName op arg */
2130 if (low
!= 2) goto failPrimitiveArgs
;
2131 op
= POPIT
; /* arg */
2132 op1
= POPIT
; /* opno */
2133 if (!LST_IS_SMALLINT(op1
)) goto failPrimitive
;
2134 tmp
= lstIntValue(op1
);
2136 case 0: /* index by name */
2137 if (op
->stclass
!= lstStringClass
&& op
->stclass
!= lstSymbolClass
) goto failPrimitive
;
2138 if (LST_SIZE(op
) > 126) {
2139 retValue
= lstNilObj
;
2141 lstGetString(sbuf
, 256, op
);
2142 int ix
= lstFindPrimitiveIdx(sbuf
);
2143 retValue
= ix
>=0 ? lstNewInt(ix
) : lstNilObj
;
2146 case 1: /* name by index */
2147 if (LST_IS_SMALLINT(op
)) tmp
= lstIntValue(op
);
2148 else if (op
== lstIntegerClass
) tmp
= lstLIntValue(op
);
2149 else goto failPrimitive
;
2151 const char *n
= lstFindPrimitiveName(tmp
);
2152 retValue
= n
? lstNewString(n
) : lstNilObj
;
2155 default: goto failPrimitive
;
2159 case 61: /* GetCurrentProcess */
2160 if (low
!= 0) goto failPrimitiveArgs
;
2161 retValue
= aProcess
;
2164 case 62: /* error trap / yield -- halt process; no args: error; else: suspend (yield) */
2165 if (low
> 1) goto failPrimitiveArgs
;
2169 stackTop
-= (low
-1); /* drop other args */
2170 tmp
= lstReturnYield
; /* no-error flag */
2173 retValue
= lstNilObj
;
2174 tmp
= lstReturnError
; /* error flag */
2176 int rr
= doReturn(tmp
);
2177 if (rr
) XRETURN(rr
);
2178 if (tmp
|| retGSwitch
) goto doAllAgain
;
2181 case 63: /* ExecuteNewProcessAndWait proc tics */
2182 if (low
!= 2) goto failPrimitiveArgs
;
2183 op1
= POPIT
; /* ticks */
2184 op
= POPIT
; /* new process */
2185 if (!LST_IS_SMALLINT(op1
)) goto failPrimitive
;
2186 if (!lstIsKindOf(op
, lstProcessClass
)) goto failPrimitive
;
2187 tmp
= lstIntValue(op1
);
2188 saveCurrentProcess();
2189 if (loadNewProcess(op
) == 0) {
2190 /* new process succesfully loaded */
2191 ticks
= tmp
<1 ? 0 : tmp
;
2192 lockCount
= lockCount
>0; /* start locked if locked */
2193 goto doAllAgain
; /* go on with the new process */
2195 reloadFromGroup(); /* restore old process */
2197 low
= lstReturnError
;
2198 execComplete
: /* low is the result */
2199 retValue
= lstNewInt(low
);
2202 case 64: /* LockUnlockSheduler */
2203 if (low
> 1) goto failPrimitiveArgs
;
2206 stackTop
-= (low
-1); /* drop other args */
2207 if (op
== lstFalseObj
) {
2209 if (--lockCount
< 0) {
2211 /*goto failPrimitive;*/
2218 /* query lock state */
2219 retValue
= lockCount
? lstTrueObj
: lstFalseObj
;
2221 case 65: /* TicksGetSet */
2222 if (low
> 1) goto failPrimitiveArgs
;
2225 stackTop
-= (low
-1); /* drop other args */
2226 if (LST_IS_SMALLINT(op
)) tmp
= lstIntValue(op
);
2227 else if (op
== lstIntegerClass
) tmp
= lstLIntValue(op
);
2228 else goto failPrimitive
;
2229 if (tmp
< 1) tmp
= 1;
2232 retValue
= LST_FITS_SMALLINT(ticks
) ? lstNewInt(ticks
) : lstNewLongInt(ticks
);
2234 case 66: /* RunGC */
2235 if (low
!= 0) goto failPrimitiveArgs
;
2237 retValue
= lstNilObj
;
2239 case 67: /* UserBreakSignal */
2240 if (low
!= 0) goto failPrimitiveArgs
;
2242 retValue
= lstNilObj
;
2244 case 68: /* EventHandlerCtl */
2248 if (low
!= 2) goto failPrimitiveArgs
;
2250 * <EventHandlerCtl eid true> -- suspend this process; wait for the event
2254 if (!LST_IS_SMALLINT(op
)) goto failPrimitive
;
2255 tmp
= lstIntValue(op
);
2256 if (tmp
< 1 || tmp
> 65535) goto failPrimitive
;
2257 if (op1
!= lstTrueObj
) goto failPrimitive
;
2258 /*dprintf("eventWaitFor: %d\n", tmp);*/
2259 addOneShotEventHandler(tmp
, curGroup
);
2260 curGroup
->ewait
= -tmp
; /* sheduler will save and skip this process */
2262 retValue
= lstTrueObj
;
2264 case 69: /* ProcessGroupCtl */
2266 * <ProcessGroupCtl 0 process [ticks]> -- create new process group
2268 if (low
< 2 || low
> 3) goto failPrimitiveArgs
;
2271 if (!LST_IS_SMALLINT(op
)) goto failPrimitiveArgs
;
2272 tmp
= lstIntValue(op
);
2273 if (tmp
< 1) tmp
= 10000;
2277 if (!LST_IS_SMALLINT(op1
)) goto failPrimitive
;
2278 if (lstIntValue(op1
) != 0) goto failPrimitive
;
2279 if (!lstIsKindOf(op
, lstProcessClass
)) goto failPrimitive
;
2280 if (op
->data
[lstIVrunningInProcess
] != lstNilObj
) goto failPrimitive
;
2282 saveCurrentProcess();
2283 /* create new process group */
2284 LstRunGroup
*ng
= calloc(1, sizeof(LstRunGroup
)); /*TODO: reuse free groups*/
2285 LstRunGroup
*pg
= curGroup
;
2289 if (loadNewProcess(op
) == 0) {
2290 /* new process succesfully loaded, insert group in list (after current) */
2291 /*fprintf(stderr, "OK!\n");*/
2292 saveCurrentProcess();
2294 ng
->next
= pg
->next
;
2296 if (ng
->next
) ng
->next
->prev
= ng
;
2298 /* remove this group */
2302 /* restore old process */
2305 if (!ng
) goto failPrimitive
;
2309 case 70: /* PrintObject */
2313 if (low
> 2) goto failPrimitiveArgs
;
2314 op1
= low
==2 ? POPIT
: lstNilObj
;
2316 if (LST_IS_SMALLINT(op
)) {
2317 tmp
= lstIntValue(op
);
2318 if (tmp
>= 0 && tmp
<= 255) fputc(tmp
, stdout
);
2319 } else if (LST_IS_BYTES(op
)) {
2320 fwrite(lstBytePtr(op
), LST_SIZE(op
), 1, stdout
);
2321 } else if (op
->stclass
== lstCharClass
) {
2323 if (!LST_IS_SMALLINT(op
)) goto failPrimitive
;
2324 tmp
= lstIntValue(op
);
2325 if (tmp
>= 0 && tmp
<= 255) fputc(tmp
, stdout
);
2326 } else goto failPrimitive
;
2327 if (op1
!= lstNilObj
) fputc('\n', stdout
);
2329 retValue
= lstNilObj
;
2331 case 71: /* ReadCharacter */
2332 if (low
!= 0) goto failPrimitiveArgs
;
2334 retValue
= tmp
==EOF
? lstNilObj
: lstNewInt((int)(((unsigned int)tmp
)&0xff));
2337 case 72: /* FloatBAIO opcode num */
2338 if (low
!= 2) goto failPrimitiveArgs
;
2339 op
= POPIT
; /* num */
2340 op1
= POPIT
; /* opcode */
2341 if (!LST_IS_SMALLINT(op1
)) goto failPrimitive
;
2342 tmp
= lstIntValue(op1
);
2343 if (tmp
< 0 || tmp
> 1) goto failPrimitive
;
2346 if (LST_CLASS(op
) != lstFloatClass
) goto failPrimitive
;
2348 retValue
= lstNewBinary(lstBytePtr(ptemp
), sizeof(LstFloat
));
2351 /* from byte array */
2353 if (LST_CLASS(op
) != lstByteArrayClass
) goto failPrimitive
;
2354 if (LST_SIZE(op
) != sizeof(n
)) goto failPrimitive
;
2355 memcpy(&n
, lstBytePtr(op
), sizeof(n
));
2356 retValue
= lstNewFloat(n
);
2359 case 73: /* IntegerBAIO opcode num */
2360 if (low
!= 2) goto failPrimitiveArgs
;
2361 op
= POPIT
; /* num */
2362 op1
= POPIT
; /* opcode */
2363 if (!LST_IS_SMALLINT(op1
)) goto failPrimitive
;
2364 tmp
= lstIntValue(op1
);
2365 if (tmp
< 0 || tmp
> 1) goto failPrimitive
;
2368 if (LST_CLASS(op
) != lstIntegerClass
) goto failPrimitive
;
2370 retValue
= lstNewBinary(lstBytePtr(ptemp
), sizeof(LstLInt
));
2373 /* from byte array */
2375 if (LST_CLASS(op
) != lstByteArrayClass
) goto failPrimitive
;
2376 if (LST_SIZE(op
) != sizeof(n
)) goto failPrimitive
;
2377 memcpy(&n
, lstBytePtr(op
), sizeof(n
));
2378 retValue
= lstNewLongInt(n
);
2382 case 74: /* ExecuteContext ctx */
2383 if (low
!= 1) goto failPrimitiveArgs
;
2384 op
= POPIT
; /* ctx */
2385 if (LST_CLASS(op
) != lstContextClass
&& !lstIsKindOf(op
, lstContextClass
)) goto failPrimitive
;
2386 op
->data
[lstIVpreviousContextInContext
] = context
->data
[lstIVpreviousContextInContext
];
2391 case 75: /* StFinalizeCtl obj add-remove-flag */
2392 if (low
!= 2) goto failPrimitiveArgs
;
2393 op1
= POPIT
; /* flag */
2394 op
= POPIT
; /* object */
2395 if (LST_IS_SMALLINT(op
)) goto failPrimitive
; /* SmallInt can't have finalizer */
2396 if (op1
== lstNilObj
|| op1
== lstFalseObj
) {
2397 /* remove from list */
2398 if (LST_IS_STFIN(op
)) {
2399 LST_RESET_STFIN(op
);
2400 lstRemoveFromFList(&stFinListHead
, op
->fin
);
2405 if (!LST_IS_STFIN(op
)) {
2406 if (op
->fin
) goto failPrimitive
; /* object can have either C or ST finalizer, but not both */
2407 op
->fin
= calloc(1, sizeof(LstFinLink
));
2408 if (!op
->fin
) lstFatal("out of memory is StFinalizeCtl", 0x29a);
2410 op
->fin
->obj
= op
; /* owner */
2411 lstAddToFList(&stFinListHead
, op
->fin
);
2414 retValue
= lstNilObj
;
2417 case 76: /* StWeakCtl obj */
2418 if (low
!= 1) goto failPrimitiveArgs
;
2419 op
= POPIT
; /* object */
2420 if (LST_IS_SMALLINT(op
)) goto failPrimitive
; /* SmallInt can't have finalizer */
2422 if (!LST_IS_WEAK(op
)) {
2423 if (op
->fin
) goto failPrimitive
; /* object can have either C or ST finalizer, or marked as weak, but not all */
2424 op
->fin
= calloc(1, sizeof(LstFinLink
));
2425 if (!op
->fin
) lstFatal("out of memory is StWeakCtl", 0x29a);
2427 op
->fin
->obj
= op
; /* owner */
2428 lstAddToFList(&stWeakListHead
, op
->fin
);
2430 retValue
= lstNilObj
;
2433 case 77: /* FloatFunc float idx */
2434 if (low
!= 2) goto failPrimitiveArgs
;
2435 op1
= POPIT
; /* idx */
2436 op
= POPIT
; /* float */
2437 if (!LST_IS_SMALLINT(op1
)) goto failPrimitive
;
2438 tmp
= lstIntValue(op1
);
2439 if (LST_IS_SMALLINT(op
)) fop0
= lstIntValue(op
);
2440 else if (op
->stclass
== lstIntegerClass
) fop0
= lstLIntValue(op
);
2441 else if (op
->stclass
== lstFloatClass
) fop0
= lstFloatValue(op
);
2442 else goto failPrimitive
;
2444 case 0: fop0
= log2(fop0
); break;
2445 default: goto failPrimitive
;
2447 retValue
= lstNewFloat(fop0
);
2450 case 78: /* LastFailedPrim */
2452 retValue
= lstNewInt(lastFailedPrim
);
2455 case 79: {/* FNVHash byteobj */
2457 if (low
!= 1) goto failPrimitiveArgs
;
2458 op
= POPIT
; /* obj */
2459 if (!LST_IS_BYTES_EX(op
)) goto failPrimitive
;
2460 h
= fnvHash(lstBytePtr(op
), LST_SIZE(op
));
2461 tmp
= (h
%(INT_MAX
/2));
2462 retValue
= lstNewInt(tmp
);
2466 /* save stack pointers */
2471 lstPrimCtx
= context
;
2472 saveCurrentProcess();
2474 resetEvtCheckLeft
= 0;
2475 LSTPrimitiveFn pfn
= lstFindExtPrimitiveFn(high
);
2476 retValue
= pfn
? pfn(high
, &(stack
->data
[stackTop
-low
]), low
) : NULL
;
2477 if (resetEvtCheckLeft
) { evtCheckLeft
= 1; }
2479 stackTop
-= low
; /* remove primitive args */
2480 /* restore stacks */
2481 if (lstRootTop
< l0
) lstFatal("root stack error in primitive", high
);
2482 if (lstTempSP
< l1
) lstFatal("temp stack error in primitive", high
);
2485 if (!retValue
) goto failPrimitive
;
2488 /* force a stack return due to successful primitive */
2494 lastFailedPrim
= lastCalledPrim
;
2495 /* supply a return value for the failed primitive */
2498 /* done with primitive, continue execution loop */
2502 case lstBCDoSpecial
:
2504 case lstBXSelfReturn
:
2505 DBG0("DoSpecial: SelfReturn");
2506 retValue
= arguments
->data
[lstIVreceiverInArguments
];
2508 case lstBXStackReturn
:
2509 DBG0("DoSpecial: StackReturn");
2511 doReturn
: /*context->data[lstIVbytePointerInContext] = lstNilObj;*/
2512 context
= context
->data
[lstIVpreviousContextInContext
];
2513 doReturn2
: if (context
== lstNilObj
) {
2514 /*aProcess->data[lstIVcontextInProcess] = lstNilObj;*/ /* 'complete' flag */
2515 int rr
= doReturn(lstReturnReturned
);
2516 if (rr
) XRETURN(rr
);
2517 if (tmp
|| retGSwitch
) goto doAllAgain
;
2520 doReturn3
: aProcess
->data
[lstIVcontextInProcess
] = context
;
2524 case lstBXBlockReturn
:
2525 DBG0("DoSpecial: BlockReturn");
2526 /* the very bad thing is that this can be inter-group return */
2528 /*context->data[lstIVbytePointerInContext] = lstNilObj;*/
2529 /*dprintf("cp=%p\n", aProcess);*/
2530 context
= context
->data
[lstIVcreatingContextInBlock
]->data
[lstIVpreviousContextInContext
];
2531 if (context
== lstNilObj
) {
2532 if (curGroup
->group
->prev
) {
2533 /* not the last process */
2536 /* return from the process of the group */
2537 /* if this is return from the main group, we have to return from executor */
2538 if (curGroup
== runGroups
) {
2539 aProcess
= runGroups
->group
->process
; /* initial process */
2540 aProcess
->data
[lstIVresultInProcess
] = retValue
;
2541 aProcess
->data
[lstIVcontextInProcess
] = lstNilObj
;
2542 /* clear the current run group */
2543 while (curGroup
->group
) releaseRunContext();
2544 XRETURN(lstReturnReturned
); /* done */
2546 /* just kill the current run group */
2547 while (curGroup
->group
) releaseRunContext();
2548 free(removeCurrentGroup());
2550 /* the current group is dead, go on with the next */
2554 /* check if we should do unwinding and possibly group switching */
2555 if (context
->data
[lstIVprocOwnerInContext
] != aProcess
) {
2556 /* yes, this is inter-process return; do unwinding */
2557 op
= context
->data
[lstIVprocOwnerInContext
];
2558 dprintf(" ct=%p\n", context
);
2559 dprintf(" op=%p\n", op
);
2560 dprintf(" nl=%p\n", lstNilObj
);
2561 /* first try our own process group */
2562 if (groupHasProcess(curGroup
, op
)) {
2563 /* unwinding in current process group */
2564 while (curGroup
->group
->process
!= op
) releaseRunContext();
2567 /* not in the current group; this means that the current group is effectively dead */
2568 /* remove current group */
2569 if (curGroup
== runGroups
) {
2571 while (curGroup
->group
->prev
) releaseRunContext();
2572 aProcess
= runGroups
->group
->process
; /* initial process */
2573 aProcess
->data
[lstIVresultInProcess
] = retValue
;
2574 aProcess
->data
[lstIVcontextInProcess
] = lstNilObj
;
2575 /* clear the current run group */
2576 while (curGroup
->group
) releaseRunContext();
2577 XRETURN(lstReturnReturned
); /* done */
2579 while (curGroup
->group
) releaseRunContext();
2580 free(removeCurrentGroup());
2581 /* inter-group communications should be done with events, so just shedule to the next process */
2586 case lstBXDuplicate
:
2587 DBG0("DoSpecial: Duplicate");
2588 assert(stackTop
> 0);
2589 retValue
= stack
->data
[stackTop
-1];
2593 DBG0("DoSpecial: PopTop");
2594 assert(stackTop
> 0);
2598 DBG0("DoSpecial: Branch");
2602 case lstBXBranchIfTrue
:
2603 DBG0("DoSpecial: BranchIfTrue");
2606 if (retValue
== lstTrueObj
) curIP
= low
; else curIP
+= VALSIZE
;
2608 case lstBXBranchIfFalse
:
2609 DBG0("DoSpecial: BranchIfFalse");
2612 if (retValue
== lstFalseObj
) curIP
= low
; else curIP
+= VALSIZE
;
2614 case lstBXBranchIfNil
:
2615 DBG0("DoSpecial: BranchIfNil");
2618 if (retValue
== lstNilObj
) curIP
= low
; else curIP
+= VALSIZE
;
2620 case lstBXBranchIfNotNil
:
2621 DBG0("DoSpecial: BranchIfNotNil");
2624 if (retValue
!= lstNilObj
) curIP
= low
; else curIP
+= VALSIZE
;
2626 case lstBXSendToSuper
:
2627 DBG0("DoSpecial: SendToSuper");
2628 /* next byte has literal selector number */
2630 messageSelector
= literals
->data
[low
];
2631 receiverClass
= method
->data
[lstIVclassInMethod
]->data
[lstIVparentClassInClass
];
2635 case lstBXThisContext
:
2636 DBG0("DoSpecial: ThisContext");
2639 case lstBXBreakpoint
:
2640 DBG0("DoSpecial: Breakpoint");
2641 /*fprintf(stderr, "BP\n");*/
2642 /* back up on top of the breaking location */
2644 /* return to our master process */
2645 /*aProcess->data[lstIVresultInProcess] = lstNilObj;*/
2646 retValue
= lstNilObj
;
2647 if (doReturn(lstReturnBreak
)) XRETURN(lstReturnBreak
);
2648 if (tmp
|| retGSwitch
) goto doAllAgain
;
2651 lstFatal("invalid doSpecial", low
);
2656 if (curGroup
== runGroups
) {
2657 retValue
= lstNilObj
;
2658 if (doReturn(lstReturnError
)) XRETURN(lstReturnError
);
2659 fprintf(stderr
, "invalid bytecode: %d\n", high
);
2660 if (tmp
|| retGSwitch
) goto doAllAgain
;
2663 lstFatal("invalid bytecode", high
);
2670 int lstExecute (lstObject
*aProcess
, int ticks
, int locked
) {
2672 return lstExecuteInternal(aProcess
, ticks
, locked
);
2676 int lstResume (void) {
2677 if (!lstSuspended
) return -1; /* very fatal error */
2678 return lstExecuteInternal(NULL
, 0, 0);
2682 int lstCanResume (void) {
2683 return lstSuspended
!= 0;
2687 void lstResetResume (void) {
2690 curGroup
= runGroups
;
2691 while (curGroup
->group
) releaseRunContext();
2696 #define RARG (lstRootStack[otop+0])
2697 #define RMETHOD (lstRootStack[otop+1])
2698 #define RPROCESS (lstRootStack[otop+2])
2699 #define RCONTEXT (lstRootStack[otop+3])
2700 int lstRunMethodWithArg (lstObject
*method
, lstObject
*inClass
, lstObject
*arg
, lstObject
**result
, int locked
) {
2702 int otop
= lstRootTop
, x
;
2703 if (result
) *result
= NULL
;
2704 /* save method and arguments */
2705 if (!method
|| method
->stclass
!= lstMethodClass
) return lstReturnError
;
2706 lstRootStack
[LST_RSTACK_NSP()] = arg
;
2707 lstRootStack
[LST_RSTACK_NSP()] = method
;
2708 /* create Process object */
2709 lstRootStack
[LST_RSTACK_NSP()] = lstAllocInstance(lstProcessSize
, lstProcessClass
); /*lstStaticAlloc(lstProcessSize);*/
2710 /* create Context object (must be dynamic) */
2711 lstRootStack
[LST_RSTACK_NSP()] = lstAllocInstance(lstContextSize
, lstContextClass
);
2712 RPROCESS
->data
[lstIVcontextInProcess
] = RCONTEXT
;
2713 x
= lstIntValue(RMETHOD
->data
[lstIVstackSizeInMethod
]);
2714 o
= lstRootStack
[LST_RSTACK_NSP()] = RCONTEXT
->data
[lstIVstackInContext
] = lstAllocInstance(x
, lstArrayClass
);
2715 /*if (x) memset(lstBytePtr(o), 0, x*LST_BYTES_PER_WORD);*/
2716 /* build arguments array */
2717 o
= lstAllocInstance(arg
? 2 : 1, lstArrayClass
);
2718 /*o->data[0] = RCONTEXT;*/
2719 o
->data
[0] = inClass
? inClass
: lstNilObj
->stclass
;
2720 if (arg
) o
->data
[1] = arg
;
2721 RCONTEXT
->data
[lstIVprocOwnerInContext
] = RPROCESS
;
2722 RCONTEXT
->data
[lstIVargumentsInContext
] = o
;
2723 RCONTEXT
->data
[lstIVtemporariesInContext
] = lstAllocInstance(lstIntValue(RMETHOD
->data
[lstIVtemporarySizeInMethod
]), lstArrayClass
);
2724 RCONTEXT
->data
[lstIVbytePointerInContext
] = lstNewInt(0);
2725 RCONTEXT
->data
[lstIVstackTopInContext
] = lstNewInt(0);
2726 RCONTEXT
->data
[lstIVpreviousContextInContext
] = lstNilObj
;
2727 RCONTEXT
->data
[lstIVmethodInContext
] = RMETHOD
;
2729 int res
= lstExecute(RPROCESS
, 0, locked
>0);
2730 if (res
== lstReturnReturned
&& result
) *result
= RPROCESS
->data
[lstIVresultInProcess
];
2731 /*printf("OTOP: %d; TOP: %d\n", otop, lstRootTop);*/
2733 case lstReturnBadMethod
:
2734 fprintf(stderr
, "can't find method in call\n");
2735 o
= RPROCESS
->data
[lstIVresultInProcess
];
2736 fprintf(stderr
, "Unknown method: %s\n", lstBytePtr(o
));
2737 lstBackTrace(RPROCESS
->data
[lstIVcontextInProcess
]);
2739 case lstReturnAPISuspended
:
2740 if (lstExecUserBreak
!= 666) {
2741 fprintf(stderr
, "\nuser break\n");
2742 o
= RPROCESS
->data
[lstIVresultInProcess
];
2743 lstBackTrace(RPROCESS
->data
[lstIVcontextInProcess
]);
2747 if (lstRootTop
> otop
) lstRootTop
= otop
;
2752 void lstCompleteFinalizers (void) {
2755 if (finGroupCount
< 1) break;
2757 dprintf("%d finalizers left\n", finGroupCount
);
2758 lstExecuteInternal(NULL
, 10000, 0);