<commit_before>/**
 * Clever programming language
 * Copyright (c) Clever Team
 *
 * This file is distributed under the MIT license. See LICENSE for details.
 */

#ifdef CLEVER_DEBUG
#include <stdio.h>
#endif
#include "core/opcode.h"
#include "core/vm.h"
#include "core/value.h"
#include "core/location.hh"
#include "core/user.h"
#include "core/type.h"
#include "modules/std/core/function.h"
#include "modules/std/core/array.h"

#define OPCODE    m_inst[m_pc]

#if CLEVER_GCC_VERSION > 0 && !defined(CLEVER_NOGNU)
# define OP(name)    name
# define OPCODES     const static void* labels[] = { OP_LABELS }; goto *labels[m_inst[m_pc].opcode]
# define DISPATCH    ++m_pc; goto *labels[m_inst[m_pc].opcode]
# define END_OPCODES
# define VM_GOTO(n)  m_pc = n; goto *labels[m_inst[m_pc].opcode]
#else
# define OP(name)    case name
# define OPCODES     for (;;) { switch (m_inst[m_pc].opcode) {
# define DISPATCH    ++m_pc; break
# define END_OPCODES EMPTY_SWITCH_DEFAULT_CASE(); } }
# define VM_GOTO(n)  m_pc = n; break
#endif

