<commit_before>/*
 *  caosVM.cpp
 *  openc2e
 *
 *  Created by <NAME> on Tue May 25 2004.
 *  Copyright (c) 2004 <NAME>. All rights reserved.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 */

#include "caosVM.h"
#include "openc2e.h"
#include "World.h"
#include "bytecode.h"
#include "caosScript.h"
#include <iostream>

#include <boost/format.hpp>

using std::cout;
using std::cerr;
using std::endl;

void dumpStack(caosVM *vm) {
	std::cerr << "\tvalueStack: ";
	int i, c = 0;
	for (i = vm->valueStack.size() - 1; i >= 0 && c++ < 5; i--)
		std::cerr << vm->valueStack[i].dump() << " | ";
	if (i >= 0)
		std::cerr << "...";
	else
		std::cerr << "END";
	std::cerr << std::endl;
}

caosVM::caosVM(const AgentRef &o)
	: vm(this)
{
	owner = o;
	currentscript.reset();
	cip = nip = 0;
	blocking = NULL;
	inputstream = 0; outputstream = 0;
	resetCore();
	trace = false;
}

caosVM::~caosVM() {
	resetCore(); // delete blocking, close streams
}

bool caosVM::isBlocking() {
	if (!blocking) return false;
	bool bl = (*blocking)();
	if (!bl) {
		delete blocking;
		blocking = NULL;
	}
	return bl;
}

void caosVM::startBlocking(blockCond *whileWhat) {
	if (!owner)
		// TODO: should this just fail to block?
		throw creaturesException("trying to block in a non-blockable script");
	inst = false;
	if (blocking)
		throw creaturesException("trying to block with a block condition in-place");
	blocking = whileWhat;
}

inline void caosVM::safeJMP(int dest) {
//	std::cerr << "jmp from " << cip << " to " << dest << "(old nip = " << nip << ") in script of length " << currentscript->scriptLength() << std::endl;
	if (dest < 0) {
		std::cerr << currentscript->dump();
		throw caosException(boost::str(boost::format("Internal error: Unrelocated jump at %08x") % cip));
	}
	if (dest >= currentscript->scriptLength()) {
		std::cerr << currentscript->dump();
		throw creaturesException(boost::str(boost::format("Internal error: Jump out of bounds at %08x") % cip));
	}
	nip = dest;
}

inline void caosVM::invoke_cmd(script *s, bool is_saver, int opidx) {
	const cmdinfo *ci = s->dialect->getcmd(opidx);
	// We subtract two here to account for a) the missing return, and b)
	// consuming the new value.
	int stackdelta = ci->stackdelta - (is_saver ? 2 : 0);
	unsigned int stackstart = valueStack.size();
	assert(result.isNull());
#ifndef VCPP_BROKENNESS
	if (is_saver)
		(this->*(ci->savehandler))();
	else
		(this->*(ci->handler))();
#else
	if (is_saver)
		dispatchCAOS(this, ci->savehandler_idx);
	else
		dispatchCAOS(this, ci->handler_idx);
#endif
	if (!is_saver && !result.isNull()) {
		valueStack.push_back(result);
		result.reset();
	} else {
		assert(result.isNull());
	}
	if (stackdelta < INT_MAX - 1) {
		if ((int)stackstart + stackdelta != (int)valueStack.size()) {
			dumpStack(this);
			throw caosException("Stack imbalance detected");
		}
	}<commit_msg>make the VM stack imbalance error a bit more informative<commit_after>/*
 *  caosVM.cpp
 *  openc2e
 *
 *  Created by <NAME> on Tue May 25 2004.
 *  Copyright (c) 2004 <NAME>. All rights reserved.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 */

#include "caosVM.h"
#include "openc2e.h"
#include "World.h"
#include "bytecode.h"
#include "caosScript.h"
#include <iostream>

#include <boost/format.hpp>

using std::cout;
using std::cerr;
using std::endl;

void dumpStack(caosVM *vm) {
	std::cerr << "\tvalueStack: ";
	int i, c = 0;
	for (i = vm->valueStack.size() - 1; i >= 0 && c++ < 5; i--)
		std::cerr << vm->valueStack[i].dump() << " | ";
	if (i >= 0)
		std::cerr << "...";
	else
		std::cerr << "END";
	std::cerr << std::endl;
}

caosVM::caosVM(const AgentRef &o)
	: vm(this)
{
	owner = o;
	currentscript.reset();
	cip = nip = 0;
	blocking = NULL;
	inputstream = 0; outputstream = 0;
	resetCore();
	trace = false;
}

caosVM::~caosVM() {
	resetCore(); // delete blocking, close streams
}

bool caosVM::isBlocking() {
	if (!blocking) return false;
	bool bl = (*blocking)();
	if (!bl) {
		delete blocking;
		blocking = NULL;
	}
	return bl;
}

void caosVM::startBlocking(blockCond *whileWhat) {
	if (!owner)
		// TODO: should this just fail to block?
		throw creaturesException("trying to block in a non-blockable script");
	inst = false;
	if (blocking)
		throw creaturesException("trying to block with a block condition in-place");
	blocking = whileWhat;
}

inline void caosVM::safeJMP(int dest) {
//	std::cerr << "jmp from " << cip << " to " << dest << "(old nip = " << nip << ") in script of length " << currentscript->scriptLength() << std::endl;
	if (dest < 0) {
		std::cerr << currentscript->dump();
		throw caosException(boost::str(boost::format("Internal error: Unrelocated jump at %08x") % cip));
	}
	if (dest >= currentscript->scriptLength()) {
		std::cerr << currentscript->dump();
		throw creaturesException(boost::str(boost::format("Internal error: Jump out of bounds at %08x") % cip));
	}
	nip = dest;
}

inline void caosVM::invoke_cmd(script *s, bool is_saver, int opidx) {
	const cmdinfo *ci = s->dialect->getcmd(opidx);
	// We subtract two here to account for a) the missing return, and b)
	// consuming the new value.
	int stackdelta = ci->stackdelta - (is_saver ? 2 : 0);
	unsigned int stackstart = valueStack.size();
	assert(result.isNull());
#ifndef VCPP_BROKENNESS
	if (is_saver)
		(this->*(ci->savehandler))();
	else
		(this->*(ci->handler))();
#else
	if (is_saver)
		dispatchCAOS(this, ci->savehandler_idx);
	else
		dispatchCAOS(this, ci->handler_idx);
#endif
	if (!is_saver && !result.isNull()) {
		valueStack.push_back(result);
		result.reset();
	} else {
		assert(result.isNull());
	}
	if (stackdelta < INT_MAX - 1) {
		if ((int)stackstart + stackdelta != (int)valueStack.size()) {
			dumpStack(this);
			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()));
		}
	}
{'fraction_non_alphanumeric': 0.15512465373961218, 'fraction_numerical': 0.00845604315497886, 'mean_word_length': 4.0852483320978505, 'pattern_counts': {'":': 0, '<': 85, '<?xml version=': 0, '>': 47, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6043189', 'n_tokens_mistral': 2490, 'n_tokens_neox': 2368, 'n_words': 728}