1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.millscript.millscript.vm;
23
24 import org.millscript.commons.util.IList;
25 import org.millscript.commons.util.ListIterator;
26 import org.millscript.commons.util.MapIterator;
27 import org.millscript.commons.util.iterator.ArrayListIterator;
28 import org.millscript.commons.util.list.EArrayList;
29 import org.millscript.commons.util.list.IArrayList;
30 import org.millscript.millscript.alert.Alerts;
31
32 import java.io.Serializable;
33 import java.util.Iterator;
34
35 /**
36 * This class provides the skeletal implementation for a machine.
37 */
38 public abstract class BasicMachine extends TraceableMachine {
39
40 /**
41 * This class implements the save/restore point marker for use in the piggy
42 * bank.
43 */
44 private static class StackMark implements Serializable {
45
46 /**
47 * This is the new ID for the stack mark, which might be useful in the
48 * event a machine is serialized. It should ensure portability between
49 * JVMs.
50 */
51 private static final long serialVersionUID = 3760848956401463600L;
52
53 }
54
55 /**
56 * This class is used to mark the last save/restore point in the piggy
57 * bank.
58 */
59 private static final StackMark STACKMARK = new StackMark();
60
61 /**
62 * This is the equivalent of the call stack in a conventional
63 * implementation.
64 */
65 protected Object[] piggyBank = new Object[ 1024 ];
66
67 /**
68 * The index of the next value in the piggy bank. Note this is also the
69 * number of values in the stack.
70 */
71 private int piggyBankIndex = 0;
72
73 /**
74 * This is an operand stack.
75 */
76 protected Object[] valueStack = new Object[ 1024 ];
77
78 /**
79 * The index of the next value in the operand stack. Note this is also the
80 * number of values in the stack and hence it is always one greater than
81 * the index of the object in the value stack.
82 */
83 private int valueStackIndex = 0;
84
85 /**
86 * Returns the number of objects in the stack.
87 *
88 * @return the number of objects in the stack
89 */
90 public int getCount() {
91 return this.valueStackIndex;
92 }
93
94 /**
95 * Returns the object at the specified position in the stack.
96 *
97 * @param n index of the element to return
98 * @return the object at the specified position in the stack
99 */
100 public Object getIndex( final int n ) {
101 return this.valueStack[ n ];
102 }
103
104 /**
105 * Returns the object at the top of the stack. The object is left at it's
106 * current position on the stack.
107 *
108 * @return the object at the top of the stack
109 */
110 public Object peekObject() {
111 return this.valueStack[ this.valueStackIndex - 1 ];
112 }
113
114 /**
115 * Replaces the object at the top of the stack with the specified object.
116 *
117 * @param x the object to put at the top of the stack
118 */
119 public void pokeObject( final Object x ) {
120 this.valueStack[ this.valueStackIndex - 1 ] = x;
121 }
122
123 /**
124 * Pops the specified number of values from the top of the value stack and
125 * returns them as an array.
126 *
127 * @param nargs the number of values to pop
128 * @return
129 */
130 public Object[] popArgsArray( final int nargs ) {
131 if ( nargs >= 0 && nargs <= this.valueStackIndex ) {
132 final Object[] args = new Object[ nargs ];
133 for ( int i = nargs - 1; i >=0; i-- ) {
134 args[ i ] = this.popObject();
135 }
136 return args;
137 } else {
138 if ( nargs < 0 ) {
139 throw(
140 Alerts.fault(
141 "Compiler bug - trying to construct an array of negative size"
142 ).mishap()
143 );
144 } else {
145 throw(
146 Alerts.fault(
147 "Compiler bug - insufficient values left on the stack"
148 ).mishap()
149 );
150 }
151 }
152 }
153
154 /**
155 * Pops enough arguments off the stack to fill the supplied array. The item
156 * at the top of the stack goes at the end of the array.
157 *
158 * @param args the array to fill with values from the stack
159 * @return the supplied array filled with new values
160 */
161 public Object[] popArgsIntoArray( final Object[] args ) {
162 if ( args.length >= 0 && args.length <= this.valueStackIndex ) {
163 for ( int i = args.length - 1; i >=0; i-- ) {
164 args[ i ] = this.popObject();
165 }
166 return args;
167 } else {
168 if ( args.length < 0 ) {
169 throw(
170 Alerts.fault(
171 "Compiler bug - trying to construct an array of negative size"
172 ).mishap()
173 );
174 } else {
175 throw(
176 Alerts.fault(
177 "Compiler bug - insufficient values left on the stack"
178 ).mishap()
179 );
180 }
181 }
182 }
183
184 /**
185 * Pops the specified number of values from the top of the operand stack
186 * and returns an iterator to iterate over them.
187 *
188 * @param nargs the number of values to pop
189 * @return an iterator over the poped values
190 */
191 public ListIterator< Object > popArgsIterator( final int nargs ) {
192 return new ArrayListIterator< Object >( this.popArgsArray( nargs ), true );
193 }
194
195 /**
196 * Pops the specifed number of values from the top of the operand stack and
197 * returns them as a list.
198 *
199 * @param nargs the number of values to pop
200 * @return a list of the poped values
201 */
202 public IList< Object > popArgsList( final int nargs, final boolean mutable ) {
203 if ( nargs >= 0 && nargs <= this.valueStackIndex ) {
204 if ( mutable ) {
205
206 return new EArrayList< Object >( this.popArgsArray( nargs ), true );
207 } else {
208
209 return new IArrayList< Object >( this.popArgsArray( nargs ), true );
210 }
211 } else {
212 throw(
213 Alerts.fault(
214 "Compiler bug - insufficient values left on the stack"
215 ).mishap()
216 );
217 }
218 }
219
220 /**
221 * Pops the object off the top of the operand stack and returns it as the
222 * value of this method.
223 *
224 * @return the object from the top of the operand stack.
225 */
226 public Object popObject() {
227
228
229
230
231
232
233 return this.valueStack[ --this.valueStackIndex ];
234 }
235
236 /**
237 * Pushes all of the values in the specified array onto the stack. The
238 * items are pushed onto the stack from first in the array to the last.
239 *
240 * @param args the array to get values to push onto the stack from
241 */
242 public void pushArgsArray( final Object[] args ) {
243 for ( int i = 0; i < args.length; i++ ) {
244 this.pushObject( args[ i ] );
245 }
246 }
247
248 /**
249 * Pushes each of the values from the specified iterator onto the stack.
250 *
251 * @param it the iterator to get values to push onto the stack from
252 */
253 public void pushArgsIterator( final Iterator< ? > it ) {
254 while ( it.hasNext() ) {
255 this.pushObject( it.next() );
256 }
257 }
258
259 /**
260 * Pushes all of the values in the specified list onto the stack. The items
261 * are pushed onto the stack from first in the list to the last.
262 *
263 * @param list the list to get values to push onto the stack from
264 */
265 public void pushArgsList( final IList< ? > list ) {
266 for ( ListIterator< ? > it = list.iterator( true ); it.hasNext(); ) {
267 this.pushObject( it.nextValue() );
268 }
269 }
270
271 /**
272 * Pushes each of the values from the specified map iterator onto the
273 * stack. Note, this method ignores the keys from the supplied map iterator
274 * and only pushes the values.
275 *
276 * @param it the map iterator to push values onto the stack from
277 */
278 public void pushArgsMapIterator( final MapIterator< ?, ? > it ) {
279 while ( it.hasNext() ) {
280 this.pushObject( it.nextValue() );
281 }
282 }
283
284 /**
285 * Pushes the specified boolean value onto the operand stack.
286 *
287 * @param b a boolean value to push onto the operand stack.
288 */
289 public void pushBoolean( final boolean b ) {
290 if ( this.valueStackIndex >= this.valueStack.length ) {
291
292 final Object[] newArray = new Object[ (int) ( this.valueStack.length * 1.5 ) ];
293 System.arraycopy( this.valueStack, 0, newArray, 0, this.valueStack.length );
294 this.valueStack = newArray;
295 }
296
297 this.valueStack[ this.valueStackIndex++ ] = b ? Boolean.TRUE : Boolean.FALSE;
298 }
299
300 /**
301 * Pushes the specified object onto the operand stack.
302 *
303 * @param val an object to push onto the operand stack.
304 */
305 public void pushObject( final Object val ) {
306 if ( this.valueStackIndex >= this.valueStack.length ) {
307
308 final Object[] newArray = new Object[ (int) ( this.valueStack.length * 1.5 ) ];
309 System.arraycopy( this.valueStack, 0, newArray, 0, this.valueStack.length );
310 this.valueStack = newArray;
311 }
312
313 this.valueStack[ this.valueStackIndex++ ] = val;
314 }
315
316 /**
317 * Resets this machine after an error, making it ready to action new code.
318 */
319 public void reset() {
320 this.valueStackIndex = 0;
321 this.piggyBankIndex = 0;
322 this.doingIndex = 0;
323 }
324
325 /**
326 * Restore all the variables in the current save set (in reverse order) and
327 * dispose of the save set.
328 */
329 public void restoreSaved() {
330 for (;;) {
331 Object v = this.piggyBank[ --this.piggyBankIndex ];
332 if ( v == STACKMARK ) {
333 break;
334 }
335 Ref r = (Ref) this.piggyBank[ --this.piggyBankIndex ];
336 r.value = v;
337 }
338 }
339
340 /**
341 * Save a variable and its current value.
342 *
343 * @param r the reference to save
344 */
345 public void saveRef( final Ref r ) {
346 if ( this.piggyBankIndex >= this.piggyBank.length ) {
347
348 final Object[] newArray = new Object[ (int) ( this.piggyBank.length * 1.5 ) ];
349 System.arraycopy( this.piggyBank, 0, newArray, 0, this.piggyBank.length );
350 this.piggyBank = newArray;
351 }
352
353 this.piggyBank[ this.piggyBankIndex++ ] = r;
354 if ( this.piggyBankIndex >= this.piggyBank.length ) {
355
356 final Object[] newArray = new Object[ (int) ( this.piggyBank.length * 1.5 ) ];
357 System.arraycopy( this.piggyBank, 0, newArray, 0, this.piggyBank.length );
358 this.piggyBank = newArray;
359 }
360 this.piggyBank[ this.piggyBankIndex++ ] = r.value;
361 }
362
363 /**
364 * Sets the number of values on the stack. The new size should be less
365 * than the current size of the stack, i.e. this method cannot be used to
366 * increase the size of the stack.
367 *
368 * @param n the maximum number of values that should remain on the stack
369 */
370 public void setCount( final int n ) {
371
372
373 if ( n <= valueStackIndex ) {
374 this.valueStackIndex = n;
375 } else {
376 throw new IllegalArgumentException( "new count must be not be larger than the current count" );
377 }
378 }
379
380 /**
381 * Start a new save-set.
382 */
383 public void startSaving() {
384 if ( this.piggyBankIndex >= this.piggyBank.length ) {
385
386 final Object[] newArray = new Object[ (int) ( this.piggyBank.length * 1.5 ) ];
387 System.arraycopy( this.piggyBank, 0, newArray, 0, this.piggyBank.length );
388 this.piggyBank = newArray;
389 }
390
391 this.piggyBank[ this.piggyBankIndex++ ] = STACKMARK;
392 }
393
394 }