namespace clever {

/// Displays an error message
void VM::error(const location& loc, const char* format, ...)
{
	std::ostringstream out;
	va_list args;

	out << "Fatal error: ";

	va_start(args, format);
	vsprintf(out, format, args);

	std::cerr << out.str();

	if (loc.begin.filename) {
		std::cerr << " on " << *loc.begin.filename << " line " << loc.begin.line;
	} else {
		std::cerr << " on line " << loc.begin.line;
	}
	std::cerr << std::endl;

	CLEVER_EXIT_FATAL();
}

/// Dumps the current stack trace
void VM::dumpStackTrace(std::ostringstream& out)
{
	if (m_call_stack.empty()) {
		return;
	}

	out << std::endl << "Stack trace:" << std::endl;

	do {
		const CallStackEntry& entry = m_call_stack.top();

		if (entry.loc) {
			if (entry.func) {
				out << "# " << entry.func->getName() << "()";
			} else {
				out << "# <main>";
			}

			if (entry.loc->begin.filename) {
				out << " at " << *entry.loc->begin.filename << ":";
			} else {
				out << " at <command line>:";
			}
			out << entry.loc->begin.line << std::endl;
		}

		m_call_stack.pop();
	} while (!m_call_stack.empty());
}

/// Fetchs a Value ptr according to the operand type
CLEVER_FORCE_INLINE Value* VM::getValue(const Operand& operand) const
{
	const Environment* source = NULL;

	switch (operand.op_type) {
		case FETCH_CONST: source = m_const_env;                          break;
		case FETCH_VAR:   source = m_call_stack.top().env;               break;
		case FETCH_TMP:   source = m_call_stack.top().env->getTempEnv(); break;
		default:  	      return NULL;
	}
	clever_assert_not_null(source);

	return source->getValue(operand.voffset);
}

/// Fetchs and change a Value pointer in the current temporary environment
void CLEVER_FORCE_INLINE VM::setTempValue(const Operand& operand,
	Value* value) const
{
	Environment* source = m_call_stack.top().env->getTempEnv();

	clever_delref(source->getValue(operand.voffset));
	source->setData(operand.voffset.second, value);
}

#ifdef CLEVER_DEBUG
void VM::dumpOperand(const Operand& op)
{
	const char* type[] = {
		"UNUSED", "FETCH_VAR", "FETCH_CONST", "FETCH_TMP", "JMP_ADDR"
	};

	switch (op.op_type) {
		case FETCH_CONST:
		case FETCH_TMP:
		case FETCH_VAR:
			::printf("%3zu:%3zu ", op.voffset.first, op.voffset.second);
			break;
		case JMP_ADDR:
			::printf("%7zu ", op.jmp_addr);
			break;
		case UNUSED:
			::printf("        ");
			break;
	}
	::printf(" (%-11s) | ", type[op.op_type]);
}

void VM::dumpOpcodes() const
{
	for (size_t i = 0, j = m_inst.size(); i < j; ++i) {
		const IR& ir = m_inst[i];
		::printf("[%03zu] %-12s |", i, get_opcode_name(ir.opcode));
		dumpOperand(ir.op1);
		dumpOperand(ir.op2);
		dumpOperand(ir.result);
		::printf("\n");
	}
	std::cout << std::endl;
}
#endif

// Make a copy of VM instance
void VM::copy(const VM* vm, bool deep)
{
	f_mutex      = const_cast<VM*>(vm)->getMutex();
	m_pc         = vm->m_pc;
	m_inst       = vm->m_inst;
	m_try_stack  = vm->m_try_stack;
	m_global_env = vm->m_global_env;
	m_call_stack = vm->m_call_stack;
	m_const_env  = vm->m_const_env;
}

// Function parameter binding
static CLEVER_FORCE_INLINE void _param_binding(const Function* func,
	const Environment* fenv, const ValueVector& args)
{
	size_t nargs = 0;

	if (func->hasArgs() && !args.empty()) {
		size_t num_args = args.size();
		ValueOffset argoff(0,0);

		if (num_args > func->getNumArgs()) {
			num_args = func->getNumArgs();
		}

		for (size_t i = 0, len = num_args; i < len; ++i) {
			fenv->getValue(argoff)->copy(args[i]);
			argoff.second++;
			++nargs;
		}
	}

	if (func->isVariadic()) {
		ArrayObject* arr = new ArrayObject;

		if (EXPECTED(!args.empty() && (args.size() - nargs) > 0)) {
			for (size_t i = nargs, j = args.size(); i < j; ++i) {
				arr->getData().push_back(args[i]->clone());
			}
		}

		Value* vararg = fenv->getValue(ValueOffset(0, func->getNumArgs()));
		vararg->setObj(CLEVER_ARRAY_TYPE, arr);
	}
}

// Prepares an user function/method call
CLEVER_FORCE_INLINE void VM::prepareCall(const Function* func, Environment* env)<commit_msg>- Simplify code<commit_after>/**
 * Clever programming language
 * Copyright (c) Clever Team
 *
 * This file is distributed under the MIT license. See LICENSE for details.
 */

#ifdef CLEVER_DEBUG
#include <stdio.h>
#endif
#include "core/opcode.h"
#include "core/vm.h"
#include "core/value.h"
#include "core/location.hh"
#include "core/user.h"
#include "core/type.h"
#include "modules/std/core/function.h"
#include "modules/std/core/array.h"

#define OPCODE    m_inst[m_pc]

#if CLEVER_GCC_VERSION > 0 && !defined(CLEVER_NOGNU)
# define OP(name)    name
# define OPCODES     const static void* labels[] = { OP_LABELS }; goto *labels[m_inst[m_pc].opcode]
# define DISPATCH    ++m_pc; goto *labels[m_inst[m_pc].opcode]
# define END_OPCODES
# define VM_GOTO(n)  m_pc = n; goto *labels[m_inst[m_pc].opcode]
#else
# define OP(name)    case name
# define OPCODES     for (;;) { switch (m_inst[m_pc].opcode) {
# define DISPATCH    ++m_pc; break
# define END_OPCODES EMPTY_SWITCH_DEFAULT_CASE(); } }
# define VM_GOTO(n)  m_pc = n; break
#endif

namespace clever {

/// Displays an error message
void VM::error(const location& loc, const char* format, ...)
{
	std::ostringstream out;
	va_list args;

	out << "Fatal error: ";

	va_start(args, format);
	vsprintf(out, format, args);

	std::cerr << out.str();

	if (loc.begin.filename) {
		std::cerr << " on " << *loc.begin.filename << " line " << loc.begin.line;
	} else {
		std::cerr << " on line " << loc.begin.line;
	}
	std::cerr << std::endl;

	CLEVER_EXIT_FATAL();
}

/// Dumps the current stack trace
void VM::dumpStackTrace(std::ostringstream& out)
{
	if (m_call_stack.empty()) {
		return;
	}

	out << std::endl << "Stack trace:" << std::endl;

	do {
		const CallStackEntry& entry = m_call_stack.top();

		if (entry.loc) {
			if (entry.func) {
				out << "# " << entry.func->getName() << "()";
			} else {
				out << "# <main>";
			}

			if (entry.loc->begin.filename) {
				out << " at " << *entry.loc->begin.filename << ":";
			} else {
				out << " at <command line>:";
			}
			out << entry.loc->begin.line << std::endl;
		}

		m_call_stack.pop();
	} while (!m_call_stack.empty());
}

/// Fetchs a Value ptr according to the operand type
CLEVER_FORCE_INLINE Value* VM::getValue(const Operand& operand) const
{
	const Environment* source = NULL;

	switch (operand.op_type) {
		case FETCH_CONST: source = m_const_env;                          break;
		case FETCH_VAR:   source = m_call_stack.top().env;               break;
		case FETCH_TMP:   source = m_call_stack.top().env->getTempEnv(); break;
		default:  	      return NULL;
	}
	clever_assert_not_null(source);

	return source->getValue(operand.voffset);
}

/// Fetchs and change a Value pointer in the current temporary environment
void CLEVER_FORCE_INLINE VM::setTempValue(const Operand& operand,
	Value* value) const
{
	Environment* source = m_call_stack.top().env->getTempEnv();

	clever_delref(source->getValue(operand.voffset));
	source->setData(operand.voffset.second, value);
}

#ifdef CLEVER_DEBUG
void VM::dumpOperand(const Operand& op)
{
	const char* type[] = {
		"UNUSED", "FETCH_VAR", "FETCH_CONST", "FETCH_TMP", "JMP_ADDR"
	};

	switch (op.op_type) {
		case FETCH_CONST:
		case FETCH_TMP:
		case FETCH_VAR:
			::printf("%3zu:%3zu ", op.voffset.first, op.voffset.second);
			break;
		case JMP_ADDR:
			::printf("%7zu ", op.jmp_addr);
			break;
		case UNUSED:
			::printf("        ");
			break;
	}
	::printf(" (%-11s) | ", type[op.op_type]);
}

void VM::dumpOpcodes() const
{
	for (size_t i = 0, j = m_inst.size(); i < j; ++i) {
		const IR& ir = m_inst[i];
		::printf("[%03zu] %-12s |", i, get_opcode_name(ir.opcode));
		dumpOperand(ir.op1);
		dumpOperand(ir.op2);
		dumpOperand(ir.result);
		::printf("\n");
	}
	std::cout << std::endl;
}
#endif

// Make a copy of VM instance
void VM::copy(const VM* vm, bool deep)
{
	f_mutex      = const_cast<VM*>(vm)->getMutex();
	m_pc         = vm->m_pc;
	m_inst       = vm->m_inst;
	m_try_stack  = vm->m_try_stack;
	m_global_env = vm->m_global_env;
	m_call_stack = vm->m_call_stack;
	m_const_env  = vm->m_const_env;
}

// Function parameter binding
static CLEVER_FORCE_INLINE void _param_binding(const Function* func,
	const Environment* fenv, const ValueVector& args)
{
	size_t nargs = 0;
	size_t args_count = args.size();

	if (func->hasArgs() && args_count) {
		ValueOffset argoff(0,0);
		size_t num_args = args_count;

		if (args_count > func->getNumArgs()) {
			num_args = func->getNumArgs();
		}
		nargs += num_args;

		for (size_t i = 0; i < num_args; ++i) {
			fenv->getValue(argoff)->copy(args[i]);
			argoff.second++;
		}
	}

	if (func->isVariadic()) {
		ArrayObject* arr = new ArrayObject;

		if ((args_count - nargs) > 0) {
			for (size_t i = nargs; i < args_count; ++i) {
				arr->getData().push_back(args[i]->clone());
			}
		}

		Value* vararg = fenv->getValue(ValueOffset(0, func->getNumArgs()));
		vararg->setObj(CLEVER_ARRAY_TYPE, arr);
	}
}

// Prepares an user function/method call
CLEVER_FORCE_INLINE void VM::prepareCall(const Function* func, Environment* env)
{'fraction_non_alphanumeric': 0.16666666666666666, 'fraction_numerical': 0.003772086559459996, 'mean_word_length': 3.7658467360454115, 'pattern_counts': {'":': 2, '<': 109, '<?xml version=': 0, '>': 71, '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': '6216256', 'n_tokens_mistral': 4079, 'n_tokens_neox': 3859, 'n_words': 848}