-
Notifications
You must be signed in to change notification settings - Fork 23
/
Copy pathStreamPrim.cpp
344 lines (261 loc) · 12.7 KB
/
StreamPrim.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
/******************************************************************************
File: StreamPrim.cpp
Description:
Implementation of the Interpreter class' Stream primitives
******************************************************************************/
#include "Ist.h"
#pragma code_seg(PRIM_SEG)
#include "ObjMem.h"
#include "Interprt.h"
#include "InterprtPrim.inl"
// Smalltalk classes
#include "STBehavior.h"
#include "STStream.h"
#include "STArray.h"
#include "STByteArray.h"
#include "STString.h"
#include "STInteger.h"
#include "STCharacter.h"
// This primitive handles PositionableStream>>next, but only for Arrays, Strings and ByteArrays
// Unary message, so does not modify stack pointer, and is therefore called directly from the ASM
// primitive table without indirection through an ASM thunk.
BOOL __fastcall Interpreter::primitiveNext()
{
PosStreamOTE* streamPointer = reinterpret_cast<PosStreamOTE*>(stackTop()); // Access receiver
// Only works for subclasses of PositionableStream (or look alikes)
//ASSERT(!ObjectMemoryIsIntegerObject(streamPointer) && ObjectMemory::isKindOf(streamPointer, Pointers.ClassPositionableStream));
PositionableStream* readStream = streamPointer->m_location;
// Ensure valid stream - unusually this validity check is included in the Blue Book spec
// and appears to be implemented in most Smalltalks, so we implement here too.
if (!ObjectMemoryIsIntegerObject(readStream->m_index) ||
!ObjectMemoryIsIntegerObject(readStream->m_readLimit))
return primitiveFailure(0); // Receiver fails invariant check
SMALLINTEGER index = ObjectMemoryIntegerValueOf(readStream->m_index);
SMALLINTEGER limit = ObjectMemoryIntegerValueOf(readStream->m_readLimit);
// Is the current index within the limits of the collection?
// Remember that the index is 1 based (it's a Smalltalk index), and we're 0 based,
// so we don't need to increment it until after we've got the next object
if (index < 0 || index >= limit)
return primitiveFailure(2); // No, fail it
OTE* oteBuf = readStream->m_array;
BehaviorOTE* bufClass = oteBuf->m_oteClass;
if (bufClass == Pointers.ClassString)
{
StringOTE* oteString = reinterpret_cast<StringOTE*>(oteBuf);
// A sanity check - ensure within bounds of object too (again in Blue Book spec)
if (MWORD(index) >= oteString->bytesSize())
return primitiveFailure(3);
String* buf = oteString->m_location;
stackTop() = reinterpret_cast<Oop>(Character::New(buf->m_characters[index]));
}
// We also support ByteArrays in our primitiveNext (unlike BB).
else if (bufClass == Pointers.ClassByteArray)
{
ByteArrayOTE* oteBytes = reinterpret_cast<ByteArrayOTE*>(oteBuf);
if (MWORD(index) >= oteBytes->bytesSize())
return primitiveFailure(3);
ByteArray* buf = oteBytes->m_location;
stackTop() = ObjectMemoryIntegerObjectOf(buf->m_elements[index]);
}
else if (bufClass == Pointers.ClassArray)
{
ArrayOTE* oteArray = reinterpret_cast<ArrayOTE*>(oteBuf);
if (MWORD(index) >= oteArray->pointersSize())
return primitiveFailure(3);
Array* buf = oteArray->m_location;
stackTop() = buf->m_elements[index];
}
else
return primitiveFailure(1); // Collection cannot be handled by primitive, rely on Smalltalk code
// When incrementing the index we must allow for it overflowing a SmallInteger, even though
// this is extremely unlikely in practice
readStream->m_index = Integer::NewSigned32WithRef(index+1);
return primitiveSuccess(); // Succeed
}
// This primitive handles WriteStream>>NextPut:, but only for Arrays, Strings & ByteArrays
// Uses but does not modify stack pointer, instead returns the number of bytes to
// pop from the Smalltalk stack.
BOOL __fastcall Interpreter::primitiveNextPut()
{
Oop* sp = m_registers.m_stackPointer;
WriteStreamOTE* streamPointer = reinterpret_cast<WriteStreamOTE*>(*(sp-1)); // Access receiver under argument
//ASSERT(!ObjectMemoryIsIntegerObject(streamPointer) && ObjectMemory::isKindOf(streamPointer, Pointers.ClassPositionableStream));
WriteStream* writeStream = streamPointer->m_location;
// Ensure valid stream - checks from Blue Book
if (!ObjectMemoryIsIntegerObject(writeStream->m_index) ||
!ObjectMemoryIsIntegerObject(writeStream->m_writeLimit))
return primitiveFailure(0); // Fails invariant check
SMALLINTEGER index = ObjectMemoryIntegerValueOf(writeStream->m_index);
SMALLINTEGER limit = ObjectMemoryIntegerValueOf(writeStream->m_writeLimit);
// Within the bounds of the limit
if (index < 0 || index >= limit)
return primitiveFailure(2);
Oop value = *(sp);
OTE* oteBuf = writeStream->m_array;
BehaviorOTE* bufClass = oteBuf->m_oteClass;
if (bufClass == Pointers.ClassString)
{
if (ObjectMemory::fetchClassOf(value) != Pointers.ClassCharacter)
return primitiveFailure(4); // Attempt to put non-character
StringOTE* oteString = reinterpret_cast<StringOTE*>(oteBuf);
if (index >= oteString->bytesSizeForUpdate())
return primitiveFailure(3); // Attempt to put non-character or off end of String
String* buf = oteString->m_location;
CharOTE* oteChar = reinterpret_cast<CharOTE*>(value);
buf->m_characters[index] = static_cast<char>(oteChar->getIndex() - ObjectMemory::FirstCharacterIdx);
}
else if (bufClass == Pointers.ClassArray)
{
ArrayOTE* oteArray = reinterpret_cast<ArrayOTE*>(oteBuf);
// In bounds of Array?
if (index >= oteArray->pointersSizeForUpdate())
return primitiveFailure(3);
Array* buf = oteArray->m_location;
// We must ref. count value here as we're storing into a heap object slot
ObjectMemory::storePointerWithValue(buf->m_elements[index], value);
}
else if (bufClass == Pointers.ClassByteArray)
{
if (!ObjectMemoryIsIntegerObject(value))
return primitiveFailure(4); // Attempt to put non-SmallInteger
SMALLINTEGER intValue = ObjectMemoryIntegerValueOf(value);
if (intValue < 0 || intValue > 255)
return primitiveFailure(4); // Can only store 0..255
ByteArrayOTE* oteByteArray = reinterpret_cast<ByteArrayOTE*>(oteBuf);
if (index >= oteByteArray->bytesSizeForUpdate())
return primitiveFailure(3); // Attempt to put non-character or off end of String
oteByteArray->m_location->m_elements[index] = static_cast<BYTE>(intValue);
}
else
return primitiveFailure(1);
writeStream->m_index = Integer::NewSigned32WithRef(index + 1); // Increment the stream index
// As we no longer pop stack here, the receiver is still under the argument
*(sp-1) = value;
return sizeof(Oop); // Pop 4 bytes
}
// Non-standard, but has very beneficial effect on performance
BOOL __fastcall Interpreter::primitiveNextPutAll()
{
Oop* sp = m_registers.m_stackPointer;
WriteStreamOTE* streamPointer = reinterpret_cast<WriteStreamOTE*>(*(sp-1)); // Access receiver under argument
WriteStream* writeStream = streamPointer->m_location;
// Ensure valid stream - checks from Blue Book
if (!ObjectMemoryIsIntegerObject(writeStream->m_index) ||
!ObjectMemoryIsIntegerObject(writeStream->m_writeLimit))
return primitiveFailure(0); // Fails invariant check
SMALLINTEGER index = ObjectMemoryIntegerValueOf(writeStream->m_index);
SMALLINTEGER limit = ObjectMemoryIntegerValueOf(writeStream->m_writeLimit);
if (index < 0)
return primitiveFailure(2);
Oop value = *(sp);
OTE* oteBuf = writeStream->m_array;
BehaviorOTE* bufClass = oteBuf->m_oteClass;
MWORD newIndex;
if (bufClass == Pointers.ClassString)
{
BehaviorOTE* oteClass = ObjectMemory::fetchClassOf(value);
if (oteClass != Pointers.ClassString && oteClass != Pointers.ClassSymbol)
return primitiveFailure(4); // Attempt to put non-string
StringOTE* oteString = reinterpret_cast<StringOTE*>(value);
String* str = oteString->m_location;
MWORD valueSize = oteString->bytesSize();
newIndex = MWORD(index)+valueSize;
if (newIndex >= static_cast<MWORD>(limit)) // Beyond write limit
return primitiveFailure(2);
if (static_cast<int>(newIndex) >= oteBuf->bytesSizeForUpdate())
return primitiveFailure(3); // Attempt to write off end of buffer
String* buf = static_cast<String*>(oteBuf->m_location);
memcpy(buf->m_characters+index, str->m_characters, valueSize);
}
else if (bufClass == Pointers.ClassByteArray)
{
if (ObjectMemory::fetchClassOf(value) != bufClass)
return primitiveFailure(4); // Attempt to put non-ByteArray
ByteArrayOTE* oteBytes = reinterpret_cast<ByteArrayOTE*>(value);
ByteArray* bytes = oteBytes->m_location;
MWORD valueSize = oteBytes->bytesSize();
newIndex = MWORD(index)+valueSize;
if (newIndex >= (MWORD)limit) // Beyond write limit
return primitiveFailure(2);
if (static_cast<int>(newIndex) >= oteBuf->bytesSizeForUpdate())
return primitiveFailure(3); // Attempt to write off end of buffer
ByteArray* buf = static_cast<ByteArray*>(oteBuf->m_location);
memcpy(buf->m_elements+index, bytes->m_elements, valueSize);
}
else if (bufClass == Pointers.ClassArray)
{
if (ObjectMemory::fetchClassOf(value) != Pointers.ClassArray)
return primitiveFailure(4); // Attempt to put non-Array
// Not yet implemented
return primitiveFailure(1);
}
else
return primitiveFailure(1);
writeStream->m_index = Integer::NewUnsigned32WithRef(newIndex); // Increment the stream index
// As we no longer pop stack here, the receiver is still under the argument
*(sp-1) = value;
return sizeof(Oop); // Pop 4 bytes
}
// The primitive handles PositionableStream>>atEnd, but only for arrays/strings
// Does not use successFlag. Unary, so does not modify the stack pointer
BOOL __fastcall Interpreter::primitiveAtEnd()
{
PosStreamOTE* streamPointer = reinterpret_cast<PosStreamOTE*>(stackTop()); // Access receiver
//ASSERT(!ObjectMemoryIsIntegerObject(streamPointer) && ObjectMemory::isKindOf(streamPointer, Pointers.ClassPositionableStream));
PositionableStream* readStream = streamPointer->m_location;
// Ensure valid stream (see BBB p632)
if (!ObjectMemoryIsIntegerObject(readStream->m_index) ||
!ObjectMemoryIsIntegerObject(readStream->m_readLimit))
return primitiveFailure(0);
SMALLINTEGER index = ObjectMemoryIntegerValueOf(readStream->m_index);
SMALLINTEGER limit = ObjectMemoryIntegerValueOf(readStream->m_readLimit);
BehaviorOTE* bufClass = readStream->m_array->m_oteClass;
OTE* boolResult;
if (bufClass == Pointers.ClassString || bufClass == Pointers.ClassByteArray)
boolResult = index >= limit || (MWORD(index) >= readStream->m_array->bytesSize()) ?
Pointers.True : Pointers.False;
else if (bufClass == Pointers.ClassArray)
boolResult = index >= limit || (MWORD(index) >= readStream->m_array->pointersSize()) ?
Pointers.True : Pointers.False;
else
return primitiveFailure(1); // Doesn't work for non-Strings/ByteArrays/Arrays, or if out of bounds
stackTop() = reinterpret_cast<Oop>(boolResult);
return primitiveSuccess();
}
// This primitive handles PositionableStream>>nextSDWORD, but only for byte-arrays
// Unary message, so does not modify stack pointer
BOOL __fastcall Interpreter::primitiveNextSDWORD()
{
PosStreamOTE* streamPointer = reinterpret_cast<PosStreamOTE*>(stackTop()); // Access receiver
PositionableStream* readStream = streamPointer->m_location;
// Ensure valid stream - unusually this validity check is included in the Blue Book spec
// and appears to be implemented in most Smalltalks, so we implement here too.
if (!ObjectMemoryIsIntegerObject(readStream->m_index) ||
!ObjectMemoryIsIntegerObject(readStream->m_readLimit))
return primitiveFailure(0); // Receiver fails invariant check
SMALLINTEGER index = ObjectMemoryIntegerValueOf(readStream->m_index);
SMALLINTEGER limit = ObjectMemoryIntegerValueOf(readStream->m_readLimit);
// Is the current index within the limits of the collection?
// Remember that the index is 1 based (it's a Smalltalk index), and we're 0 based,
// so we don't need to increment it until after we've got the next object
if (index < 0 || index >= limit)
return primitiveFailure(2); // No, fail it
OTE* oteBuf = readStream->m_array;
BehaviorOTE* bufClass = oteBuf->m_oteClass;
if (bufClass != Pointers.ClassByteArray)
return primitiveFailure(1); // Collection cannot be handled by primitive, rely on Smalltalk code
ByteArrayOTE* oteBytes = reinterpret_cast<ByteArrayOTE*>(oteBuf);
const int newIndex = index + sizeof(SDWORD);
if (MWORD(newIndex) > oteBytes->bytesSize())
return primitiveFailure(3);
const Oop oopNewIndex = ObjectMemoryIntegerObjectOf(newIndex);
if (int(oopNewIndex) < 0)
return primitiveFailure(4); // index overflowed SmallInteger range
// When incrementing the index we must allow for it overflowing a SmallInteger, even though
// this is extremely unlikely in practice
readStream->m_index = oopNewIndex;
// Receiver is overwritten
ByteArray* byteArray = oteBytes->m_location;
replaceStackTopWithNew(Integer::NewSigned32(*reinterpret_cast<SDWORD*>(byteArray->m_elements+index)));
return primitiveSuccess(); // Succeed
}