5 * Created by Alyssa Milburn on Tue May 25 2004.
6 * Copyright (c) 2004 Alyssa Milburn. All rights reserved.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
24 #include "caosScript.h"
27 #include <boost/format.hpp>
33 void dumpStack(caosVM
*vm
) {
34 std::cerr
<< "\tvalueStack: ";
36 for (i
= vm
->valueStack
.size() - 1; i
>= 0 && c
++ < 5; i
--)
37 std::cerr
<< vm
->valueStack
[i
].dump() << " | ";
42 std::cerr
<< std::endl
;
45 caosVM::caosVM(const AgentRef
&o
)
49 currentscript
.reset();
52 inputstream
= 0; outputstream
= 0;
58 resetCore(); // delete blocking, close streams
61 bool caosVM::isBlocking() {
62 if (!blocking
) return false;
63 bool bl
= (*blocking
)();
71 void caosVM::startBlocking(blockCond
*whileWhat
) {
73 // TODO: should this just fail to block?
74 throw creaturesException("trying to block in a non-blockable script");
77 throw creaturesException("trying to block with a block condition in-place");
81 inline void caosVM::safeJMP(int dest
) {
82 // std::cerr << "jmp from " << cip << " to " << dest << "(old nip = " << nip << ") in script of length " << currentscript->scriptLength() << std::endl;
84 std::cerr
<< currentscript
->dump();
85 throw caosException(boost::str(boost::format("Internal error: Unrelocated jump at %08x") % cip
));
87 if (dest
>= currentscript
->scriptLength()) {
88 std::cerr
<< currentscript
->dump();
89 throw creaturesException(boost::str(boost::format("Internal error: Jump out of bounds at %08x") % cip
));
94 inline void caosVM::invoke_cmd(script
*s
, bool is_saver
, int opidx
) {
95 const cmdinfo
*ci
= s
->dialect
->getcmd(opidx
);
96 // We subtract two here to account for a) the missing return, and b)
97 // consuming the new value.
98 int stackdelta
= ci
->stackdelta
- (is_saver
? 2 : 0);
99 unsigned int stackstart
= valueStack
.size();
100 assert(result
.isNull());
101 #ifndef VCPP_BROKENNESS
103 (this->*(ci
->savehandler
))();
105 (this->*(ci
->handler
))();
108 dispatchCAOS(this, ci
->savehandler_idx
);
110 dispatchCAOS(this, ci
->handler_idx
);
112 if (!is_saver
&& !result
.isNull()) {
113 valueStack
.push_back(result
);
116 assert(result
.isNull());
118 if (stackdelta
< INT_MAX
- 1) {
119 if ((int)stackstart
+ stackdelta
!= (int)valueStack
.size()) {
121 throw caosException(boost::str(boost::format("Internal error: Stack imbalance detected: expected to be %d after start of %d, but stack size is now %d") % stackdelta
% (int)stackstart
% (int)valueStack
.size()));
126 inline void caosVM::runOpCore(script
*s
, caosOp op
) {
128 case CAOS_NOP
: break;
131 int idx
= op
.argument
;
132 std::string err
= "aborted";
133 caosVar constVal
= s
->getConstant(idx
);
134 if (constVal
.hasString())
135 err
= constVal
.getString();
136 throw creaturesException(err
);
145 invoke_cmd(s
, true, op
.argument
);
150 invoke_cmd(s
, false, op
.argument
);
156 // This condition can arise as a result of bad save data,
157 // so an assert is not appropriate... or is it?
159 // In any case, it is mostly harmless but should never occur,
160 // as it indicates a bug in the CAOS compiler.
161 caos_assert(auxStack
.size() == 0);
164 timeslice
-= op
.argument
;
171 VM_PARAM_INTEGER(condaccum
);
172 assert(!v1
.isEmpty());
173 assert(!v2
.isEmpty());
174 int condition
= op
.argument
;
175 if (condition
& CAND
) condition
-= CAND
;
176 if (condition
& COR
) condition
-= COR
;
178 if (condition
== CEQ
)
180 if (condition
== CNE
)
181 result
= !(v1
== v2
);
183 if (condition
== CLT
)
185 if (condition
== CGE
)
187 if (condition
== CGT
)
189 if (condition
== CLE
)
192 if (condition
== CBT
) {
193 caos_assert(v1
.hasInt() && v2
.hasInt());
194 result
= (v2
.getInt() == v1
.getInt() & v2
.getInt());
196 if (condition
== CBF
) {
197 caos_assert(v1
.hasInt() && v2
.hasInt());
198 result
= (0 == v1
.getInt() & v2
.getInt());
200 if (op
.argument
& CAND
)
201 result
= (condaccum
&& result
);
203 result
= (condaccum
|| result
);
204 valueStack
.push_back(caosVar(result
));
209 valueStack
.push_back(vmStackItem(s
->getConstant(op
.argument
)));
214 valueStack
.push_back(vmStackItem(caosVar(op
.argument
)));
219 valueStack
.push_back(vmStackItem(s
->getBytestr(op
.argument
)));
224 caos_assert(op
.argument
>= 0);
225 caos_assert(op
.argument
< (int)valueStack
.size());
226 auxStack
.push_back(valueStack
[valueStack
.size() - op
.argument
- 1]);
229 case CAOS_RESTORE_AUX
:
231 caos_assert(op
.argument
>= 0);
232 caos_assert(op
.argument
<= (int)auxStack
.size());
233 for (int i
= 0; i
< op
.argument
; i
++) {
234 valueStack
.push_back(auxStack
.back());
241 caos_assert(op
.argument
>= 0);
242 caos_assert(op
.argument
< (int)valueStack
.size());
243 for (int i
= 0; i
< op
.argument
; i
++) {
244 int top
= valueStack
.size() - 1;
245 std::swap(valueStack
[top
- i
], valueStack
[top
- i
- 1]);
253 safeJMP(op
.argument
);
258 safeJMP(op
.argument
);
263 VM_PARAM_INTEGER(counter
);
266 safeJMP(op
.argument
);
267 valueStack
.push_back(caosVar(counter
));
273 callStack
.push_back(callStackItem());
274 callStack
.back().nip
= nip
;
275 callStack
.back().valueStack
.swap(valueStack
);
276 safeJMP(op
.argument
);
286 throw caosException(std::string("Stack item type mismatch: ") + v
.dump());
288 targ
= v
.getAgentRef();
289 safeJMP(op
.argument
);
293 throw creaturesException(boost::str(boost::format("Illegal opcode %d") % (int)op
.opcode
));
297 inline void caosVM::runOp() {
302 if (runops
> 1000000) throw creaturesException("script exceeded 1m ops");
304 shared_ptr
<script
> scr
= currentscript
;
305 caosOp op
= currentscript
->getOp(cip
);
310 << boost::str(boost::format("optrace(%s): INST=%d TS=%d %p @%08d top=%s depth=%d ") % scr
->filename
.c_str() % (int)inst
% (int)timeslice
% (void *)this % cip
% (valueStack
.empty() ? std::string("(empty)") : valueStack
.back().dump()) % valueStack
.size())
311 << dumpOp(currentscript
->dialect
, op
) << std::endl
;
316 runOpCore(scr
.get(), op
);
317 } catch (caosException
&e
) {
318 e
.trace(currentscript
, op
.traceindex
);
321 } catch (creaturesException
&e
) {
328 void caosVM::stop() {
330 currentscript
.reset();
333 void caosVM::runEntirely(shared_ptr
<script
> s
) {
334 // caller is responsible for resetting/setting *all* state!
335 cip
= nip
= runops
= 0;
340 if (!currentscript
) break;
344 throw creaturesException("blocking in an installation script");
349 bool caosVM::fireScript(shared_ptr
<script
> s
, bool nointerrupt
, Agent
*frm
) {
352 if (lock
) return false; // can't interrupt scripts which called LOCK
353 if (currentscript
&& nointerrupt
) return false; // don't interrupt scripts with a timer script
363 void caosVM::resetScriptState() {
368 void caosVM::resetCore() {
381 c_FILE_ICLO(); // make sure input stream is freed
382 inputstream
= 0; // .. possibly not strictly necessary, when all is bugfree
383 c_FILE_OCLO(); // make sure output stream is freed
391 _p_
[0].reset(); _p_
[0].setInt(0); _p_
[1].reset(); _p_
[1].setInt(0);
392 for (unsigned int i
= 0; i
< 100; i
++) { var
[i
].reset(); var
[i
].setInt(0); }
397 cip
= nip
= runops
= 0;
400 void caosVM::tick() {
403 while (currentscript
&& !stop_loop
&& (timeslice
> 0 || inst
)) {
404 if (isBlocking()) return;
409 void caosVM::dummy_cmd() {