2 * Little Smalltalk, Version 5
4 * Copyright (C) 1987-2005 by Timothy A. Budd
5 * Copyright (C) 2007 by Charles R. Childers
6 * Copyright (C) 2005-2007 by Danny Reinhold
7 * Copyright (C) 2010 by Ketmar // Vampire Avalon
9 * ============================================================================
10 * This license applies to the virtual machine and to the initial image of
11 * the Little Smalltalk system and to all files in the Little Smalltalk
12 * packages except the files explicitly licensed with another license(s).
13 * ============================================================================
14 * Permission is hereby granted, free of charge, to any person obtaining a copy
15 * of this software and associated documentation files (the "Software"), to deal
16 * in the Software without restriction, including without limitation the rights
17 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18 * copies of the Software, and to permit persons to whom the Software is
19 * furnished to do so, subject to the following conditions:
21 * The above copyright notice and this permission notice shall be included in
22 * all copies or substantial portions of the Software.
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30 * DEALINGS IN THE SOFTWARE.
32 typedef struct EPInfo
{
38 static const EPInfo clInfo
[] = {
39 {"Boolean", &lstBooleanClass
},
40 {"SmallInt", &lstSmallIntClass
},
41 {"Integer", &lstIntegerClass
},
42 {"Char", &lstCharClass
},
43 {"Float", &lstFloatClass
},
44 {"Array", &lstArrayClass
},
45 {"Block", &lstBlockClass
},
46 {"Context", &lstContextClass
},
47 {"Process", &lstProcessClass
},
48 {"String", &lstStringClass
},
49 {"Symbol", &lstSymbolClass
},
50 {"ByteArray", &lstByteArrayClass
},
51 {"ByteCode", &lstByteCodeClass
},
52 {"Method", &lstMethodClass
},
53 {"Number", &lstNumberClass
},
57 static const EPInfo epInfo
[] = {
58 {"initialize", &lstInitMethod
},
59 {"loadFile:", &lstLoadMethod
},
60 {"doString:", &lstDoStrMethod
},
61 {"runREPL", &lstReplMethod
},
62 {"newSymbol:", &lstNewSymMethod
},
63 {"setGlobal:", &lstSetGlobMethod
},
73 static const LSTPrimDef lstPrimList
[] = {
77 { 3, "ObjectIdentity"}, /* are two args represents the same object? (address compare) */
81 { 7, "Array#at:put:"},
83 { 9, "String#at:put:"},
84 { 10, "String#clone:"},
85 { 11, "String#Position:from:"},
86 { 12, "String#LastPosition:from:"},
87 { 13, "String#CopyFromTo"},
89 { 14, "BulkObjectExchange"}, /*???*/
90 { 15, "replaceFrom:to:with:startingAt:"},
92 { 16, "BlockInvocation"},
94 { 17, "FlushMethodCache"},
96 { 18, "SmallIntToInteger"},
97 { 19, "NumberToFloat"},
98 { 20, "FloatToInteger"},
99 { 21, "IntegerToSmallInt"},
100 { 22, "IntegerToSmallIntTrunc"},
106 { 26, "SmallIntAdd"},
107 { 27, "SmallIntSub"},
108 { 28, "SmallIntMul"},
109 { 29, "SmallIntDiv"},
110 { 30, "SmallIntMod"},
111 { 31, "SmallIntLess"},
112 { 32, "SmallIntLessEqu"},
113 { 33, "SmallIntGreat"},
114 { 34, "SmallIntGreatEqu"},
115 { 35, "SmallIntEqu"},
116 { 36, "SmallIntNotEqu"},
123 { 42, "IntegerLess"},
124 { 43, "IntegerLessEqu"},
125 { 44, "IntegerGreat"},
126 { 45, "IntegerGreatEqu"},
128 { 47, "IntegerNotEqu"},
135 { 53, "FloatLessEqu"},
137 { 55, "FloatGreatEqu"},
139 { 57, "FloatNotEqu"},
141 { 58, "FloatToString"},
142 { 59, "FloatNegate"},
144 { 60, "PrimIdxName"},
146 { 61, "GetCurrentProcess"},
147 { 62, "ErrorOrYield"},
148 { 63, "ExecuteNewProcessAndWait"},
150 { 64, "LockUnlockSheduler"},
151 { 65, "TicksGetSet"},
153 { 67, "UserBreakSignal" },
154 { 68, "EventHandlerCtl" },
155 { 69, "ProcessGroupCtl" },
157 { 70, "PrintObject"},
158 { 71, "ReadCharacter"}, /* returns nil or int */
161 { 73, "IntegerBAIO"},
163 { 74, "ExecuteContext"},
165 { 75, "StFinalizeCtl"},
172 #define LST_FIRST_UNUSED_PRIM 80
175 static const LSTPrimDef
*lstPDefByIdx (int idx
) {
176 const LSTPrimDef
*pd
;
177 for (pd
= lstPrimList
; pd
->name
; pd
++) if (pd
->idx
== idx
) return pd
;
182 static const LSTPrimDef
*lstPDefByName (const char *name
) {
183 const LSTPrimDef
*pd
;
184 for (pd
= lstPrimList
; pd
->name
; pd
++) if (!strcmp(pd
->name
, name
)) return pd
;
189 static int lstPDGetInstr (const void *bcode
, int pc
, int *op
, int *arg
) {
190 const uint8_t *bc
= (const uint8_t *)bcode
;
192 l
= (h
= bc
[pc
++])&0x0f;
205 static int lstPDGetInstrSize (const void *bcode
, int pc
) {
207 const uint8_t *bc
= (const uint8_t *)bcode
;
209 low
= (high
= bc
[pc
++])&0x0f;
210 high
= (high
>>4)&0x0f;
211 if (high
== 0) { high
= low
; low
= bc
[pc
++]; }
218 case lstBCDoPrimitive
:
219 /* low is argument count; next byte is primitive number */
225 case lstBXBranchIfTrue
:
226 case lstBXBranchIfFalse
:
227 case lstBXBranchIfNil
:
228 case lstBXBranchIfNotNil
:
231 case lstBXSendToSuper
:
232 /* next byte has literal selector number */
242 #define GETVAL(pc) (bc[pc] | (bc[(pc)+1] << 8))
243 static __attribute((unused
)) int lstPDGetHighestJump (const void *bcode
, int bcsize
) {
244 const uint8_t *bc
= (const uint8_t *)bcode
;
245 int zjmp
= -1, pc
= 0, jdst
;
246 while (pc
< bcsize
) {
248 low
= (high
= bc
[pc
++])&0x0f;
249 high
= (high
>>4)&0x0f;
250 if (high
== 0) { high
= low
; low
= bc
[pc
++]; }
258 case lstBCDoPrimitive
:
259 /* low is argument count; next byte is primitive number */
265 case lstBXBranchIfTrue
:
266 case lstBXBranchIfFalse
:
267 case lstBXBranchIfNil
:
268 case lstBXBranchIfNotNil
:
272 case lstBXSendToSuper
:
273 /* next byte has literal selector number */
279 if (jdst
> zjmp
) zjmp
= jdst
;
285 static void lstWritePrimPatches (FILE *fp
, lstByteObject
*bobj
) {
290 int size
= LST_SIZE(bobj
);
291 memset(primPatch
, 0, sizeof(primPatch
));
292 int pc
= 0, primRC
= 0;
295 int apc
= lstPDGetInstr(bobj
->bytes
, pc
, &op
, &arg
);
296 if (op
== lstBCDoPrimitive
) {
297 int idx
= bobj
->bytes
[apc
];
298 if (lstFindPrimitiveName(idx
)) {
299 if (primPatch
[idx
].pccount
== 0) primRC
++;
300 if (primPatch
[idx
].pccount
>= 255) lstFatal("too many primitive calls in bytecode", (intptr_t)bobj
);
301 primPatch
[idx
].pc
[primPatch
[idx
].pccount
++] = apc
;
303 lstFatal("unknown primitive in image writer", idx
);
306 int len
= lstPDGetInstrSize(bobj
->bytes
, pc
);
307 if (len
< 1) lstFatal("internal error in bytecode tracer", (intptr_t)bobj
);
310 lstImgWriteWord(fp
, primRC
);
313 for (f
= 0; f
<= 255; f
++) {
314 if (primPatch
[f
].pccount
) {
315 const char *pname
= lstFindPrimitiveName(f
);
317 uint8_t len
= strlen(pname
);
318 if (fwrite(&len
, 1, 1, fp
) != 1) lstFatal("can't write str8", (intptr_t)bobj
);
320 if (fwrite(pname
, len
, 1, fp
) != 1) lstFatal("can't write str8", (intptr_t)bobj
);
322 lstImgWriteWord(fp
, primPatch
[f
].pccount
);
323 for (c
= 0; c
< primPatch
[f
].pccount
; c
++) lstImgWriteWord(fp
, primPatch
[f
].pc
[c
]);
334 LSTPrimitiveClearFn cfn
;
338 static LSTExtPrimDef lstExtPrimList
[256];
339 static int lstExtPrimFree
= LST_FIRST_UNUSED_PRIM
;
342 int lstFindExtPrimitiveByName (const char *name
) {
344 for (f
= LST_FIRST_UNUSED_PRIM
; f
< lstExtPrimFree
; ++f
) {
345 if (lstExtPrimList
[f
].name
&& !strcmp(lstExtPrimList
[f
].name
, name
)) return f
;
351 int lstFindPrimitiveIdx (const char *name
) {
352 const LSTPrimDef
*pd
= lstPDefByName(name
);
353 if (pd
) return pd
->idx
;
354 return lstFindExtPrimitiveByName(name
);
358 LSTPrimitiveFn
lstFindExtPrimitiveFn (int idx
) {
359 if (idx
< LST_FIRST_UNUSED_PRIM
|| idx
>= lstExtPrimFree
) return NULL
;
360 if (!lstExtPrimList
[idx
].name
) return NULL
;
361 return lstExtPrimList
[idx
].pfn
;
365 const char *lstFindPrimitiveName (int idx
) {
366 if (idx
< 0 || idx
>= lstExtPrimFree
) return NULL
;
367 if (idx
< LST_FIRST_UNUSED_PRIM
) {
368 const LSTPrimDef
*pd
= lstPDefByIdx(idx
);
369 return pd
? pd
->name
: NULL
;
371 return lstExtPrimList
[idx
].name
;
375 int lstRegisterExtPrimitive (const char *name
, LSTPrimitiveFn pfn
, LSTPrimitiveClearFn cfn
) {
376 /*fprintf(stderr, "registering: [%s]\n", name);*/
377 if (!name
|| !name
[0] || strlen(name
) > 255) return -1; /* invalid name */
378 int idx
= lstFindExtPrimitiveByName(name
);
380 if (lstExtPrimFree
> 255) return -1; /* out of space for primitives */
381 idx
= lstExtPrimFree
++;
382 lstExtPrimList
[idx
].name
= strdup(name
);
384 lstExtPrimList
[idx
].pfn
= pfn
;
385 lstExtPrimList
[idx
].cfn
= cfn
;
390 void lstClearExtPrimitives (void) {
392 for (f
= LST_FIRST_UNUSED_PRIM
; f
< lstExtPrimFree
; ++f
) {
393 if (lstExtPrimList
[f
].name
) {
394 if (lstExtPrimList
[f
].cfn
) lstExtPrimList
[f
].cfn(f
);
395 free(lstExtPrimList
[f
].name
);
396 lstExtPrimList
[f
].name
= NULL
;
399 lstExtPrimFree
= LST_FIRST_UNUSED_PRIM
;
403 void lstPrimitivesClear (void) {
404 lstClearExtPrimitives();
408 int lstRegisterExtPrimitiveTable (const LSTExtPrimitiveTable
*tbl
) {
410 int px
= lstRegisterExtPrimitive(tbl
->name
, tbl
->pfn
, tbl
->cfn
);
411 if (px
< 0) return 0;