1 /**
2 * AUTOHIT 2003
3 * Copyright Erich P Gatejen (c) 1989,1997,2003,2004
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or (at
8 * your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 *
18 * Additional license information can be found in the documentation.
19 * @author Erich P Gatejen
20 */
21 package autohit.vm;
22
23 import autohit.call.Call;
24 import autohit.call.CallException;
25 import autohit.call.Call_METHOD;
26 import autohit.common.AutohitErrorCodes;
27 import autohit.common.Constants;
28 import autohit.creator.SimLanguage;
29 import autohit.vm.i.VMIAssert;
30 import autohit.vm.i.VMICall;
31 import autohit.vm.i.VMIClear;
32 import autohit.vm.i.VMIEval;
33 import autohit.vm.i.VMIExec;
34 import autohit.vm.i.VMIFetch;
35 import autohit.vm.i.VMIGoto;
36 import autohit.vm.i.VMIIf;
37 import autohit.vm.i.VMIJump;
38 import autohit.vm.i.VMILoad;
39 import autohit.vm.i.VMIMath;
40 import autohit.vm.i.VMIMerge;
41 import autohit.vm.i.VMIMethod;
42 import autohit.vm.i.VMINew;
43 import autohit.vm.i.VMINop;
44 import autohit.vm.i.VMIRScope;
45 import autohit.vm.i.VMIReduce;
46 import autohit.vm.i.VMIScope;
47 import autohit.vm.i.VMIStore;
48 import autohit.vm.i.VMISubr;
49 import autohit.vm.i.VMInstruction;
50 import autohit.vm.process.StringProcessors;
51
52 /**
53 * A VM for a Sim.
54 * <p>
55 * A VMNOp marks the bottom of the scope stack. If we ever pop a nop, there
56 * is nothing left to do.
57 * <p>
58 * Subroutine stack frames consist of the IP pointer (to calling instruction) in Integer form and the calling
59 * VMExecutable. When a the IP goes past the last instruction, the SimVM wil
60 * check the stack for a VMExecutable. If it sees one, it will assume it is
61 * the return from a subroutine. The LEFT is left alone, since this is how the
62 * subroutine passes a return value. If the subroutine specified a return, but it
63 * isn't set, the VM will log an error and use a blank return value. The subroutine depth (subDepth)
64 * will be incremented and decremented as we enter and leave routines. This helps up
65 * keep an exception from unravelling the entire VM.
66 * <p>
67 * Currently only the following MATH operations are implemented
68 * <pre>
69 * + = plus
70 * - = minus
71 * / = division
72 * * = multiply
73 * </pre>
74 * <p>
75 * ERRORS ----------
76 *
77 * <p>
78 * This VM expects the following environment variables to be set
79 * before the execute() method is called. If they aren't set, the defaults
80 * will be used.
81 * <p>
82 *
83 * @see autohit.vm
84 *
85 * @author Erich P. Gatejen
86 * @version 1.0
87 * <i>Version History</i>
88 * <code>EPG - Rewrite - 8May03<br>
89 * EPG - Add goto - 16Jul03<br>
90 * EPG - Add assert - 5Aug03<br>
91 * EPG - Add module shortcut - 9Aug03</code>
92 *
93 */
94 public class SimVM extends VM {
95
96 /**
97 * The current executable.
98 *
99 * @see autohit.vm.VMExecutable
100 */
101 private VMExecutable mySim;
102
103 /**
104 * Subroutine depth.
105 */
106 private int subDepth;
107
108 /**
109 * Last instruction. Used to detect runawya programs.
110 */
111 private int lastIP;
112
113 /**
114 * Scratch object. Put it here because I have plans for the future
115 */
116 private Object scratch;
117
118 /**
119 * Current isntruction
120 */
121 private VMInstruction ci;
122
123 /**
124 * Default Constructor. Don't do anything!
125 */
126 public SimVM() {
127 super();
128 }
129
130 /**
131 * Complete construction. This will be (should be) called right after a VM
132 * object is constructed. This is where you set the target program name.
133 */
134 public void construct() {
135 right = Constants.EMPTY_LEFT;
136 left = Constants.EMPTY_LEFT;
137 subDepth = 0;
138 }
139
140 /**
141 * Destroy. This will be called when the VM is
142 * finalizing.
143 */
144 public void destruct() throws VMException {
145 // dont do anything
146 }
147
148 /**
149 * Prepare for execution of the first instruction. We need to
150 * add environment variables. DO NOT call this method directly.
151 *
152 * @throws Any exceptions it encounters.
153 */
154 public void prepare() throws Exception {
155
156 // Toss a NOP on the stack.
157 core.push(new VMINop());
158
159 // Load the program
160 mySim = loader.load(rootProgram);
161
162 // last instruction set to soemthing impossible
163 lastIP = -99999;
164 }
165
166 /**
167 * Implements the inherited abstract method execute(). Call this to execute
168 * a single instruction, The first call will be automatic after the inherited
169 * start() method is called. From there, the owning Object/Thread should
170 * call this method for each successive instruction to execute.
171 * <p>
172 * This method will throw a VMException(VMException.DONE) if there are
173 * no more instructions that can be executed. (The ip is past the
174 * end of the exec Vector).
175 * <p>
176 * @throws VMException
177 * @see autohit.vm.VMException
178 */
179 public void execute() throws VMException {
180
181 ci = null;
182
183 // Have we completed this routine?
184 if (ip >= mySim.core.size()) {
185
186 lastIP = -99999; // something impossible
187
188 // Pop until we get to something interesting
189 // Intermeadiate crap is prolly old stack-frame junk
190 while (true) {
191 try {
192 scratch = core.pop();
193 } catch (Exception e) {
194 scratch = null;
195 }
196 if (scratch == null) {
197 throw new VMException(
198 "Instruction pointer out of bounds. Stack drained. All hell has broken loose.",
199 VMException.CODE_VM_PANIC);
200 }
201
202 if (scratch instanceof String) {
203 // local variable. Remove it.
204 core.remove((String) scratch);
205 }
206
207 if (scratch instanceof VMExecutable) {
208 // leaving this subroutine. change context and cycle
209 try {
210 exit_subr();
211 mySim = (VMExecutable) scratch;
212 ip++;
213 } catch (VMException e) {
214 throw e;
215 }
216 return;
217 }
218
219 if (scratch instanceof VMINop) {
220 // found that NOP at the bottom of the stack. Must be done.
221 throw new VMException(VMException.CODE_VM_DONE);
222 }
223
224 } // end while
225 } // end if
226
227 try {
228
229 // check for a runaway program that just keeps executing the
230 // same dead command. This is what happens when a GOTO
231 // points at itself. :^)
232 if (ip == lastIP) {
233 ip++; // kick it
234 myLog.error(
235 "VMSim:"
236 + mySim.name
237 + " Detected a runaway program that is looping on the same instruction. IP kicked by one, but the VM is probibly unstable.",
238 AutohitErrorCodes.CODE_VM_INSTRUCTION_ABORT);
239 return;
240 } else {
241 lastIP = ip;
242 }
243
244 ci = (VMInstruction) mySim.core.get(ip);
245 // NOTES: Each instruction is responsible for advancing the
246 // ip.
247 //DEBUG
248 //myLog.debug("SIMVM: get token at ip=" + ip + " is=" + ci.instruction);
249
250 switch (ci.instruction) {
251
252 case VMInstruction.CALL :
253 // i.call(target) : call TARGET, target put result in LEFT, store LEFT in result.
254 handleCall((VMICall) ci);
255 ip++;
256 break;
257
258 case VMInstruction.METHOD :
259 // i.method(n,m) : call MATHOD, target put result in LEFT, store LEFT in result.
260 handleMethod((VMIMethod) ci);
261 ip++;
262 break;
263
264 case VMInstruction.CLEAR :
265 // i.clear(buffer) : clear a buffer
266 if (core.exists(((VMIClear) ci).t)) {
267 core.replace(((VMIClear) ci).t, new StringBuffer());
268 } else {
269 // It's a brand new buffer
270 core.store(((VMIClear) ci).t, new StringBuffer());
271 }
272 ip++;
273 break;
274
275 case VMInstruction.EVAL :
276 // * i.eval(literal) : evaluate and store in LEFT(literal)
277 handleEval((VMIEval) ci);
278 ip++;
279 break;
280
281 case VMInstruction.EXEC :
282 // i.exec(class) : exec TARGET, target put result in LEFT, store LEFT in result.
283 handleExec((VMIExec) ci);
284 ip++;
285 break;
286
287 case VMInstruction.FAULT :
288 // i.fault : push the IP out of bounds. this will bust us
289 // out of this routine.
290 ip = mySim.core.size() + 1;
291 break;
292
293 case VMInstruction.FETCH :
294 // i.fetch(variable) : load LEFT from storage specified
295 // may not be a buffer
296 // TODO SimVM currently only supports String and StringBuffer for passbyreference
297 if (core.exists(((VMIFetch) ci).v)) {
298 scratch = core.fetch(((VMIFetch) ci).v);
299 if ((scratch instanceof String)
300 || (scratch instanceof StringBuffer)) {
301 left = scratch;
302
303 } else {
304 // Blech! Something bad in this object
305 throw new VMException(
306 "ERROR in FETCH: Fetched object is not a String or StringBuffer. This may mean bad things later. name="
307 + ((VMIFetch) ci).v,
308 AutohitErrorCodes.CODE_VM_INSTRUCTION_WARNING);
309 }
310
311 } else {
312 // The variable doesn't exist. Forgive it if the next command is an assert
313 // TODO Terrible hack to look ahead for asserts on fetch fail
314 boolean faultme = true;
315 try {
316 if (mySim.core.get(ip + 1) instanceof VMIAssert) {
317 faultme = false;
318 left = null;
319 }
320 } catch (Exception ecccc) {
321 }
322 // FAULT.
323 if (faultme)
324 throw new VMException(
325 "FETCH failed: Variable does not exist. name="
326 + ((VMIFetch) ci).v,
327 AutohitErrorCodes
328 .CODE_VM_VARIABLE_NOT_DEFINED_FAULT);
329 }
330 ip++;
331 break;
332
333 case VMInstruction.IF :
334 handleIf((VMIIf) ci);
335 break;
336
337 case VMInstruction.ASSERT :
338 handleAssert((VMIAssert) ci);
339 break;
340
341 case VMInstruction.JUMP :
342 // literal jump
343 ip = ((VMIJump) ci).t;
344 break;
345
346 case VMInstruction.GOTO :
347 // literal jump
348 handleGoto((VMIGoto) ci);
349 break;
350
351 case VMInstruction.LOAD :
352 // i.load(literal) : load literal into LEFT
353 left = ((VMILoad) ci).l;
354 ip++;
355 break;
356
357 case VMInstruction.MATH :
358 // i.math(oper) : execute operation from RIGHT(literal) to LEFT(literal)
359 handleMath((VMIMath) ci);
360 ip++;
361 break;
362
363 case VMInstruction.MERGE :
364 // i.merge(buffer) : merge LEFT(literal) with named buffer
365 handleMerge((VMIMerge) ci);
366 ip++;
367 break;
368
369 case VMInstruction.NEW :
370 // i.new(variable) : push LEFT(literal) on stack and mirror to storage
371 core.store(((VMINew) ci).v, left);
372 ip++;
373 break;
374
375 case VMInstruction.NOP :
376 // Just burn the cycle
377 ip++;
378 break;
379
380 case VMInstruction.REDUCE :
381 // i.reduce(buffer) : reduce a buffer and put in LEFT(literal)
382 handleReduce((VMIReduce) ci);
383 ip++;
384 break;
385
386 case VMInstruction.RIGHT :
387 // move LEFT(literal) to RIGHT(literal)
388 right = left;
389 ip++;
390 break;
391
392 case VMInstruction.RSCOPE :
393 // pop the stack to the next i.scope. remove all encounted vars
394 core.discardScopeFrame();
395 ip++;
396 break;
397
398 case VMInstruction.SCOPE :
399 // push to stack as a marker
400 core.markScope();
401 ip++;
402 break;
403
404 case VMInstruction.STORE :
405 // i.store(variable) : update in scope variable. if does not exist, do a i.new
406 if (core.exists(((VMIStore) ci).v)) {
407 core.replace(((VMIStore) ci).v, left);
408 } else {
409 core.store(((VMIStore) ci).v, left);
410 }
411 ip++;
412 break;
413
414 case VMInstruction.SUBR :
415 // i.subr(target) : fork to ROUTINE, store LEFT in result.
416 entry_subr((VMISubr) ci);
417 break;
418
419 case VMInstruction.MASK :
420 default :
421 throw new VMException(
422 "Unsupported instruction encounted by autohit.SimVM. nToken=["
423 + ci.instruction
424 + "]",
425 AutohitErrorCodes.CODE_VM_INVALID_INSTRUCTION_FAULT);
426 }
427
428 } catch (VMException e) {
429
430 // Act based on the level
431 if (VMException.isFault(e.numeric)
432 || VMException.isPanic(e.numeric)) {
433
434 faults++;
435
436 // BUST THIS ROUTINE
437 if (subDepth > 0) {
438 // we are in a subroutine. Bomb out of it. Find the stack bottom or
439 // the bottom of the subroutine stackframe
440 try {
441 scratch = core.peek();
442 while ((scratch != null)
443 && (!(scratch instanceof VMINop))
444 && (!(scratch instanceof VMExecutable))) {
445 core.pop();
446 scratch = core.peek();
447 }
448 } catch (Exception ex) {
449 // don't care - end of line
450 }
451 ip = mySim.core.size() + 9999;
452 // make sure it is out of bounds
453
454 myLog.error(
455 "VMSim:Routine "
456 + mySim.name
457 + " FAULT at ip="
458 + ip
459 + ". Message="
460 + e.getMessage(),
461 e.numeric);
462 faults++;
463
464 } else {
465 // top level - this is the root routine - let the VM unravel
466 myLog.error(
467 "VMSim:Routine:"
468 + mySim.name
469 + " TERMINAL FAULT at ip="
470 + ip
471 + ". Message="
472 + e.getMessage(),
473 e.numeric);
474 dumpei(ci);
475 faults++;
476 throw e;
477 }
478
479 } else if (VMException.isError(e.numeric)) {
480 myLog.error(
481 "VMSim:Routine:"
482 + mySim.name
483 + " ERROR at ip="
484 + ip
485 + ". Message="
486 + e.getMessage(),
487 e.numeric);
488 errors++;
489 if (myLog.debugState())
490 dumpei(ci);
491
492 // make sure the IP advances!
493 ip++;
494
495 } else {
496 // Don't consider anything else an error, but log it with that priority
497 myLog.error(
498 "VMSim:Routine:"
499 + mySim.name
500 + " INFO. Message="
501 + e.getMessage(),
502 e.numeric);
503
504 // make sure the IP advances!
505 ip++;
506 }
507
508 } catch (Exception e) {
509 throw new VMException(
510 "Unrecoverable Fault: exception=[" + e.getMessage(),
511 AutohitErrorCodes.CODE_VM_PANIC,
512 e);
513 }
514 }
515
516 /*
517 * Handle CALL instruction
518 * i.call(target) : call TARGET, target put result in LEFT, store LEFT in result.
519 */
520 private void handleCall(VMICall instr) throws Exception {
521
522 try {
523
524 Call c = loader.get(instr.t, core, myLog);
525 left = c.call();
526
527 } catch (CallException e) {
528 if (myLog.debugState())
529 myLog.error(
530 "CALL Subsystem error. CALL implementation threw an exception. Unable to complete CALL. error="
531 + e.getMessage(),
532 e.numeric);
533 throw e;
534
535 } catch (VMException e) {
536 throw e;
537 } catch (Exception e) {
538 throw new VMException(
539 "CALL Subsystem fault. Faulted out of a CALL. error="
540 + e.getMessage(),
541 VMException.CODE_VM_CALL_FAULT,
542 e);
543 }
544 }
545
546 /*
547 * Handle METHOD instruction. Basically, this is a cheater for
548 * using CALL_METHOD.
549 * i.method() : call Module/method, target put result in LEFT, store LEFT in result.
550 */
551 private void handleMethod(VMIMethod instr) throws Exception {
552
553 try {
554
555 // We know it is a method
556 Call_METHOD c = (Call_METHOD) loader.get("METHOD", core, myLog);
557 left = c.do_call((String) left, instr.m);
558
559 } catch (CallException e) {
560 if (myLog.debugState())
561 myLog.error(
562 "METHOD Subsystem error. METHOD implementation threw an exception. Unable to complete CALL. error="
563 + e.getMessage(),
564 e.numeric);
565 throw e;
566
567 } catch (VMException e) {
568 throw e;
569 } catch (Exception e) {
570 throw new VMException(
571 "METHOD Subsystem fault. Faulted out of a METHOD. error="
572 + e.getMessage(),
573 VMException.CODE_VM_CALL_FAULT,
574 e);
575 }
576 }
577
578 /*
579 * Handle EXEC instruction
580 * i.exec(class) : exec TARGET, target put result in LEFT, store LEFT in result.
581 */
582 private void handleExec(VMIExec instr) throws VMException {
583
584 try {
585
586 // TODO implement exec
587
588 } catch (Exception e) {
589 throw new VMException(
590 " Execution fork Subsystem fault. Unable to complete EXEC. error="
591 + e.getMessage(),
592 AutohitErrorCodes.CODE_VM_EXEC_FAULT,
593 e);
594 }
595 }
596
597 /*
598 * Handle GOTO instruction
599 * i.goto(target) : scope sensitive jump. includes a nasty ass hack to bust any scope frame.
600 */
601 private void handleGoto(VMIGoto instr) throws VMException {
602
603 // decide which way we jump
604 int delta = instr.t - ip;
605 if (delta == 0) {
606 // points at itself. this is BAD
607 throw new VMException(
608 "Circular GOTO detected. This subr will deadlock. Forcing it to bust out.",
609 VMException.CODE_VM_INTENTIONAL_FAULT);
610
611 } else if (delta == 1) {
612 // points at the next instruction. silly but valid.
613 ip++;
614
615 } else if (delta < 0) {
616
617 // jumping UP. Look for any SCOPES and bust their frames
618 Object tci;
619 int drscopes = 0;
620 int dscopes = 0;
621 int dbalance = 0;
622 for (int i = ip; i > instr.t; i--) {
623 tci = mySim.core.get(i);
624 if (tci instanceof VMIScope) {
625 dscopes++;
626 } else if (tci instanceof VMIRScope) {
627 drscopes++;
628 }
629 }
630 dbalance = dscopes - drscopes;
631 while (dbalance > 0) {
632 core.discardScopeFrame();
633 dbalance--;
634 }
635
636 ip = instr.t;
637
638 } else {
639
640 // jumping DOWN. Look for any RSCOPES and bust their frames
641 Object tci;
642 int drscopes = 0;
643 int dscopes = 0;
644 int dbalance = 0;
645 for (int i = ip; i < instr.t; i++) {
646 tci = mySim.core.get(i);
647 if (tci instanceof VMIScope) {
648 dscopes++;
649 } else if (tci instanceof VMIRScope) {
650 drscopes++;
651 }
652 }
653 dbalance = drscopes - dscopes;
654 while (dbalance > 0) {
655 core.discardScopeFrame();
656 dbalance--;
657 }
658
659 ip = instr.t;
660
661 } // end if down
662 }
663
664 /*
665 * Handle Subroutine instruction
666 * i.subr(target) : fork to ROUTINE, store LEFT in result.
667 *
668 * Handle the subroute call, steps 3 through 7
669 * ENTRY
670 * 1- Hit i.scope (emitted)
671 * 2- Instantiate parameters (emitted)
672 * 3- Instantiate return variable as defined in i.subr instruction
673 * 4- Toss i.subr instruction on the stack
674 * 5- Toss Instruction Pointer on the stack
675 * 6- Toss reference to currently runninng executable (VMExec.) on stack
676 * 7- Load new SUBR and let run
677 *
678 * 1, 2 are emitted instructions, as such:
679 * <subroutine> i.scope // do 1
680 * (SET)* // do 2
681 * i.subr(name) // do 3 through 13
682 * i.rscope // do 14
683 * if (result exist) i.store(result) // do 15
684 */
685 private void entry_subr(VMISubr instr) throws VMException {
686
687 myLog.debug("SIMVM: Enter subr. t=" + instr.t);
688
689 VMExecutable loadedSim = null;
690
691 try {
692
693 // Make sure we can load it
694 loadedSim = loader.load(instr.t);
695
696 } catch (Exception e) {
697
698 // This error is recoverable
699 left = Constants.EMPTY_LEFT;
700 ip++;
701 throw new VMException(
702 "Failed to load subroutine= "
703 + instr.t
704 + ". Aborting call. Reason="
705 + e.getMessage(),
706 AutohitErrorCodes.CODE_VM_INSTRUCTION_ABORT,
707 e);
708 }
709
710 // Any error after here is catastrophic
711 try {
712 // 3- Instantiate return variable as defined in i.subr instruction
713 if ((loadedSim.output != null)
714 && (loadedSim.output.name != null)) {
715 core.store(loadedSim.output.name, Constants.EMPTY_LEFT);
716 }
717
718 // 4- Toss i.subr instruction on the stack
719 core.push(instr);
720
721 // 5- Toss Instruction Pointer on the stack
722 core.push(new Integer(ip));
723
724 // 6- Toss reference to currently runninng executable (VMExec.) on stack
725 core.push(mySim);
726
727 // 7- Load new SUBR and let run
728 mySim = loadedSim;
729 subDepth++;
730 ip = 0;
731
732 } catch (Exception e) {
733 throw new VMException(
734 "SUBROUTINE Subsystem fault. Unable to complete SUBR. error="
735 + e.getMessage(),
736 VMException.CODE_VM_SUBSYSTEM_FAULT,
737 e);
738 }
739 }
740
741 /*
742 * Handle the subroutine exit, steps 10 through 11
743 * EXIT
744 * 10- Pop the IP (in the Integer)
745 * 11- Pop the i.subr. Read the i.subr.return, put in left, remove from core.
746 */
747 private void exit_subr() throws VMException {
748 try {
749
750 myLog.debug("SIMVM: Exit subr.");
751
752 // 10- Pop the IP (in the Integer)
753 Integer ipObj = (Integer) core.pop();
754 ip = ipObj.intValue();
755
756 // 11- Pop the i.subr. Read the vmexec.output.name, put in left.
757 core.pop();
758 if ((mySim.output != null)&&(mySim.output.name != null)) {
759 left = (String) core.fetch(mySim.output.name);
760 if (left == null) {
761 left = Constants.EMPTY_LEFT;
762 myLog.warning(
763 "SUBR EXIT: Expected a return value for "
764 + mySim.output.name
765 + " but it was null.");
766 }
767 } else {
768 left = Constants.EMPTY_LEFT;
769 }
770
771 // Unwrap
772 subDepth--;
773
774 } catch (Exception e) {
775 throw new VMException(
776 "Software Detected Fault: Failed subroutine context switch. error="
777 + e.getMessage(),
778 VMException.CODE_VM_SOFTWARE_DETECTED_FAULT,
779 e);
780 }
781 }
782
783 /*
784 * Handle eval Instruction
785 * i.eval(literal) : evaluate and store in LEFT(literal)
786 */
787 private void handleEval(VMIEval instr) throws VMException {
788
789 try {
790
791 left = StringProcessors.evalString2Core(instr.e, core);
792
793 } catch (Exception e) {
794 left = Constants.EMPTY_LEFT;
795
796 throw new VMException(
797 "Evaluation error. Unable to complete EVAL on ["
798 + instr.e
799 + "]. error="
800 + e.getMessage(),
801 AutohitErrorCodes.CODE_PROGRAM_ERROR,
802 e);
803
804 }
805 }
806
807 /**
808 * Handle Math Instruction<code>
809 * If either left or right fail a parse, the values will be compared
810 * as strings.
811 * i.math(oper) : execute operation from RIGHT(literal)</code>
812 */
813 private void handleMath(VMIMath instr) throws VMException {
814
815 int res = 0;
816 int l = 0;
817 int r = 0;
818 boolean donumbers = true;
819
820 myLog.debug(
821 "MATH start: left="
822 + left.toString()
823 + " right="
824 + right.toString());
825
826 try {
827
828 try {
829 l = Integer.parseInt(left.toString());
830 } catch (NumberFormatException e) {
831 donumbers = false;
832 }
833 try {
834 r = Integer.parseInt(right.toString());
835 } catch (NumberFormatException e) {
836 donumbers = false;
837 }
838
839 if (donumbers) {
840
841 // treat like numeric arthimatic
842 if (instr.o.charAt(0) == Constants.MATH_PLUS) {
843 res = l + r;
844
845 } else if (instr.o.charAt(0) == Constants.MATH_MINUS) {
846 res = l - r;
847
848 } else if (instr.o.charAt(0) == Constants.MATH_DIVIDE) {
849
850 try {
851 res = l / r;
852 } catch (ArithmeticException e) {
853 dumpei(instr);
854 res = 0;
855 throw new VMException(
856 "DIVIDE BY ZERO. Math operation aborted; result = 0. Operation="
857 + instr.o,
858 AutohitErrorCodes.CODE_PROGRAM_DIVIDEBYZERO,
859 e);
860 }
861
862 } else if (instr.o.charAt(0) == Constants.MATH_MULTIPLY) {
863 res = r * l;
864
865 } else if (instr.o.charAt(0) == SimLanguage.cEQ_OPERATION) {
866 res = r - l;
867
868 } else {
869 dumpei(instr);
870 throw new VMException(
871 "Unrecognized math operations. Math operation aborted; result = 0. Operation="
872 + instr.o,
873 AutohitErrorCodes.CODE_PROGRAM_ERROR);
874
875 }
876
877 } else if (right instanceof String) {
878 // treat like a string compare. Compares two strings lexicographically.
879 // "0" means the strings are identical
880 res = ((String) right).compareTo(left.toString());
881
882 } else {
883 throw new VMException(
884 "Math error. Left and/or Right expressions inappropriate objects for a math operation.",
885 VMException.CODE_VM_INSTRUCTION_ERROR);
886 }
887
888 } catch (VMException ve) {
889 throw ve;
890 } catch (Exception e) {
891 throw new VMException(
892 "Math fault. Unable to complete MATH. error="
893 + e.getMessage(),
894 VMException.CODE_VM_INSTRUCTION_FAULT,
895 e);
896 }
897
898 // store result
899 left = Integer.toString(res);
900
901 myLog.debug("MATH done: left=" + left);
902 }
903
904 /**
905 * Handle merge instruction<p><code>
906 * i.merge(buffer) : merge LEFT(literal) with named buffer</code>
907 */
908 private void handleMerge(VMIMerge instr) throws VMException {
909
910 try {
911
912 Object tb = core.fetch(instr.b);
913
914 if (tb != null) {
915
916 if (tb instanceof StringBuffer) {
917
918 ((StringBuffer) tb).append(left);
919
920 } else {
921 throw new VMException(
922 "VMSim: Merge not possible. ["
923 + instr.b
924 + "] is not a buffer. Aborting instruction, but not execution.",
925 AutohitErrorCodes.CODE_PROGRAM_ERROR);
926 }
927
928 } else {
929 throw new VMException(
930 "VMSim: Merge not possible. ["
931 + instr.b
932 + "] does not exist. Aborting instruction, but not execution.",
933 AutohitErrorCodes.CODE_PROGRAM_ERROR);
934 }
935
936 } catch (Exception e) {
937 throw new VMException(
938 "VMSim: Merge fault. Unable to complete MERGE. error="
939 + e.getMessage(),
940 AutohitErrorCodes.CODE_VM_SOFTWARE_DETECTED_FAULT,
941 e);
942 }
943 }
944
945 /**
946 * Handle If instruction<p><code>
947 * i.if(literal,oper): Evaluate LEFT(literal). 0 means the evaluation was a match.
948 * You can modify with an operationl, oper = (gt|lt|eq|not):
949 * eq : default. if LEFT(literal) is not 0, jump to literal
950 * gt : if LEFT(literal) is =< 0, jump to literal
951 * lt : if LEFT(literal) is >= 0, jump to literal
952 * not : if LEFT(literal) is 0, jump to literal</code>
953 * TODO This using a non-zero as a default in IF resolution may be scary
954 */
955 private void handleIf(VMIIf instr) throws VMException {
956
957 // Prepare Left. Default value is not zero
958 int leftval = SimLanguage.NOT_ZERO;
959 String scrubleft;
960
961 if (left instanceof String) {
962 scrubleft = (String) left;
963
964 } else if (left instanceof StringBuffer) {
965 scrubleft = ((StringBuffer) left).toString();
966 } else {
967 ip = ((VMIIf) ci).t;
968 throw new VMException(
969 "Left expression inappropriate type for IF operation. Assume FALSE.",
970 VMException.CODE_VM_INSTRUCTION_ERROR);
971 }
972
973 try {
974 leftval = Integer.parseInt(scrubleft);
975 } catch (Exception e) {
976 } // Dont care
977
978 switch (instr.operFlag) {
979
980 case SimLanguage.EQ :
981 if (leftval == SimLanguage.ZERO) {
982 ip++; // success. branch.
983 } else {
984 ip = ((VMIIf) ci).t; // Failed if
985 }
986 break;
987
988 case SimLanguage.LT :
989 if (leftval <= SimLanguage.ZERO) {
990 ip++; // success. branch.
991 } else {
992 ip = ((VMIIf) ci).t; // Failed if
993 }
994 break;
995
996 case SimLanguage.GT :
997 if (leftval >= SimLanguage.ZERO) {
998 ip++; // success. branch.
999 } else {
1000 ip = ((VMIIf) ci).t; // Failed if
1001 }
1002 break;
1003
1004 case SimLanguage.NOT :
1005 if (leftval != SimLanguage.ZERO) {
1006 ip++; // success. branch.
1007 } else {
1008 ip = ((VMIIf) ci).t; // Failed if
1009 }
1010 break;
1011
1012 default :
1013 throw new VMException(
1014 "Software Detected Fault. IF with an unknown oper. This should never happen.",
1015 VMException.CODE_VM_SOFTWARE_DETECTED_FAULT);
1016 }
1017
1018 }
1019
1020 /**
1021 * Handle Assert instruction<p><code>
1022 * i.if(literal,oper): Evaluate LEFT(literal). If it is empty, null, or void, the check passes
1023 * and the IP should move to the next instruction. If the check fails,
1024 * then the IP should be set to the target.
1025 * You can modify with an operationl, oper = (eq|not):
1026 * eq : default. if LEFT(literal) is empty, null, or void, then jump to literal
1027 * not : if LEFT(literal) is NOT empty, null, or void, jump to literal</code>
1028 */
1029 private void handleAssert(VMIAssert instr) throws VMException {
1030
1031 // See if it is empty
1032 boolean isempty = false;
1033 if (left == null) {
1034 isempty = true;
1035 } else if (left instanceof String) {
1036 if (((String) left).equals(Constants.EMPTY_LEFT)
1037 || ((String) left).length() < 1) {
1038 isempty = true;
1039 }
1040 }
1041
1042 switch (instr.operFlag) {
1043
1044 // ASSERT THERE IS SOMETHING
1045 case SimLanguage.EQ :
1046 if (isempty) {
1047 // It is empty. Assert failed
1048 ip = ((VMIAssert) ci).t;
1049 } else {
1050 // It is not empty. Assert succeeded.
1051 ip++;
1052 }
1053 break;
1054
1055 // ASSERT THERE IS NOT SOMETHING
1056 case SimLanguage.NOT :
1057 if (isempty) {
1058 // It is empty. Assert succeeded.
1059 ip++;
1060 } else {
1061 // It is not empty. Assert failed
1062 ip = ((VMIAssert) ci).t;
1063 }
1064 break;
1065
1066 default :
1067 throw new VMException(
1068 "Software Detected Fault. ASSERT with an unknown or unsupported operation. This should never happen. oper="
1069 + instr.operFlag,
1070 VMException.CODE_VM_SOFTWARE_DETECTED_FAULT);
1071 } // end case
1072 }
1073
1074 /*
1075 * Handle merge instruction
1076 * i.reduce(buffer) : reduce a buffer and put in LEFT(literal)
1077 */
1078 private void handleReduce(VMIReduce instr) throws VMException {
1079
1080 try {
1081
1082 Object tb = core.fetch(instr.b);
1083
1084 if (tb != null) {
1085
1086 if (tb instanceof StringBuffer) {
1087
1088 left = tb.toString();
1089
1090 } else {
1091 throw new VMException(
1092 "VMSim: Reduce not possible. ["
1093 + instr.b
1094 + "] is not a buffer. Aborting instruction, but not execution.",
1095 AutohitErrorCodes.CODE_PROGRAM_ERROR);
1096 }
1097
1098 } else {
1099 throw new VMException(
1100 "Reduce not possible. ["
1101 + instr.b
1102 + "] does not exist. Aborting instruction, but not execution.",
1103 AutohitErrorCodes.CODE_PROGRAM_ERROR);
1104 }
1105
1106 } catch (Exception e) {
1107 throw new VMException(
1108 "Reduce fault. Unable to complete REDUCE. error="
1109 + e.getMessage(),
1110 AutohitErrorCodes.CODE_VM_SOFTWARE_DETECTED_FAULT,
1111 e);
1112 }
1113 }
1114
1115 /*
1116 * LOG/Message Helper. -- DUMP
1117 */
1118 private void dumpei(VMInstruction i) {
1119 if ((myLog.debugState() == false) || (ci == null))
1120 return;
1121 myLog.debug("VMSim: DUMP instruction pointer. ip=" + ip);
1122 myLog.debug("VMSim: DUMP errored instruction. i=" + i.toString());
1123 myLog.debug("VMSim: DUMP left. l=[" + left + "]");
1124 myLog.debug("VMSim: DUMP right. l=[" + right + "]");
1125 myLog.debug("VMSim: DUMP state. state=" + state);
1126 myLog.debug(
1127 "VMSim: DUMP running program="
1128 + mySim.name
1129 + "v"
1130 + mySim.major
1131 + "."
1132 + mySim.minor);
1133 }
1134
1135 }
|