<commit_before>/*
** Copyright 2014-2015 Robert Fratto. See the LICENSE.txt file at the top-level
** directory of this distribution.
**
** Licensed under the MIT license <http://opensource.org/licenses/MIT>. This file
** may not be copied, modified, or distributed except according to those terms.
*/
#include <orange/runner.h>
#include <orange/generator.h>
#include <orange/AnyType.h>

#include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/ExecutionEngine/GenericValue.h>
#include <llvm/ExecutionEngine/MCJIT.h>
#include <llvm/ExecutionEngine/Interpreter.h>
#include <llvm/MC/MCObjectFileInfo.h>
#include <llvm/MC/MCContext.h>
#include <llvm/MC/MCAsmInfo.h>
#include <llvm/MC/SubtargetFeature.h>
#include <llvm/CodeGen/AsmPrinter.h>
#include <llvm/CodeGen/Passes.h>
#include <llvm/Support/FormattedStream.h>
#include <llvm/Support/FileSystem.h>
#include <llvm/Support/raw_os_ostream.h>
#include <llvm/Support/TargetRegistry.h>
#include <llvm/Target/TargetLibraryInfo.h>
#include <llvm/Analysis/Lint.h>
#include <llvm/Analysis/CFGPrinter.h>
#include <llvm/Target/TargetMachine.h>
#include <llvm/Transforms/IPO/InlinerPass.h>
#include <llvm/Transforms/IPO.h>
#include <llvm/Transforms/Vectorize.h>
#include <llvm/Transforms/Utils/UnifyFunctionExitNodes.h>
#include <llvm/Analysis/InlineCost.h>
#include <llvm/IR/IRPrintingPasses.h>
#include <llvm/Analysis/Passes.h>

Runner::Runner(std::string pathname) {
	m_pathname = pathname;

	bool added = GeneratingEngine::sharedEngine()->addRunner(this);
	if (added == false) {
		throw std::runtime_error("File cannot be added as an entity twice.");
	}

	m_context = new LLVMContext();
	GeneratingEngine::sharedEngine()->setActive(this);

	// Create the global block
	SymTable *globalSymtab = new SymTable(true);
	m_function = new FunctionStmt("__INTERNAL_main", new AnyType("int"), ParamList(), globalSymtab);
	pushBlock(m_function);

	// Create LLVM stuff; module, builder, etc
	m_module = new Module("orange", context());

	std::string triple = sys::getProcessTriple();

	#ifdef _WIN32
		// This is required to run JIT on windows
		triple += "-elf";
	#endif

	m_module->setTargetTriple(triple);

	m_builder = new IRBuilder<>(context());

	m_functionOptimizer = new FunctionPassManager(m_module);
	m_functionOptimizer->doInitialization();

	GeneratingEngine::sharedEngine()->setActive(nullptr);
}

void Runner::haltRun() {
	m_isRunning = false;
}

bool Runner::hasError() {
	for (auto msg : m_messages) {
		if (msg.type() == ERROR) return true;
	}

	return false;
}

BuildResult Runner::build() {
	// First, set us to running and activate us as the current runner.
	m_isRunning = true;
	GeneratingEngine::sharedEngine()->setActive(this);

	// Try to find our file...
	FILE *file = fopen(pathname().c_str(), "r");
	if (file == nullptr) {
		// Halt our current run and return an error.
		haltRun();

		CompilerMessage msg(NO_FILE, "file " + pathname() + " not found.", pathname(), -1, -1, -1, -1);
		return BuildResult(pathname(), false, msg);
	}

	// Parse the file. get yyin and yyparse and use them
	extern FILE* yyin;
	extern int yyparse();
	extern int yyonce; // used to get the endline
	extern void yyflushbuffer();

	BuildResult result(pathname());
	result.start();

	try {
		yyflushbuffer(); // reset buffer
		yyonce = 0; // reset yyonce
		yyin = file; // give flex the file
		yyparse(); // and do our parse.

		// Now that we've parsed everything, let's analyze and resolve code...
		mainFunction()->resolve();

		if (hasError()) {
			result.finish(false, m_messages);
			return result;
		}

		if (debug())
			std::cout << mainFunction()->string() << std::endl;

		Value *function = mainFunction()->Codegen();

		if (hasError()) {
			result.finish(false, m_messages);
			return result;
		}

		if (debug())
			m_module->dump();

		optimizeModule();

		if (debug())
			m_module->dump();

		// Compile the module here.
		buildModule();

		// Do cleanup.
		fclose(file);
		m_isRunning = false;

		result.finish(hasError() == false, m_messages);
		return result;
	} catch (CompilerMessage& e) {
		std::cerr << "fatal: " << e.what() << std::endl;
		exit(1);
	} catch (std::runtime_error& e) {
		std::cerr << "fatal: " << e.what() << std::endl;
		exit(1);
	}
}

RunResult Runner::run() {
	// First, set us to running and activate us as the current runner.
	m_isRunning = true;
	GeneratingEngine::sharedEngine()->setActive(this);

	// Try to find our file...
	FILE *file = fopen(pathname().c_str(), "r");
	if (file == nullptr) {
		// Halt our current run and return an error.
		haltRun();

		CompilerMessage msg(NO_FILE, "file " + pathname() + " not found.", pathname(), -1, -1, -1, -1);
		return RunResult(pathname(), false, 1, msg);
	}

	// Parse the file. get yyin and yyparse and use them
	extern FILE* yyin;
	extern int yyparse();
	extern int yyonce; // used to get the endline
	extern void yyflushbuffer();

	RunResult result(pathname());
	result.start();

	try {
		yyflushbuffer(); // reset buffer
		yyonce = 0; // reset yyonce
		yyin = file; // give flex the file
		yyparse(); // and do our parse.

		// Now that we've parsed everything, let's analyze and resolve code...
		mainFunction()->resolve();

		if (hasError()) {
			result.finish(false, 1, m_messages);
			return result;
		}

		if (debug())
			std::cout << mainFunction()->string() << std::endl;

		Value *function = mainFunction()->Codegen();

		if (hasError()) {
			result.finish(false, 1, m_messages);
			return result;
		}

		if (debug())
			m_module->dump();

		optimizeModule();

		if (debug())
			m_module->dump();

		int retCode = hasError() == false ? runModule((Function *)function) : 1;

		// Do cleanup.
		fclose(file);
		m_isRunning = false;

		if (debug() && hasError() == false)
			std::cout << "Program returned " << retCode << std::endl;

		bool succeeded = (retCode == 0) && (hasError() == false);

		result.finish(succeeded, retCode, m_messages);
		return result;
	} catch (CompilerMessage& e) {
		std::cerr << "fatal: " << e.what() << std::endl;
		exit(1);
	} catch (std::runtime_error& e) {
		std::cerr << "fatal: " << e.what() << std::endl;
		exit(1);
	}
}

void Runner::buildModule() {
  InitializeNativeTarget();
  InitializeNativeTargetAsmPrinter();

	std::string err = "";
	Triple triple = Triple(m_module->getTargetTriple());

	auto target = TargetRegistry::lookupTarget(m_module->getTargetTriple(), err);
	if (target == nullptr) {
		throw std::runtime_error(err);
	}

	StringRef name = sys::getHostCPUName();
  SubtargetFeatures Features;

	Features.getDefaultSubtargetFeatures(triple);
	std::string FeaturesStr = Features.getString();

	TargetOptions options;

	TargetMachine *tm = target->createTargetMachine(triple.getTriple(), name, FeaturesStr, options, Reloc::Default, CodeModel::Default);

	if (tm == nullptr) {
		throw std::runtime_error("could not create target marchine.");
	}

	std::string loutput = path(pathname()).stem().generic_string();

#if defined(__linux__) || defined(__APPLE__)
		loutput += ".o";
#elif defined(_WIN32)
		loutput += ".obj";
#endif

	std::error_code ec;
	raw_fd_ostream raw(loutput.c_str(), ec, llvm::sys::fs::OpenFlags::F_RW);

	if (ec) {
		throw std::runtime_error(ec.message());
	}

	formatted_raw_ostream strm(raw);

  PassManager* ThePM = new PassManager;
	ThePM->add(new DataLayoutPass());

	bool b = tm->addPassesToEmitFile(*ThePM, strm, LLVMTargetMachine::CGFT_ObjectFile, false);

	if (b == true) {
		throw std::runtime_error("emission is not supported.\n");
	}

	ThePM->run(*m_module);
	strm.flush();
	raw.flush();
	raw.close();

	std::vector<const char *> voptions;

#if defined(__APPLE__) || defined(__linux__)
		std::string root = INSTALL_LOCATION;
		root += "/lib/libor/boot.o";

		voptions.push_back(root.c_str());

#ifdef __APPLE__
		voptions.push_back("-w");
#endif

#ifdef __linux__
		voptions.push_back("-I/lib64/ld-linux-x86-64.so.2");
#endif

		voptions.push_back("-lc");
		voptions.push_back("-o");
		voptions.push_back("a.out");

#elif defined(_WIN32)
		// This code needs some work
		std::string root = "\"";
		root += INSTALL_LOCATION;
		root += "/lib/libor/boot.obj\"";

		voptions.push_back(root.c_str());

		voptions.push_back("-o");
		voptions.push_back("a.exe");
#endif

		voptions.push_back(loutput.c_str());

		invokeLinkerWithOptions(voptions);

		remove(loutput.c_str());
}

int Runner::runModule(Function *function) {
	// MCJIT requires the target & asm printer to be initalized
  InitializeNativeTarget();
  InitializeNativeTargetAsmPrinter();

  // Create our builder & engine to run the function
	EngineBuilder builder((std::unique_ptr<Module>(m_module)));

	std::string error = "";

	ExecutionEngine* engine = builder
		.setErrorStr(&error)
		.setVerifyModules(true)
		.setEngineKind(EngineKind::Kind::JIT)
		.create();

	if (engine == nullptr) {
		std::cerr << "Fatal: could not create engine: " << error << std::endl;
		exit(1);
	}

	// MCJIT requires the engine to be finalized before running it.
	engine->clearAllGlobalMappings();
	engine->finalizeObject();

  std::vector<GenericValue> args;
  return engine->runFunction((Function *)function, args).IntVal.getSExtValue();
}

void Runner::optimizeModule() {
	static PassManager* MPM = nullptr;
	if (MPM == nullptr) {
		MPM = new PassManager;

		MPM->add(createVerifierPass(true));
		MPM->add(createUnreachableBlockEliminationPass());
		MPM->add(createGCLoweringPass());
		MPM->add(createAlwaysInlinerPass());
		MPM->add(createPruneEHPass());
		MPM->add(createIPConstantPropagationPass());
		MPM->add(createIPSCCPPass());
		MPM->add(createMergeFunctionsPass());
		MPM->add(createStripDeadPrototypesPass());
		MPM->add(createConstantPropagationPass());
		MPM->add(createAliasAnalysisCounterPass());
		MPM->add(createBasicAliasAnalysisPass());
		MPM->add(createLazyValueInfoPass());
		MPM->add(createDependenceAnalysisPass());
		MPM->add(createCostModelAnalysisPass());
		MPM->add(createDelinearizationPass());
		MPM->add(createInstCountPass());
		MPM->add(createInstructionCombiningPass());
		MPM->add(createInstructionSimplifierPass());
		MPM->add(createReassociatePass());
		MPM->add(createGVNPass());
		MPM->add(createCFGSimplificationPass());
		MPM->add(createBBVectorizePass());
		MPM->add(createCodeGenPreparePass());
		MPM->add(createGVNPass());
		MPM->add(createSinkingPass());
		MPM->add(createUnifyFunctionExitNodesPass());
		MPM->add(createAggressiveDCEPass());
		MPM->add(createDeadInstEliminationPass());
		MPM->add(createDeadCodeEliminationPass());
		MPM->add(createDeadStoreEliminationPass());
		MPM->add(createGlobalsModRefPass());
		MPM->add(createModuleDebugInfoPrinterPass());
		MPM->add(createArgumentPromotionPass());
		MPM->add(createGlobalOptimizerPass());
		MPM->add(createConstantMergePass());
		MPM->add(createGlobalsModRefPass());
		MPM->add(createPartialInliningPass());
		MPM->add(createFunctionInliningPass());
	}

	MPM->run(*m_module);
	MPM->run(*m_module);
}

void Runner::log(CompilerMessage message) {
	// We only want to log if we're currently running.
	if (m_isRunning == false) return;

	m_messages.push_back(message);
}

std::string Runner::pathname() const {
	return m_pathname;
}

void Runner::pushBlock(Block* block) {
	// We don't want to push anything if it's nullptr.
	if (block == nullptr) return;

	m_blocks.push(block);
}

Block* Runner::popBlock() {
	if (m_blocks.empty()) return nullptr;
	Block* top = m_blocks.top();
	m_blocks.pop();
	return top;
}

Block* Runner::makeBlock() {
	// First, get the top block
	Block* top = topBlock();

	// Create a new block with a new symtable, linked to the top block.
	SymTable *newSymtab = new SymTable(top->symtab());
	Block* newBlock = new Block(new SymTable(newSymtab));

	pushBlock(newBlock);
	return newBlock;
}

Block* Runner::topBlock() const {
	if (m_blocks.empty()) return nullptr;

	return m_blocks.top();
}

SymTable* Runner::symtab() const {
	if (topBlock()) return topBlock()->symtab();
    throw std::runtime_error("No symtab!");
	return nullptr;
}

FunctionStmt* Runner::mainFunction() const {
	return m_function;
}

Module* Runner::module() const {
	return m_module;
}

IRBuilder<>* Runner::builder() const {
	return m_builder;
}

LLVMContext& Runner::context() {
    return getGlobalContext();
	return *m_context;
}

FunctionPassManager* Runner::functionOptimizer() const {
	return m_functionOptimizer;
}<commit_msg>Set the main function to not be name mangled.<commit_after>/*
** Copyright 2014-2015 Robert Fratto. See the LICENSE.txt file at the top-level
** directory of this distribution.
**
** Licensed under the MIT license <http://opensource.org/licenses/MIT>. This file
** may not be copied, modified, or distributed except according to those terms.
*/
#include <orange/runner.h>
#include <orange/generator.h>
#include <orange/AnyType.h>

#include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/ExecutionEngine/GenericValue.h>
#include <llvm/ExecutionEngine/MCJIT.h>
#include <llvm/ExecutionEngine/Interpreter.h>
#include <llvm/MC/MCObjectFileInfo.h>
#include <llvm/MC/MCContext.h>
#include <llvm/MC/MCAsmInfo.h>
#include <llvm/MC/SubtargetFeature.h>
#include <llvm/CodeGen/AsmPrinter.h>
#include <llvm/CodeGen/Passes.h>
#include <llvm/Support/FormattedStream.h>
#include <llvm/Support/FileSystem.h>
#include <llvm/Support/raw_os_ostream.h>
#include <llvm/Support/TargetRegistry.h>
#include <llvm/Target/TargetLibraryInfo.h>
#include <llvm/Analysis/Lint.h>
#include <llvm/Analysis/CFGPrinter.h>
#include <llvm/Target/TargetMachine.h>
#include <llvm/Transforms/IPO/InlinerPass.h>
#include <llvm/Transforms/IPO.h>
#include <llvm/Transforms/Vectorize.h>
#include <llvm/Transforms/Utils/UnifyFunctionExitNodes.h>
#include <llvm/Analysis/InlineCost.h>
#include <llvm/IR/IRPrintingPasses.h>
#include <llvm/Analysis/Passes.h>

Runner::Runner(std::string pathname) {
	m_pathname = pathname;

	bool added = GeneratingEngine::sharedEngine()->addRunner(this);
	if (added == false) {
		throw std::runtime_error("File cannot be added as an entity twice.");
	}

	m_context = new LLVMContext();
	GeneratingEngine::sharedEngine()->setActive(this);

	// Create the global block
	SymTable *globalSymtab = new SymTable(true);
	m_function = new FunctionStmt("__INTERNAL_main", new AnyType("int"), ParamList(), globalSymtab);
	m_function->disableMangle();
	pushBlock(m_function);

	// Create LLVM stuff; module, builder, etc
	m_module = new Module("orange", context());

	std::string triple = sys::getProcessTriple();

	#ifdef _WIN32
		// This is required to run JIT on windows
		triple += "-elf";
	#endif

	m_module->setTargetTriple(triple);

	m_builder = new IRBuilder<>(context());

	m_functionOptimizer = new FunctionPassManager(m_module);
	m_functionOptimizer->doInitialization();

	GeneratingEngine::sharedEngine()->setActive(nullptr);
}

void Runner::haltRun() {
	m_isRunning = false;
}

bool Runner::hasError() {
	for (auto msg : m_messages) {
		if (msg.type() == ERROR) return true;
	}

	return false;
}

BuildResult Runner::build() {
	// First, set us to running and activate us as the current runner.
	m_isRunning = true;
	GeneratingEngine::sharedEngine()->setActive(this);

	// Try to find our file...
	FILE *file = fopen(pathname().c_str(), "r");
	if (file == nullptr) {
		// Halt our current run and return an error.
		haltRun();

		CompilerMessage msg(NO_FILE, "file " + pathname() + " not found.", pathname(), -1, -1, -1, -1);
		return BuildResult(pathname(), false, msg);
	}

	// Parse the file. get yyin and yyparse and use them
	extern FILE* yyin;
	extern int yyparse();
	extern int yyonce; // used to get the endline
	extern void yyflushbuffer();

	BuildResult result(pathname());
	result.start();

	try {
		yyflushbuffer(); // reset buffer
		yyonce = 0; // reset yyonce
		yyin = file; // give flex the file
		yyparse(); // and do our parse.

		// Now that we've parsed everything, let's analyze and resolve code...
		mainFunction()->resolve();

		if (hasError()) {
			result.finish(false, m_messages);
			return result;
		}

		if (debug())
			std::cout << mainFunction()->string() << std::endl;

		Value *function = mainFunction()->Codegen();

		if (hasError()) {
			result.finish(false, m_messages);
			return result;
		}

		if (debug())
			m_module->dump();

		optimizeModule();

		if (debug())
			m_module->dump();

		// Compile the module here.
		buildModule();

		// Do cleanup.
		fclose(file);
		m_isRunning = false;

		result.finish(hasError() == false, m_messages);
		return result;
	} catch (CompilerMessage& e) {
		std::cerr << "fatal: " << e.what() << std::endl;
		exit(1);
	} catch (std::runtime_error& e) {
		std::cerr << "fatal: " << e.what() << std::endl;
		exit(1);
	}
}

RunResult Runner::run() {
	// First, set us to running and activate us as the current runner.
	m_isRunning = true;
	GeneratingEngine::sharedEngine()->setActive(this);

	// Try to find our file...
	FILE *file = fopen(pathname().c_str(), "r");
	if (file == nullptr) {
		// Halt our current run and return an error.
		haltRun();

		CompilerMessage msg(NO_FILE, "file " + pathname() + " not found.", pathname(), -1, -1, -1, -1);
		return RunResult(pathname(), false, 1, msg);
	}

	// Parse the file. get yyin and yyparse and use them
	extern FILE* yyin;
	extern int yyparse();
	extern int yyonce; // used to get the endline
	extern void yyflushbuffer();

	RunResult result(pathname());
	result.start();

	try {
		yyflushbuffer(); // reset buffer
		yyonce = 0; // reset yyonce
		yyin = file; // give flex the file
		yyparse(); // and do our parse.

		// Now that we've parsed everything, let's analyze and resolve code...
		mainFunction()->resolve();

		if (hasError()) {
			result.finish(false, 1, m_messages);
			return result;
		}

		if (debug())
			std::cout << mainFunction()->string() << std::endl;

		Value *function = mainFunction()->Codegen();

		if (hasError()) {
			result.finish(false, 1, m_messages);
			return result;
		}

		if (debug())
			m_module->dump();

		optimizeModule();

		if (debug())
			m_module->dump();

		int retCode = hasError() == false ? runModule((Function *)function) : 1;

		// Do cleanup.
		fclose(file);
		m_isRunning = false;

		if (debug() && hasError() == false)
			std::cout << "Program returned " << retCode << std::endl;

		bool succeeded = (retCode == 0) && (hasError() == false);

		result.finish(succeeded, retCode, m_messages);
		return result;
	} catch (CompilerMessage& e) {
		std::cerr << "fatal: " << e.what() << std::endl;
		exit(1);
	} catch (std::runtime_error& e) {
		std::cerr << "fatal: " << e.what() << std::endl;
		exit(1);
	}
}

void Runner::buildModule() {
  InitializeNativeTarget();
  InitializeNativeTargetAsmPrinter();

	std::string err = "";
	Triple triple = Triple(m_module->getTargetTriple());

	auto target = TargetRegistry::lookupTarget(m_module->getTargetTriple(), err);
	if (target == nullptr) {
		throw std::runtime_error(err);
	}

	StringRef name = sys::getHostCPUName();
  SubtargetFeatures Features;

	Features.getDefaultSubtargetFeatures(triple);
	std::string FeaturesStr = Features.getString();

	TargetOptions options;

	TargetMachine *tm = target->createTargetMachine(triple.getTriple(), name, FeaturesStr, options, Reloc::Default, CodeModel::Default);

	if (tm == nullptr) {
		throw std::runtime_error("could not create target marchine.");
	}

	std::string loutput = path(pathname()).stem().generic_string();

#if defined(__linux__) || defined(__APPLE__)
		loutput += ".o";
#elif defined(_WIN32)
		loutput += ".obj";
#endif

	std::error_code ec;
	raw_fd_ostream raw(loutput.c_str(), ec, llvm::sys::fs::OpenFlags::F_RW);

	if (ec) {
		throw std::runtime_error(ec.message());
	}

	formatted_raw_ostream strm(raw);

  PassManager* ThePM = new PassManager;
	ThePM->add(new DataLayoutPass());

	bool b = tm->addPassesToEmitFile(*ThePM, strm, LLVMTargetMachine::CGFT_ObjectFile, false);

	if (b == true) {
		throw std::runtime_error("emission is not supported.\n");
	}

	ThePM->run(*m_module);
	strm.flush();
	raw.flush();
	raw.close();

	std::vector<const char *> voptions;

#if defined(__APPLE__) || defined(__linux__)
		std::string root = INSTALL_LOCATION;
		root += "/lib/libor/boot.o";

		voptions.push_back(root.c_str());

#ifdef __APPLE__
		voptions.push_back("-w");
#endif

#ifdef __linux__
		voptions.push_back("-I/lib64/ld-linux-x86-64.so.2");
#endif

		voptions.push_back("-lc");
		voptions.push_back("-o");
		voptions.push_back("a.out");

#elif defined(_WIN32)
		// This code needs some work
		std::string root = "\"";
		root += INSTALL_LOCATION;
		root += "/lib/libor/boot.obj\"";

		voptions.push_back(root.c_str());

		voptions.push_back("-o");
		voptions.push_back("a.exe");
#endif

		voptions.push_back(loutput.c_str());

		invokeLinkerWithOptions(voptions);

		remove(loutput.c_str());
}

int Runner::runModule(Function *function) {
	// MCJIT requires the target & asm printer to be initalized
  InitializeNativeTarget();
  InitializeNativeTargetAsmPrinter();

  // Create our builder & engine to run the function
	EngineBuilder builder((std::unique_ptr<Module>(m_module)));

	std::string error = "";

	ExecutionEngine* engine = builder
		.setErrorStr(&error)
		.setVerifyModules(true)
		.setEngineKind(EngineKind::Kind::JIT)
		.create();

	if (engine == nullptr) {
		std::cerr << "Fatal: could not create engine: " << error << std::endl;
		exit(1);
	}

	// MCJIT requires the engine to be finalized before running it.
	engine->clearAllGlobalMappings();
	engine->finalizeObject();

  std::vector<GenericValue> args;
  return engine->runFunction((Function *)function, args).IntVal.getSExtValue();
}

void Runner::optimizeModule() {
	static PassManager* MPM = nullptr;
	if (MPM == nullptr) {
		MPM = new PassManager;

		MPM->add(createVerifierPass(true));
		MPM->add(createUnreachableBlockEliminationPass());
		MPM->add(createGCLoweringPass());
		MPM->add(createAlwaysInlinerPass());
		MPM->add(createPruneEHPass());
		MPM->add(createIPConstantPropagationPass());
		MPM->add(createIPSCCPPass());
		MPM->add(createMergeFunctionsPass());
		MPM->add(createStripDeadPrototypesPass());
		MPM->add(createConstantPropagationPass());
		MPM->add(createAliasAnalysisCounterPass());
		MPM->add(createBasicAliasAnalysisPass());
		MPM->add(createLazyValueInfoPass());
		MPM->add(createDependenceAnalysisPass());
		MPM->add(createCostModelAnalysisPass());
		MPM->add(createDelinearizationPass());
		MPM->add(createInstCountPass());
		MPM->add(createInstructionCombiningPass());
		MPM->add(createInstructionSimplifierPass());
		MPM->add(createReassociatePass());
		MPM->add(createGVNPass());
		MPM->add(createCFGSimplificationPass());
		MPM->add(createBBVectorizePass());
		MPM->add(createCodeGenPreparePass());
		MPM->add(createGVNPass());
		MPM->add(createSinkingPass());
		MPM->add(createUnifyFunctionExitNodesPass());
		MPM->add(createAggressiveDCEPass());
		MPM->add(createDeadInstEliminationPass());
		MPM->add(createDeadCodeEliminationPass());
		MPM->add(createDeadStoreEliminationPass());
		MPM->add(createGlobalsModRefPass());
		MPM->add(createModuleDebugInfoPrinterPass());
		MPM->add(createArgumentPromotionPass());
		MPM->add(createGlobalOptimizerPass());
		MPM->add(createConstantMergePass());
		MPM->add(createGlobalsModRefPass());
		MPM->add(createPartialInliningPass());
		MPM->add(createFunctionInliningPass());
	}

	MPM->run(*m_module);
	MPM->run(*m_module);
}

void Runner::log(CompilerMessage message) {
	// We only want to log if we're currently running.
	if (m_isRunning == false) return;

	m_messages.push_back(message);
}

std::string Runner::pathname() const {
	return m_pathname;
}

void Runner::pushBlock(Block* block) {
	// We don't want to push anything if it's nullptr.
	if (block == nullptr) return;

	m_blocks.push(block);
}

Block* Runner::popBlock() {
	if (m_blocks.empty()) return nullptr;
	Block* top = m_blocks.top();
	m_blocks.pop();
	return top;
}

Block* Runner::makeBlock() {
	// First, get the top block
	Block* top = topBlock();

	// Create a new block with a new symtable, linked to the top block.
	SymTable *newSymtab = new SymTable(top->symtab());
	Block* newBlock = new Block(new SymTable(newSymtab));

	pushBlock(newBlock);
	return newBlock;
}

Block* Runner::topBlock() const {
	if (m_blocks.empty()) return nullptr;

	return m_blocks.top();
}

SymTable* Runner::symtab() const {
	if (topBlock()) return topBlock()->symtab();
    throw std::runtime_error("No symtab!");
	return nullptr;
}

FunctionStmt* Runner::mainFunction() const {
	return m_function;
}

Module* Runner::module() const {
	return m_module;
}

IRBuilder<>* Runner::builder() const {
	return m_builder;
}

LLVMContext& Runner::context() {
    return getGlobalContext();
	return *m_context;
}

FunctionPassManager* Runner::functionOptimizer() const {
	return m_functionOptimizer;
}<|endoftext|>


<commit_before>/* Copyright 2015 CyberTech Labs Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. */

#include "configurer.h"

#include <functional>

#include <QtCore/qglobal.h>
#include <QtXml/QDomElement>

#include "exceptions/malformedConfigException.h"
#include "fileUtils.h"

using QDomNamedNodeMapLengthType = int;
using namespace trikKernel;

Configurer::Configurer(const QString &systemConfigFileName, const QString &modelConfigFileName)
{
	const QDomElement systemConfig = trikKernel::FileUtils::readXmlFile(systemConfigFileName);
	const QDomElement modelConfig = trikKernel::FileUtils::readXmlFile(modelConfigFileName);

	auto parseSection = [&systemConfig](const QString &sectionName, const std::function<void(const QDomElement &)> &action) {
		const QDomNodeList section = systemConfig.elementsByTagName(sectionName);
		if (section.size() != 1) {
			throw MalformedConfigException("'" + sectionName + "' element shall appear exactly once in config");
		}

		action(section.at(0).toElement());
	};

	if (systemConfig.tagName() != "config") {
		throw MalformedConfigException("'config' tag shall be the root attribute of system config");
	}

	mVersion = systemConfig.attribute("version", "");

	parseSection("deviceClasses", [this](const QDomElement &element) { parseDeviceClasses(element); });
	parseSection("devicePorts", [this](const QDomElement &element) { parseDevicePorts(element); });
	parseSection("deviceTypes", [this](const QDomElement &element) { parseDeviceTypes(element); });

	parseSection("initScript", [this](const QDomElement &element) { parseInitScript(element); });

	parseAdditionalConfigurations(systemConfig);

	parseModelConfig(modelConfig);
}

QString Configurer::attributeByDevice(const QString &deviceClass, const QString &attributeName) const
{
	if (mAdditionalModelConfiguration.contains(deviceClass)
			&& mAdditionalModelConfiguration[deviceClass].attributes.contains(attributeName))
	{
		return mAdditionalModelConfiguration[deviceClass].attributes[attributeName];
	}

	if (mAdditionalConfiguration.contains(deviceClass)
			&& mAdditionalConfiguration[deviceClass].attributes.contains(attributeName))
	{
		return mAdditionalConfiguration[deviceClass].attributes[attributeName];
	}

	if (mDevices.contains(deviceClass) && mDevices[deviceClass].attributes.contains(attributeName)) {
		return mDevices[deviceClass].attributes[attributeName];
	}

	throw MalformedConfigException(
				QString("Unknown attribute '%1' of device '%2'").arg(attributeName).arg(deviceClass));
}

QString Configurer::attributeByPort(const QString &port, const QString &attributeName) const
{
	if (!mModelConfiguration.contains(port)) {
		throw MalformedConfigException(QString("Port '%1' is not configured").arg(port));
	}

	if (mModelConfiguration[port].attributes.contains(attributeName)) {
		return mModelConfiguration[port].attributes[attributeName];
	}

	const QString &deviceType = mModelConfiguration.value(port).deviceType;

	if (mDeviceTypes.contains(deviceType)) {
		if (mDeviceTypes[deviceType].attributes.contains(attributeName)) {
			return mDeviceTypes[deviceType].attributes[attributeName];
		}

		const QString deviceClass = mDeviceTypes[deviceType].deviceClass;
		if (mDevices.contains(deviceClass)) {
			const Device &device = mDevices[deviceClass];
			if (device.portSpecificAttributes.contains(port)) {
				if (device.portSpecificAttributes[port].contains(attributeName)) {
					return device.portSpecificAttributes[port][attributeName];
				}
			}

			if (device.attributes.contains(attributeName)) {
				return device.attributes[attributeName];
			}

			if (!device.portSpecificAttributes.contains(port)) {
				throw MalformedConfigException(QString("Device type '%1' is not allowed on port %2.")
						.arg(deviceType).arg(port));
			}
		} else {
			throw MalformedConfigException(
					QString("Device type '%1' has device class '%2' which is not listed in 'deviceClasses' section.")
							.arg(deviceType).arg(deviceClass));
		}
	}

	if (mDevices.contains(deviceType)) {
		const Device &device = mDevices[deviceType];
		if (device.portSpecificAttributes.contains(port)) {
			if (device.portSpecificAttributes[port].contains(attributeName)) {
				return device.portSpecificAttributes[port][attributeName];
			}
		}

		if (device.attributes.contains(attributeName)) {
			return device.attributes[attributeName];
		}
	}

	throw MalformedConfigException(QString("Unknown attribute '%1' of device '%2' on port '%3'")
			.arg(attributeName).arg(deviceType).arg(port));
}

bool Configurer::isEnabled(const QString &deviceName) const
{
	if (mAdditionalModelConfiguration.contains(deviceName)) {
		return true;
	}

	if (mDevices.contains(deviceName) && !mDevices.value(deviceName).isOptional) {
		return true;
	}

	return false;
}

QStringList Configurer::ports() const
{
	return mModelConfiguration.keys();
}

QString Configurer::deviceClass(const QString &port) const
{
	if (!mModelConfiguration.contains(port)) {
		throw MalformedConfigException(QString("Port '%1' is not configured").arg(port));
	}

	const QString &deviceType = mModelConfiguration.value(port).deviceType;
	if (mDeviceTypes.contains(deviceType)) {
		return mDeviceTypes.value(deviceType).deviceClass;
	}

	if (!mDevices.contains(deviceType)) {
		throw MalformedConfigException(QString("Port '%1' is configured to use unknown device class '%2'")
				.arg(port)
				.arg(deviceType));
	}

	return deviceType;
}

QStringList Configurer::initScripts() const
{
	return mInitScripts;
}

void Configurer::configure(const QString &portName, const QString &deviceName)
{
	mModelConfiguration[portName] = { portName, deviceName, {}};
}

QString Configurer::version() const
{
	return mVersion;
}

void Configurer::parseDeviceClasses(const QDomElement &element)
{
	const QDomNodeList deviceClasses = element.childNodes();
	for (int i = 0; i < deviceClasses.size(); ++i) {
		const QDomElement deviceNode = deviceClasses.item(i).toElement();
		if (!deviceNode.isNull()) {
			Device device;
			device.name = deviceNode.tagName();
			device.isOptional = deviceNode.attribute("optional", "false") == "true";
			const QDomNamedNodeMap &attributes = deviceNode.attributes();
			for (QDomNamedNodeMapLengthType j = 0; j < attributes.length(); ++j) {
				const QDomAttr &attribute = attributes.item(j).toAttr();
				device.attributes.insert(attribute.name(), attribute.value());
			}

			mDevices.insert(device.name, device);
		}
	}
}

void Configurer::parseDevicePorts(const QDomElement &element)
{
	const QDomNodeList devicePorts = element.childNodes();
	for (int i = 0; i < devicePorts.size(); ++i) {
		const QDomElement devicePortNode = devicePorts.item(i).toElement();
		if (!devicePortNode.isNull()) {
			const QString deviceName = devicePortNode.tagName();
			if (!mDevices.contains(deviceName)) {
				throw MalformedConfigException("Device is not listed in 'DeviceClasses' section", devicePortNode);
			}

			const QString port = devicePortNode.attribute("port");
			if (port.isEmpty()) {
				throw MalformedConfigException("Port map shall have non-empty 'port' attribute", devicePortNode);
			}

			const QDomNamedNodeMap &attributes = devicePortNode.attributes();
			for (QDomNamedNodeMapLengthType j = 0; j < attributes.length(); ++j) {
				const QDomAttr &attribute = attributes.item(j).toAttr();
				mDevices[deviceName].portSpecificAttributes[port].insert(attribute.name(), attribute.value());
			}
		}
	}
}

void Configurer::parseDeviceTypes(const QDomElement &element)
{
	const QDomNodeList deviceTypes = element.childNodes();
	for (int i = 0; i < deviceTypes.size(); ++i) {
		const QDomElement deviceTypeNode = deviceTypes.item(i).toElement();
		if (!deviceTypeNode.isNull()) {
			DeviceType deviceType;
			deviceType.name = deviceTypeNode.tagName();
			deviceType.deviceClass = deviceTypeNode.attribute("class");
			if (deviceType.deviceClass.isEmpty()) {
				throw MalformedConfigException("Device type shall have 'class' attribute", deviceTypeNode);
			}

			if (!mDevices.contains(deviceType.deviceClass)) {
				throw MalformedConfigException("Device is not listed in 'DeviceClasses' section", deviceTypeNode);
			}

			const QDomNamedNodeMap &attributes = deviceTypeNode.attributes();
			for (QDomNamedNodeMapLengthType j = 0; j < attributes.length(); ++j) {
				const QDomAttr &attribute = attributes.item(j).toAttr();
				deviceType.attributes.insert(attribute.name(), attribute.value());
			}

			mDeviceTypes.insert(deviceType.name, deviceType);
		}
	}
}

void Configurer::parseInitScript(const QDomElement &element)
{
	mInitScripts.append(element.text());
}

void Configurer::parseAdditionalConfigurations(const QDomElement &element)
{
	const QDomNodeList tags = element.childNodes();
	for (int i = 0; i < tags.size(); ++i) {
		const QDomElement tag = tags.item(i).toElement();
		if (!tag.isNull()) {
			if (tag.tagName() == "initScript"
					|| tag.tagName() == "deviceClasses"
					|| tag.tagName() == "devicePorts"
					|| tag.tagName() == "deviceTypes")
			{
				continue;
			}

			Device additionalDevice;
			additionalDevice.name = tag.tagName();
			const QDomNamedNodeMap &attributes = tag.attributes();
			for (QDomNamedNodeMapLengthType j = 0; j < attributes.length(); ++j) {
				const QDomAttr &attribute = attributes.item(j).toAttr();
				additionalDevice.attributes.insert(attribute.name(), attribute.value());
			}

			mAdditionalConfiguration.insert(additionalDevice.name, additionalDevice);
		}
	}
}

void Configurer::parseModelConfig(const QDomElement &element)
{
	const QDomNodeList tags = element.childNodes();
	for (int i = 0; i < tags.size(); ++i) {
		const QDomElement tag = tags.item(i).toElement();
		if (!tag.isNull()) {
			if (tag.tagName() == "initScript") {
				parseInitScript(tag);
			} else if (tag.hasChildNodes()) {
				ModelConfigurationElement port;
				port.port = tag.tagName();
				const QDomNodeList devices = tag.childNodes();
				if (devices.count() > 1) {
					throw MalformedConfigException("Only one device can be configured on a port", tag);
				}

				const QDomElement device = devices.item(0).toElement();
				if (!device.isNull()) {
					port.deviceType = device.tagName();
					const QDomNamedNodeMap &attributes = device.attributes();
					for (QDomNamedNodeMapLengthType j = 0; j < attributes.length(); ++j) {
						const QDomAttr &attribute = attributes.item(j).toAttr();
						port.attributes.insert(attribute.name(), attribute.value());
					}

					mModelConfiguration.insert(port.port, port);
				}
			} else {
				AdditionalModelConfigurationElement e;
				e.deviceClass = tag.tagName();
				if (!mDevices.contains(e.deviceClass)) {
					throw MalformedConfigException(
							"Device shall be listed in 'deviceClasses' section in system config", tag);
				}

				if (tag.attribute("disabled", "false") == "false") {
					const QDomNamedNodeMap &attributes = tag.attributes();
					for (QDomNamedNodeMapLengthType j = 0; j < attributes.length(); ++j) {
						const QDomAttr &attribute = attributes.item(j).toAttr();
						e.attributes.insert(attribute.name(), attribute.value());
					}

					mAdditionalModelConfiguration.insert(e.deviceClass, e);
				}
			}
		}
	}
}
<commit_msg>Fix Vera++<commit_after>/* Copyright 2015 CyberTech Labs Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. */

#include "configurer.h"

#include <functional>

#include <QtCore/qglobal.h>
#include <QtXml/QDomElement>

#include "exceptions/malformedConfigException.h"
#include "fileUtils.h"

using QDomNamedNodeMapLengthType = int;
using namespace trikKernel;

Configurer::Configurer(const QString &systemConfigFileName, const QString &modelConfigFileName)
{
	const QDomElement systemConfig = trikKernel::FileUtils::readXmlFile(systemConfigFileName);
	const QDomElement modelConfig = trikKernel::FileUtils::readXmlFile(modelConfigFileName);

	auto parseSection = [&systemConfig](const QString &sectionName
			, const std::function<void(const QDomElement &)> &action) {
		const QDomNodeList section = systemConfig.elementsByTagName(sectionName);
		if (section.size() != 1) {
			throw MalformedConfigException("'" + sectionName + "' element shall appear exactly once in config");
		}

		action(section.at(0).toElement());
	};

	if (systemConfig.tagName() != "config") {
		throw MalformedConfigException("'config' tag shall be the root attribute of system config");
	}

	mVersion = systemConfig.attribute("version", "");

	parseSection("deviceClasses", [this](const QDomElement &element) { parseDeviceClasses(element); });
	parseSection("devicePorts", [this](const QDomElement &element) { parseDevicePorts(element); });
	parseSection("deviceTypes", [this](const QDomElement &element) { parseDeviceTypes(element); });

	parseSection("initScript", [this](const QDomElement &element) { parseInitScript(element); });

	parseAdditionalConfigurations(systemConfig);

	parseModelConfig(modelConfig);
}

QString Configurer::attributeByDevice(const QString &deviceClass, const QString &attributeName) const
{
	if (mAdditionalModelConfiguration.contains(deviceClass)
			&& mAdditionalModelConfiguration[deviceClass].attributes.contains(attributeName))
	{
		return mAdditionalModelConfiguration[deviceClass].attributes[attributeName];
	}

	if (mAdditionalConfiguration.contains(deviceClass)
			&& mAdditionalConfiguration[deviceClass].attributes.contains(attributeName))
	{
		return mAdditionalConfiguration[deviceClass].attributes[attributeName];
	}

	if (mDevices.contains(deviceClass) && mDevices[deviceClass].attributes.contains(attributeName)) {
		return mDevices[deviceClass].attributes[attributeName];
	}

	throw MalformedConfigException(
				QString("Unknown attribute '%1' of device '%2'").arg(attributeName).arg(deviceClass));
}

QString Configurer::attributeByPort(const QString &port, const QString &attributeName) const
{
	if (!mModelConfiguration.contains(port)) {
		throw MalformedConfigException(QString("Port '%1' is not configured").arg(port));
	}

	if (mModelConfiguration[port].attributes.contains(attributeName)) {
		return mModelConfiguration[port].attributes[attributeName];
	}

	const QString &deviceType = mModelConfiguration.value(port).deviceType;

	if (mDeviceTypes.contains(deviceType)) {
		if (mDeviceTypes[deviceType].attributes.contains(attributeName)) {
			return mDeviceTypes[deviceType].attributes[attributeName];
		}

		const QString deviceClass = mDeviceTypes[deviceType].deviceClass;
		if (mDevices.contains(deviceClass)) {
			const Device &device = mDevices[deviceClass];
			if (device.portSpecificAttributes.contains(port)) {
				if (device.portSpecificAttributes[port].contains(attributeName)) {
					return device.portSpecificAttributes[port][attributeName];
				}
			}

			if (device.attributes.contains(attributeName)) {
				return device.attributes[attributeName];
			}

			if (!device.portSpecificAttributes.contains(port)) {
				throw MalformedConfigException(QString("Device type '%1' is not allowed on port %2.")
						.arg(deviceType).arg(port));
			}
		} else {
			throw MalformedConfigException(
					QString("Device type '%1' has device class '%2' which is not listed in 'deviceClasses' section.")
							.arg(deviceType).arg(deviceClass));
		}
	}

	if (mDevices.contains(deviceType)) {
		const Device &device = mDevices[deviceType];
		if (device.portSpecificAttributes.contains(port)) {
			if (device.portSpecificAttributes[port].contains(attributeName)) {
				return device.portSpecificAttributes[port][attributeName];
			}
		}

		if (device.attributes.contains(attributeName)) {
			return device.attributes[attributeName];
		}
	}

	throw MalformedConfigException(QString("Unknown attribute '%1' of device '%2' on port '%3'")
			.arg(attributeName).arg(deviceType).arg(port));
}

bool Configurer::isEnabled(const QString &deviceName) const
{
	if (mAdditionalModelConfiguration.contains(deviceName)) {
		return true;
	}

	if (mDevices.contains(deviceName) && !mDevices.value(deviceName).isOptional) {
		return true;
	}

	return false;
}

QStringList Configurer::ports() const
{
	return mModelConfiguration.keys();
}

QString Configurer::deviceClass(const QString &port) const
{
	if (!mModelConfiguration.contains(port)) {
		throw MalformedConfigException(QString("Port '%1' is not configured").arg(port));
	}

	const QString &deviceType = mModelConfiguration.value(port).deviceType;
	if (mDeviceTypes.contains(deviceType)) {
		return mDeviceTypes.value(deviceType).deviceClass;
	}

	if (!mDevices.contains(deviceType)) {
		throw MalformedConfigException(QString("Port '%1' is configured to use unknown device class '%2'")
				.arg(port)
				.arg(deviceType));
	}

	return deviceType;
}

QStringList Configurer::initScripts() const
{
	return mInitScripts;
}

void Configurer::configure(const QString &portName, const QString &deviceName)
{
	mModelConfiguration[portName] = { portName, deviceName, {}};
}

QString Configurer::version() const
{
	return mVersion;
}

void Configurer::parseDeviceClasses(const QDomElement &element)
{
	const QDomNodeList deviceClasses = element.childNodes();
	for (int i = 0; i < deviceClasses.size(); ++i) {
		const QDomElement deviceNode = deviceClasses.item(i).toElement();
		if (!deviceNode.isNull()) {
			Device device;
			device.name = deviceNode.tagName();
			device.isOptional = deviceNode.attribute("optional", "false") == "true";
			const QDomNamedNodeMap &attributes = deviceNode.attributes();
			for (QDomNamedNodeMapLengthType j = 0; j < attributes.length(); ++j) {
				const QDomAttr &attribute = attributes.item(j).toAttr();
				device.attributes.insert(attribute.name(), attribute.value());
			}

			mDevices.insert(device.name, device);
		}
	}
}

void Configurer::parseDevicePorts(const QDomElement &element)
{
	const QDomNodeList devicePorts = element.childNodes();
	for (int i = 0; i < devicePorts.size(); ++i) {
		const QDomElement devicePortNode = devicePorts.item(i).toElement();
		if (!devicePortNode.isNull()) {
			const QString deviceName = devicePortNode.tagName();
			if (!mDevices.contains(deviceName)) {
				throw MalformedConfigException("Device is not listed in 'DeviceClasses' section", devicePortNode);
			}

			const QString port = devicePortNode.attribute("port");
			if (port.isEmpty()) {
				throw MalformedConfigException("Port map shall have non-empty 'port' attribute", devicePortNode);
			}

			const QDomNamedNodeMap &attributes = devicePortNode.attributes();
			for (QDomNamedNodeMapLengthType j = 0; j < attributes.length(); ++j) {
				const QDomAttr &attribute = attributes.item(j).toAttr();
				mDevices[deviceName].portSpecificAttributes[port].insert(attribute.name(), attribute.value());
			}
		}
	}
}

void Configurer::parseDeviceTypes(const QDomElement &element)
{
	const QDomNodeList deviceTypes = element.childNodes();
	for (int i = 0; i < deviceTypes.size(); ++i) {
		const QDomElement deviceTypeNode = deviceTypes.item(i).toElement();
		if (!deviceTypeNode.isNull()) {
			DeviceType deviceType;
			deviceType.name = deviceTypeNode.tagName();
			deviceType.deviceClass = deviceTypeNode.attribute("class");
			if (deviceType.deviceClass.isEmpty()) {
				throw MalformedConfigException("Device type shall have 'class' attribute", deviceTypeNode);
			}

			if (!mDevices.contains(deviceType.deviceClass)) {
				throw MalformedConfigException("Device is not listed in 'DeviceClasses' section", deviceTypeNode);
			}

			const QDomNamedNodeMap &attributes = deviceTypeNode.attributes();
			for (QDomNamedNodeMapLengthType j = 0; j < attributes.length(); ++j) {
				const QDomAttr &attribute = attributes.item(j).toAttr();
				deviceType.attributes.insert(attribute.name(), attribute.value());
			}

			mDeviceTypes.insert(deviceType.name, deviceType);
		}
	}
}

void Configurer::parseInitScript(const QDomElement &element)
{
	mInitScripts.append(element.text());
}

void Configurer::parseAdditionalConfigurations(const QDomElement &element)
{
	const QDomNodeList tags = element.childNodes();
	for (int i = 0; i < tags.size(); ++i) {
		const QDomElement tag = tags.item(i).toElement();
		if (!tag.isNull()) {
			if (tag.tagName() == "initScript"
					|| tag.tagName() == "deviceClasses"
					|| tag.tagName() == "devicePorts"
					|| tag.tagName() == "deviceTypes")
			{
				continue;
			}

			Device additionalDevice;
			additionalDevice.name = tag.tagName();
			const QDomNamedNodeMap &attributes = tag.attributes();
			for (QDomNamedNodeMapLengthType j = 0; j < attributes.length(); ++j) {
				const QDomAttr &attribute = attributes.item(j).toAttr();
				additionalDevice.attributes.insert(attribute.name(), attribute.value());
			}

			mAdditionalConfiguration.insert(additionalDevice.name, additionalDevice);
		}
	}
}

void Configurer::parseModelConfig(const QDomElement &element)
{
	const QDomNodeList tags = element.childNodes();
	for (int i = 0; i < tags.size(); ++i) {
		const QDomElement tag = tags.item(i).toElement();
		if (!tag.isNull()) {
			if (tag.tagName() == "initScript") {
				parseInitScript(tag);
			} else if (tag.hasChildNodes()) {
				ModelConfigurationElement port;
				port.port = tag.tagName();
				const QDomNodeList devices = tag.childNodes();
				if (devices.count() > 1) {
					throw MalformedConfigException("Only one device can be configured on a port", tag);
				}

				const QDomElement device = devices.item(0).toElement();
				if (!device.isNull()) {
					port.deviceType = device.tagName();
					const QDomNamedNodeMap &attributes = device.attributes();
					for (QDomNamedNodeMapLengthType j = 0; j < attributes.length(); ++j) {
						const QDomAttr &attribute = attributes.item(j).toAttr();
						port.attributes.insert(attribute.name(), attribute.value());
					}

					mModelConfiguration.insert(port.port, port);
				}
			} else {
				AdditionalModelConfigurationElement e;
				e.deviceClass = tag.tagName();
				if (!mDevices.contains(e.deviceClass)) {
					throw MalformedConfigException(
							"Device shall be listed in 'deviceClasses' section in system config", tag);
				}

				if (tag.attribute("disabled", "false") == "false") {
					const QDomNamedNodeMap &attributes = tag.attributes();
					for (QDomNamedNodeMapLengthType j = 0; j < attributes.length(); ++j) {
						const QDomAttr &attribute = attributes.item(j).toAttr();
						e.attributes.insert(attribute.name(), attribute.value());
					}

					mAdditionalModelConfiguration.insert(e.deviceClass, e);
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>#pragma once

namespace gcl::mp::value_traits
{
    template <auto... values>
    constexpr static auto equal_v = []() consteval
    {
        static_assert(sizeof...(values) > 0, "gcl::mp::value_traits::equal_v : no arguments");
        constexpr auto first_value = std::get<0>(std::tuple{values...});
        return ((values == first_value) && ...);
    }
    ();
    template <auto... values>
    constexpr static auto not_equal_v = not equal_v<values...>;
}

namespace gcl::mp::value_traits::tests::equal
{
    static_assert(equal_v<true>);
    static_assert(equal_v<true, true>);
    static_assert(equal_v<true, true, true>);
    static_assert(equal_v<true, true, true, true>);

    static_assert(not not_equal_v<true>);
    static_assert(not_equal_v<true, false>);
    static_assert(not_equal_v<false, true>);
    static_assert(not_equal_v<false, true, true>);
    static_assert(not_equal_v<true, false, true>);
    static_assert(not_equal_v<true, true, false>);
    static_assert(not_equal_v<false, true, true, true>);
    static_assert(not_equal_v<true, false, true, true>);
    static_assert(not_equal_v<true, true, false, true>);
    static_assert(not_equal_v<true, true, true, false>);
}<commit_msg>[mp::value_traits] : equal_v : ensure all types are comparable<commit_after>#pragma once

#include <concepts>

namespace gcl::mp::value_traits
{
    template <auto... values>
    constexpr static auto equal_v = []() consteval
    {
        static_assert(sizeof...(values) > 0, "gcl::mp::value_traits::equal_v : no arguments");
        constexpr auto first_value = std::get<0>(std::tuple{values...});
        static_assert(
            (std::equality_comparable_with<decltype(values), decltype(first_value)> && ...),
            "gcl::mp::value_traits::equal_v : cannot compare values");
        return ((values == first_value) && ...);
    }
    ();
    template <auto... values>
    constexpr static auto not_equal_v = not equal_v<values...>;
}

namespace gcl::mp::value_traits::tests::equal
{
    static_assert(equal_v<true>);
    static_assert(equal_v<true, true>);
    static_assert(equal_v<true, true, true>);
    static_assert(equal_v<true, true, true, true>);

    static_assert(not not_equal_v<true>);
    static_assert(not_equal_v<true, false>);
    static_assert(not_equal_v<false, true>);
    static_assert(not_equal_v<false, true, true>);
    static_assert(not_equal_v<true, false, true>);
    static_assert(not_equal_v<true, true, false>);
    static_assert(not_equal_v<false, true, true, true>);
    static_assert(not_equal_v<true, false, true, true>);
    static_assert(not_equal_v<true, true, false, true>);
    static_assert(not_equal_v<true, true, true, false>);
}<|endoftext|>


<commit_before>#include "thread.h"

// C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// v8
#include <v8.h>
using namespace v8;

// custom source
#include "file_manager.h"
#include "json.h"
#include "utilities.h"
#include "callback_queue.h"
#include "nrequire.h"

// file loader and hash (npool.cc)
static FileManager *fileManager = &(FileManager::GetInstance());

// callback queue
static CallbackQueue *callbackQueue = &(CallbackQueue::GetInstance());

typedef struct THREAD_CONTEXT_STRUCT
{
    // libuv
    uv_async_t*         uvAsync;

    // v8
    Isolate*            threadIsolate;
    Persistent<Context> threadJSContext;

    // thread module cache
    ThreadModuleMap*    moduleMap;

} THREAD_CONTEXT;

void* Thread::ThreadInit()
{
    // allocate memory for thread context
    THREAD_CONTEXT* threadContext = (THREAD_CONTEXT*)malloc(sizeof(THREAD_CONTEXT));
    memset(threadContext, 0, sizeof(THREAD_CONTEXT));

    // create and initialize async watcher
    threadContext->uvAsync = (uv_async_t*)malloc(sizeof(uv_async_t));
    memset(threadContext->uvAsync, 0, sizeof(uv_async_t));
    uv_async_init(uv_default_loop(), threadContext->uvAsync, Thread::uvAsyncCallback);
    threadContext->uvAsync->close_cb = Thread::uvCloseCallback;

    // create thread isolate
    threadContext->threadIsolate = Isolate::New();

    // create module map
    threadContext->moduleMap = new ThreadModuleMap();

    return (void*)threadContext;
}

void Thread::ThreadPostInit(void* threadContext)
{
    // thread context
    THREAD_CONTEXT* thisContext = (THREAD_CONTEXT*)threadContext;

    // get reference to thread isolate
    Isolate* isolate = thisContext->threadIsolate;
    {
        // lock the isolate
        Locker myLocker(isolate);

        // enter the isolate
        isolate->Enter();

        // create a stack-allocated handle-scope
        HandleScope handle_scope;

        // create a persistent context for the javascript in this thread
        Persistent<Context> persistentContext(Context::New());

        // store reference to persistent context
        thisContext->threadJSContext = persistentContext;

        // enter thread specific context
        Context::Scope context_scope(thisContext->threadJSContext);

        // get handle to nRequire function
        Local<FunctionTemplate> functionTemplate = FunctionTemplate::New(Require::RequireFunction);
        Local<Function> requireFunction = functionTemplate->GetFunction();
        requireFunction->SetName(String::NewSymbol(REQUIRE_FUNCTION_NAME));

        // attach function to context
        Handle<Object> globalContext = thisContext->threadJSContext->Global();
        globalContext->Set(String::New(REQUIRE_FUNCTION_NAME), requireFunction);

        // setup the module and exports objects
        Local<Object> module = Object::New();
        module->Set(String::New("exports"), Object::New());

        // attach to the context
        globalContext->Set(String::New("module"), module);
        globalContext->Set(String::New("exports"), Handle<Object>::Cast(module->Get(String::New("exports"))));
    }

    // leave the isolate
    isolate->Exit();
}

void Thread::ThreadDestroy(void* threadContext)
{
    // thread context
    THREAD_CONTEXT* thisContext = (THREAD_CONTEXT*)threadContext;

    // get reference to thread isolate
    Isolate* isolate = thisContext->threadIsolate;
    {
        // lock the isolate
        Locker myLocker(isolate);

        // enter the isolate
        isolate->Enter();

        // dispose of js context
        thisContext->threadJSContext.Dispose();
    }

    // exit the isolate
    isolate->Exit();

    // Dispose of the isolate
    ((Isolate *)(thisContext->threadIsolate))->Dispose();

    // release the module map
    delete thisContext->moduleMap;

    //uv_unref((uv_handle_t*)thisContext->uvAsync);
    uv_close((uv_handle_t*)thisContext->uvAsync, ((uv_async_t*)thisContext->uvAsync)->close_cb);

    // release the thread context memory
    free(threadContext);
}

THREAD_WORK_ITEM* Thread::BuildWorkItem(Handle<Object> v8Object)
{
    //fprintf(stdout, "[%u] Thread::BuildWorkItem\n", SyncGetThreadId());
    //Utilities::ParseObject(v8Object);

    // return value
    THREAD_WORK_ITEM *workItem = (THREAD_WORK_ITEM*)malloc(sizeof(THREAD_WORK_ITEM));
    memset(workItem, 0, sizeof(THREAD_WORK_ITEM));

    // temporary handles
    Local<String> propertyName;
    Local<Value> propertyValue;
    Local<String> propertyString;
    Local<Object> propertyObject;

    // workId
    propertyName = String::New("workId");
    propertyValue = v8Object->Get(propertyName);
    workItem->workId = propertyValue->ToUint32()->Value();
    //fprintf(stdout, "[%u] Thread::BuildWorkItem - WorkId: %u\n", SyncGetThreadId(), propertyValue->ToUint32()->Value());

    // fileKey
    propertyName = String::New("fileKey");
    propertyValue = v8Object->Get(propertyName);
    workItem->fileKey = propertyValue->ToUint32()->Value();

    // workFunction
    propertyName = String::New("workFunction");
    propertyString = v8Object->Get(propertyName)->ToString();
    workItem->workFunction = Utilities::CreateCharBuffer(propertyString);

    // generate JSON c str of param object
    propertyName = String::New("workParam");
    propertyObject = v8Object->Get(propertyName)->ToObject();
    char* stringify = JSON::Stringify(propertyObject);
    workItem->workParam = stringify;

    // callback context
    propertyName = String::New("callbackContext");
    propertyObject = v8Object->Get(propertyName)->ToObject();
    workItem->callbackContext = Persistent<Object>::New(propertyObject);

    // callback function
    propertyName = String::New("callbackFunction");
    workItem->callbackFunction = Persistent<Function>::New(Handle<Function>::Cast(v8Object->Get(propertyName)));

    // register external memory
    int bytesAlloc = strlen(workItem->workParam) + strlen(workItem->workFunction);
    V8::AdjustAmountOfExternalAllocatedMemory(bytesAlloc);

    return workItem;
}

void Thread::QueueWorkItem(TASK_QUEUE_DATA *taskQueue, THREAD_WORK_ITEM *workItem)
{
    // reference to task queue item to be added
    TASK_QUEUE_ITEM     *taskQueueItem = 0;

    // create task queue item object
    taskQueueItem = (TASK_QUEUE_ITEM*)malloc(sizeof(TASK_QUEUE_ITEM));
    memset(taskQueueItem, 0, sizeof(TASK_QUEUE_ITEM));

    // set the data size
    taskQueueItem->dataSize = sizeof(THREAD_WORK_ITEM);

    // store reference to work item
    taskQueueItem->taskItemData = (void*)workItem;

    // set the task item work function
    taskQueueItem->taskItemFunction = Thread::WorkItemFunction;

    // set the task item callback function
    taskQueueItem->taskItemCallback = Thread::WorkItemCallback;

    // set the task item id
    taskQueueItem->taskId = workItem->workId;

    // add the task to the queue
    AddTaskToQueue(taskQueue, taskQueueItem);
}

void* Thread::WorkItemFunction(TASK_QUEUE_WORK_DATA *taskData, void *threadContext, void *threadWorkItem)
{
    //fprintf(stdout, "[%u] Thread::WorkItemFunction\n", SyncGetThreadId());

    // thread context
    THREAD_CONTEXT* thisContext = (THREAD_CONTEXT*)threadContext;

    // thread work item
    THREAD_WORK_ITEM* workItem = (THREAD_WORK_ITEM*)threadWorkItem;

    // check module cache
    const string* workFile = 0;
    if(thisContext->moduleMap->find(workItem->fileKey) == thisContext->moduleMap->end())
    {
        // get work file string
        workFile = fileManager->GetFileString(workItem->fileKey);
    }

    // get reference to thread isolate
    Isolate* isolate = thisContext->threadIsolate;
    {
        // Lock the isolate
        Locker myLocker(isolate);

        // Enter the isolate
        isolate->Enter();

        // Create a stack-allocated handle scope.
        HandleScope handle_scope;

        // enter thread specific context
        Context::Scope context_scope(thisContext->threadJSContext);

        // get the module string if necessary
        Handle<Object> workerObject;
        TryCatch tryCatch;
        if(workFile != 0)
        {
            // compile the source code
            Handle<Script> script = Script::Compile(String::New(workFile->c_str()));

            // throw exception if script failed to compile
            if(script.IsEmpty() || tryCatch.HasCaught())
            {
                Utilities::HandleException(&tryCatch, true);
                return 0;
            }

            // run the script to get the result
            Handle<Value> scriptResult = script->Run();

            // throw exception if script failed to run properly
            if(scriptResult.IsEmpty() || tryCatch.HasCaught())
            {
                Utilities::HandleException(&tryCatch, true);
                return 0;
            }

            // get the handle to constructor function of worker object type
            Handle<Function> constructorFunction = Handle<Function>::Cast(scriptResult);
            workerObject = constructorFunction->NewInstance();

            // cache the persistent object type for later use
            thisContext->moduleMap->insert(make_pair(workItem->fileKey, Persistent<Object>::New(workerObject)));
        }
        else
        {
            // get the constructor function from cache
            workerObject = thisContext->moduleMap->find(workItem->fileKey)->second;
        }

        // get work param
        Handle<Value> workParam = JSON::Parse(workItem->workParam);

        // get worker function name
        Handle<Value> workerFunction = workerObject->Get(String::New(workItem->workFunction));

        // execute function and get work result
        Handle<Value> workResult = Handle<Function>::Cast(workerFunction)->Call(workerObject, 1, &workParam);
        if(workResult.IsEmpty())
        {
            Utilities::HandleException(&tryCatch, true);
            return 0;
        }

        // strinigfy callback object
        char* stringify = JSON::Stringify(workResult->ToObject());
        workItem->callbackObject = stringify;

        // register external memory
        V8::AdjustAmountOfExternalAllocatedMemory(strlen(workItem->callbackObject));
    }

    // leave the isolate
    isolate->Exit();

    // return the work item
    return workItem;
}

void Thread::WorkItemCallback(TASK_QUEUE_WORK_DATA *taskData, void *threadContext, void *threadWorkItem)
{
  //fprintf(stdout, "[%u] Thread::WorkItemCallback\n", SyncGetThreadId());

  // thread context
  THREAD_CONTEXT* thisContext = (THREAD_CONTEXT*)threadContext;

  // add work item to callback queue
  THREAD_WORK_ITEM* workItem = (THREAD_WORK_ITEM*)malloc(sizeof(THREAD_WORK_ITEM));
  memcpy(workItem, threadWorkItem, sizeof(THREAD_WORK_ITEM));
  callbackQueue->AddWorkItem(workItem);

  // async callback
  uv_async_t *uvAsync = (uv_async_t*)thisContext->uvAsync;
  uv_async_send(uvAsync);
}

void Thread::uvCloseCallback(uv_handle_t* handle)
{
  //fprintf(stdout, "[%u] Thread::uvCloseCallback - Async: %p\n", SyncGetThreadId(), handle);

  // cleanup the handle
  //free(handle->data);
  free(handle);
}

void Thread::uvAsyncCallback(uv_async_t* handle, int status)
{
    //fprintf(stdout, "[%u] Thread::uvAsyncCallback - Async: %p\n", SyncGetThreadId(), handle);

    HandleScope scope;

    // process all work items awaiting callback
    THREAD_WORK_ITEM* workItem = 0;
    while((workItem = callbackQueue->GetWorkItem()) != 0)
    {
        // get references to work item callback data
        Persistent<Object> callbackContext = workItem->callbackContext;
        Persistent<Function> callbackFunction = workItem->callbackFunction;
        Handle<Value> callbackObject = JSON::Parse(workItem->callbackObject);

        //create arguments array
        const unsigned argc = 2;
        Handle<Value> argv[argc] = { callbackObject, Number::New(workItem->workId) };

        //fprintf(stdout, "[%u] Thread::uvAsyncCallback - Call", SyncGetThreadId());

        // make callback on node thread
        callbackFunction->Call(callbackContext, argc, argv);

        //fprintf(stdout, "[%u] Thread::uvAsyncCallback - Clearing data...\n", SyncGetThreadId());

        // cleanup the work item data
        callbackContext.Dispose();
        callbackContext.Clear();

        callbackFunction.Dispose();
        callbackFunction.Clear();

        free(workItem->workFunction);
        free(workItem->workParam);
        free(workItem->callbackObject);
        free(workItem);

        // de-register memory
        int bytesFree = strlen(workItem->workParam) + strlen(workItem->workFunction) + strlen(workItem->callbackObject);
        V8::AdjustAmountOfExternalAllocatedMemory(-bytesFree);
    }

    // wait for gc to do its business (250/1000 "work")
    while(!V8::IdleNotification(250)) {
        //fprintf(stdout, "[%u] Thread::uvAsyncCallback - Notifying Idle...\n", SyncGetThreadId());
    }
}<commit_msg>Fixing bug where tryign to call strlen() after memory is free'd.<commit_after>#include "thread.h"

// C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// v8
#include <v8.h>
using namespace v8;

// custom source
#include "file_manager.h"
#include "json.h"
#include "utilities.h"
#include "callback_queue.h"
#include "nrequire.h"

// file loader and hash (npool.cc)
static FileManager *fileManager = &(FileManager::GetInstance());

// callback queue
static CallbackQueue *callbackQueue = &(CallbackQueue::GetInstance());

typedef struct THREAD_CONTEXT_STRUCT
{
    // libuv
    uv_async_t*         uvAsync;

    // v8
    Isolate*            threadIsolate;
    Persistent<Context> threadJSContext;

    // thread module cache
    ThreadModuleMap*    moduleMap;

} THREAD_CONTEXT;

void* Thread::ThreadInit()
{
    // allocate memory for thread context
    THREAD_CONTEXT* threadContext = (THREAD_CONTEXT*)malloc(sizeof(THREAD_CONTEXT));
    memset(threadContext, 0, sizeof(THREAD_CONTEXT));

    // create and initialize async watcher
    threadContext->uvAsync = (uv_async_t*)malloc(sizeof(uv_async_t));
    memset(threadContext->uvAsync, 0, sizeof(uv_async_t));
    uv_async_init(uv_default_loop(), threadContext->uvAsync, Thread::uvAsyncCallback);
    threadContext->uvAsync->close_cb = Thread::uvCloseCallback;

    // create thread isolate
    threadContext->threadIsolate = Isolate::New();

    // create module map
    threadContext->moduleMap = new ThreadModuleMap();

    return (void*)threadContext;
}

void Thread::ThreadPostInit(void* threadContext)
{
    // thread context
    THREAD_CONTEXT* thisContext = (THREAD_CONTEXT*)threadContext;

    // get reference to thread isolate
    Isolate* isolate = thisContext->threadIsolate;
    {
        // lock the isolate
        Locker myLocker(isolate);

        // enter the isolate
        isolate->Enter();

        // create a stack-allocated handle-scope
        HandleScope handle_scope;

        // create a persistent context for the javascript in this thread
        Persistent<Context> persistentContext(Context::New());

        // store reference to persistent context
        thisContext->threadJSContext = persistentContext;

        // enter thread specific context
        Context::Scope context_scope(thisContext->threadJSContext);

        // get handle to nRequire function
        Local<FunctionTemplate> functionTemplate = FunctionTemplate::New(Require::RequireFunction);
        Local<Function> requireFunction = functionTemplate->GetFunction();
        requireFunction->SetName(String::NewSymbol(REQUIRE_FUNCTION_NAME));

        // attach function to context
        Handle<Object> globalContext = thisContext->threadJSContext->Global();
        globalContext->Set(String::New(REQUIRE_FUNCTION_NAME), requireFunction);

        // setup the module and exports objects
        Local<Object> module = Object::New();
        module->Set(String::New("exports"), Object::New());

        // attach to the context
        globalContext->Set(String::New("module"), module);
        globalContext->Set(String::New("exports"), Handle<Object>::Cast(module->Get(String::New("exports"))));
    }

    // leave the isolate
    isolate->Exit();
}

void Thread::ThreadDestroy(void* threadContext)
{
    // thread context
    THREAD_CONTEXT* thisContext = (THREAD_CONTEXT*)threadContext;

    // get reference to thread isolate
    Isolate* isolate = thisContext->threadIsolate;
    {
        // lock the isolate
        Locker myLocker(isolate);

        // enter the isolate
        isolate->Enter();

        // dispose of js context
        thisContext->threadJSContext.Dispose();
    }

    // exit the isolate
    isolate->Exit();

    // Dispose of the isolate
    ((Isolate *)(thisContext->threadIsolate))->Dispose();

    // release the module map
    delete thisContext->moduleMap;

    //uv_unref((uv_handle_t*)thisContext->uvAsync);
    uv_close((uv_handle_t*)thisContext->uvAsync, ((uv_async_t*)thisContext->uvAsync)->close_cb);

    // release the thread context memory
    free(threadContext);
}

THREAD_WORK_ITEM* Thread::BuildWorkItem(Handle<Object> v8Object)
{
    //fprintf(stdout, "[%u] Thread::BuildWorkItem\n", SyncGetThreadId());
    //Utilities::ParseObject(v8Object);

    // return value
    THREAD_WORK_ITEM *workItem = (THREAD_WORK_ITEM*)malloc(sizeof(THREAD_WORK_ITEM));
    memset(workItem, 0, sizeof(THREAD_WORK_ITEM));

    // temporary handles
    Local<String> propertyName;
    Local<Value> propertyValue;
    Local<String> propertyString;
    Local<Object> propertyObject;

    // workId
    propertyName = String::New("workId");
    propertyValue = v8Object->Get(propertyName);
    workItem->workId = propertyValue->ToUint32()->Value();
    //fprintf(stdout, "[%u] Thread::BuildWorkItem - WorkId: %u\n", SyncGetThreadId(), propertyValue->ToUint32()->Value());

    // fileKey
    propertyName = String::New("fileKey");
    propertyValue = v8Object->Get(propertyName);
    workItem->fileKey = propertyValue->ToUint32()->Value();

    // workFunction
    propertyName = String::New("workFunction");
    propertyString = v8Object->Get(propertyName)->ToString();
    workItem->workFunction = Utilities::CreateCharBuffer(propertyString);

    // generate JSON c str of param object
    propertyName = String::New("workParam");
    propertyObject = v8Object->Get(propertyName)->ToObject();
    char* stringify = JSON::Stringify(propertyObject);
    workItem->workParam = stringify;

    // callback context
    propertyName = String::New("callbackContext");
    propertyObject = v8Object->Get(propertyName)->ToObject();
    workItem->callbackContext = Persistent<Object>::New(propertyObject);

    // callback function
    propertyName = String::New("callbackFunction");
    workItem->callbackFunction = Persistent<Function>::New(Handle<Function>::Cast(v8Object->Get(propertyName)));

    // register external memory
    int bytesAlloc = strlen(workItem->workParam) + strlen(workItem->workFunction);
    V8::AdjustAmountOfExternalAllocatedMemory(bytesAlloc);

    return workItem;
}

void Thread::QueueWorkItem(TASK_QUEUE_DATA *taskQueue, THREAD_WORK_ITEM *workItem)
{
    // reference to task queue item to be added
    TASK_QUEUE_ITEM     *taskQueueItem = 0;

    // create task queue item object
    taskQueueItem = (TASK_QUEUE_ITEM*)malloc(sizeof(TASK_QUEUE_ITEM));
    memset(taskQueueItem, 0, sizeof(TASK_QUEUE_ITEM));

    // set the data size
    taskQueueItem->dataSize = sizeof(THREAD_WORK_ITEM);

    // store reference to work item
    taskQueueItem->taskItemData = (void*)workItem;

    // set the task item work function
    taskQueueItem->taskItemFunction = Thread::WorkItemFunction;

    // set the task item callback function
    taskQueueItem->taskItemCallback = Thread::WorkItemCallback;

    // set the task item id
    taskQueueItem->taskId = workItem->workId;

    // add the task to the queue
    AddTaskToQueue(taskQueue, taskQueueItem);
}

void* Thread::WorkItemFunction(TASK_QUEUE_WORK_DATA *taskData, void *threadContext, void *threadWorkItem)
{
    //fprintf(stdout, "[%u] Thread::WorkItemFunction\n", SyncGetThreadId());

    // thread context
    THREAD_CONTEXT* thisContext = (THREAD_CONTEXT*)threadContext;

    // thread work item
    THREAD_WORK_ITEM* workItem = (THREAD_WORK_ITEM*)threadWorkItem;

    // check module cache
    const string* workFile = 0;
    if(thisContext->moduleMap->find(workItem->fileKey) == thisContext->moduleMap->end())
    {
        // get work file string
        workFile = fileManager->GetFileString(workItem->fileKey);
    }

    // get reference to thread isolate
    Isolate* isolate = thisContext->threadIsolate;
    {
        // Lock the isolate
        Locker myLocker(isolate);

        // Enter the isolate
        isolate->Enter();

        // Create a stack-allocated handle scope.
        HandleScope handle_scope;

        // enter thread specific context
        Context::Scope context_scope(thisContext->threadJSContext);

        // get the module string if necessary
        Handle<Object> workerObject;
        TryCatch tryCatch;
        if(workFile != 0)
        {
            // compile the source code
            Handle<Script> script = Script::Compile(String::New(workFile->c_str()));

            // throw exception if script failed to compile
            if(script.IsEmpty() || tryCatch.HasCaught())
            {
                Utilities::HandleException(&tryCatch, true);
                return 0;
            }

            // run the script to get the result
            Handle<Value> scriptResult = script->Run();

            // throw exception if script failed to run properly
            if(scriptResult.IsEmpty() || tryCatch.HasCaught())
            {
                Utilities::HandleException(&tryCatch, true);
                return 0;
            }

            // get the handle to constructor function of worker object type
            Handle<Function> constructorFunction = Handle<Function>::Cast(scriptResult);
            workerObject = constructorFunction->NewInstance();

            // cache the persistent object type for later use
            thisContext->moduleMap->insert(make_pair(workItem->fileKey, Persistent<Object>::New(workerObject)));
        }
        else
        {
            // get the constructor function from cache
            workerObject = thisContext->moduleMap->find(workItem->fileKey)->second;
        }

        // get work param
        Handle<Value> workParam = JSON::Parse(workItem->workParam);

        // get worker function name
        Handle<Value> workerFunction = workerObject->Get(String::New(workItem->workFunction));

        // execute function and get work result
        Handle<Value> workResult = Handle<Function>::Cast(workerFunction)->Call(workerObject, 1, &workParam);
        if(workResult.IsEmpty())
        {
            Utilities::HandleException(&tryCatch, true);
            return 0;
        }

        // strinigfy callback object
        char* stringify = JSON::Stringify(workResult->ToObject());
        workItem->callbackObject = stringify;

        // register external memory
        V8::AdjustAmountOfExternalAllocatedMemory(strlen(workItem->callbackObject));
    }

    // leave the isolate
    isolate->Exit();

    // return the work item
    return workItem;
}

void Thread::WorkItemCallback(TASK_QUEUE_WORK_DATA *taskData, void *threadContext, void *threadWorkItem)
{
  //fprintf(stdout, "[%u] Thread::WorkItemCallback\n", SyncGetThreadId());

  // thread context
  THREAD_CONTEXT* thisContext = (THREAD_CONTEXT*)threadContext;

  // add work item to callback queue
  THREAD_WORK_ITEM* workItem = (THREAD_WORK_ITEM*)malloc(sizeof(THREAD_WORK_ITEM));
  memcpy(workItem, threadWorkItem, sizeof(THREAD_WORK_ITEM));
  callbackQueue->AddWorkItem(workItem);

  // async callback
  uv_async_t *uvAsync = (uv_async_t*)thisContext->uvAsync;
  uv_async_send(uvAsync);
}

void Thread::uvCloseCallback(uv_handle_t* handle)
{
  //fprintf(stdout, "[%u] Thread::uvCloseCallback - Async: %p\n", SyncGetThreadId(), handle);

  // cleanup the handle
  //free(handle->data);
  free(handle);
}

void Thread::uvAsyncCallback(uv_async_t* handle, int status)
{
    //fprintf(stdout, "[%u] Thread::uvAsyncCallback - Async: %p\n", SyncGetThreadId(), handle);

    HandleScope scope;

    // process all work items awaiting callback
    THREAD_WORK_ITEM* workItem = 0;
    while((workItem = callbackQueue->GetWorkItem()) != 0)
    {
        // get references to work item callback data
        Persistent<Object> callbackContext = workItem->callbackContext;
        Persistent<Function> callbackFunction = workItem->callbackFunction;
        Handle<Value> callbackObject = JSON::Parse(workItem->callbackObject);

        //create arguments array
        const unsigned argc = 2;
        Handle<Value> argv[argc] = { callbackObject, Number::New(workItem->workId) };

        //fprintf(stdout, "[%u] Thread::uvAsyncCallback - Call", SyncGetThreadId());

        // make callback on node thread
        callbackFunction->Call(callbackContext, argc, argv);

        //fprintf(stdout, "[%u] Thread::uvAsyncCallback - Clearing data...\n", SyncGetThreadId());

        // cleanup the work item data
        callbackContext.Dispose();
        callbackContext.Clear();

        callbackFunction.Dispose();
        callbackFunction.Clear();

        // de-register memory
        int bytesFree = strlen(workItem->workParam) + strlen(workItem->workFunction) + strlen(workItem->callbackObject);
        V8::AdjustAmountOfExternalAllocatedMemory(-bytesFree);

        // un-alloc the memory
        free(workItem->workFunction);
        free(workItem->workParam);
        free(workItem->callbackObject);
        free(workItem);
    }

    // wait for gc to do its business (250/1000 "work")
    while(!V8::IdleNotification(250)) {
        //fprintf(stdout, "[%u] Thread::uvAsyncCallback - Notifying Idle...\n", SyncGetThreadId());
    }
}<|endoftext|>


<commit_before>#include "tools/replay/camera.h"
#include "tools/replay/util.h"

#include <cassert>

CameraServer::CameraServer(std::pair<int, int> camera_size[MAX_CAMERAS]) {
  for (int i = 0; i < MAX_CAMERAS; ++i) {
    std::tie(cameras_[i].width, cameras_[i].height) = camera_size[i];
  }
  startVipcServer();
}

CameraServer::~CameraServer() {
  for (auto &cam : cameras_) {
    if (cam.thread.joinable()) {
      cam.queue.push({});
      cam.thread.join();
    }
  }
  vipc_server_.reset(nullptr);
}

void CameraServer::startVipcServer() {
  vipc_server_.reset(new VisionIpcServer("camerad"));
  for (auto &cam : cameras_) {
    if (cam.width > 0 && cam.height > 0) {
      rInfo("camera[%d] frame size %dx%d", cam.type, cam.width, cam.height);
      vipc_server_->create_buffers(cam.stream_type, YUV_BUFFER_COUNT, false, cam.width, cam.height);
      if (!cam.thread.joinable()) {
        cam.thread = std::thread(&CameraServer::cameraThread, this, std::ref(cam));
      }
    }
  }
  vipc_server_->start_listener();
}

void CameraServer::cameraThread(Camera &cam) {
  auto read_frame = [&](FrameReader *fr, int frame_id) {
    VisionBuf *yuv_buf = vipc_server_->get_buffer(cam.stream_type);
    assert(yuv_buf);
    bool ret = fr->get(frame_id, (uint8_t *)yuv_buf->addr);
    return ret ? yuv_buf : nullptr;
  };

  while (true) {
    const auto [fr, eidx] = cam.queue.pop();
    if (!fr) break;

    const int id = eidx.getSegmentId();
    bool prefetched = (id == cam.cached_id && eidx.getSegmentNum() == cam.cached_seg);
    auto yuv = prefetched ? cam.cached_buf : read_frame(fr, id);
    if (yuv) {
      VisionIpcBufExtra extra = {
          .frame_id = eidx.getFrameId(),
          .timestamp_sof = eidx.getTimestampSof(),
          .timestamp_eof = eidx.getTimestampEof(),
      };
      vipc_server_->send(yuv, &extra, false);
    } else {
      rError("camera[%d] failed to get frame:", cam.type, eidx.getSegmentId());
    }

    cam.cached_id = id + 1;
    cam.cached_seg = eidx.getSegmentNum();
    cam.cached_buf = read_frame(fr, cam.cached_id);

    --publishing_;
  }
}

void CameraServer::pushFrame(CameraType type, FrameReader *fr, const cereal::EncodeIndex::Reader &eidx) {
  auto &cam = cameras_[type];
  if (cam.width != fr->width || cam.height != fr->height) {
    cam.width = fr->width;
    cam.height = fr->height;
    waitForSent();
    startVipcServer();
  }

  ++publishing_;
  cam.queue.push({fr, eidx});
}

void CameraServer::waitForSent() {
  while (publishing_ > 0) {
    std::this_thread::yield();
  }
}
<commit_msg>replay: add missing format specifier (#25590)<commit_after>#include "tools/replay/camera.h"
#include "tools/replay/util.h"

#include <cassert>

CameraServer::CameraServer(std::pair<int, int> camera_size[MAX_CAMERAS]) {
  for (int i = 0; i < MAX_CAMERAS; ++i) {
    std::tie(cameras_[i].width, cameras_[i].height) = camera_size[i];
  }
  startVipcServer();
}

CameraServer::~CameraServer() {
  for (auto &cam : cameras_) {
    if (cam.thread.joinable()) {
      cam.queue.push({});
      cam.thread.join();
    }
  }
  vipc_server_.reset(nullptr);
}

void CameraServer::startVipcServer() {
  vipc_server_.reset(new VisionIpcServer("camerad"));
  for (auto &cam : cameras_) {
    if (cam.width > 0 && cam.height > 0) {
      rInfo("camera[%d] frame size %dx%d", cam.type, cam.width, cam.height);
      vipc_server_->create_buffers(cam.stream_type, YUV_BUFFER_COUNT, false, cam.width, cam.height);
      if (!cam.thread.joinable()) {
        cam.thread = std::thread(&CameraServer::cameraThread, this, std::ref(cam));
      }
    }
  }
  vipc_server_->start_listener();
}

void CameraServer::cameraThread(Camera &cam) {
  auto read_frame = [&](FrameReader *fr, int frame_id) {
    VisionBuf *yuv_buf = vipc_server_->get_buffer(cam.stream_type);
    assert(yuv_buf);
    bool ret = fr->get(frame_id, (uint8_t *)yuv_buf->addr);
    return ret ? yuv_buf : nullptr;
  };

  while (true) {
    const auto [fr, eidx] = cam.queue.pop();
    if (!fr) break;

    const int id = eidx.getSegmentId();
    bool prefetched = (id == cam.cached_id && eidx.getSegmentNum() == cam.cached_seg);
    auto yuv = prefetched ? cam.cached_buf : read_frame(fr, id);
    if (yuv) {
      VisionIpcBufExtra extra = {
          .frame_id = eidx.getFrameId(),
          .timestamp_sof = eidx.getTimestampSof(),
          .timestamp_eof = eidx.getTimestampEof(),
      };
      vipc_server_->send(yuv, &extra, false);
    } else {
      rError("camera[%d] failed to get frame: %lu", cam.type, eidx.getSegmentId());
    }

    cam.cached_id = id + 1;
    cam.cached_seg = eidx.getSegmentNum();
    cam.cached_buf = read_frame(fr, cam.cached_id);

    --publishing_;
  }
}

void CameraServer::pushFrame(CameraType type, FrameReader *fr, const cereal::EncodeIndex::Reader &eidx) {
  auto &cam = cameras_[type];
  if (cam.width != fr->width || cam.height != fr->height) {
    cam.width = fr->width;
    cam.height = fr->height;
    waitForSent();
    startVipcServer();
  }

  ++publishing_;
  cam.queue.push({fr, eidx});
}

void CameraServer::waitForSent() {
  while (publishing_ > 0) {
    std::this_thread::yield();
  }
}
<|endoftext|>


<commit_before>#include "dbcparser.h"

#include <cstdint>
#include <tuple>
#include <fstream>
#include <algorithm>
#include <experimental/filesystem>

#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/adapt_struct.hpp>

BOOST_FUSION_ADAPT_STRUCT(
  dbc::Signal_def,
  (std::string, name)
  (bool, multiplex_switch)
  (std::int32_t, multiplex_value)
  (std::uint32_t, pos)
  (std::uint32_t, len)
  (dbc::Byte_order, order)
  (dbc::Value_sign, sign)
  (double, factor)
  (double, offset)
  (double, minimum)
  (double, maximum)
  (std::string, unit)
  (std::vector<std::string>, receiver)
)

BOOST_FUSION_ADAPT_STRUCT(
  dbc::Frame_def,
  (std::uint32_t, id)
  (std::string, name)
  (std::uint32_t, dlc)
  (std::string, transmitter)
  (std::vector<dbc::Signal_def>, signal_defs)
)

namespace fs = std::experimental::filesystem;
namespace x3 = boost::spirit::x3;
namespace latin1 = boost::spirit::x3::iso8859_1;

struct signs_ : x3::symbols<dbc::Value_sign>
{
  signs_()
  {
    add
      ("+", dbc::Value_sign::Unsigned)
      ("-", dbc::Value_sign::Signed)
    ;
  }
} signs;

struct oders_ : x3::symbols<dbc::Byte_order>
{
  oders_()
  {
    add
      ("0", dbc::Byte_order::Moto)
      ("1", dbc::Byte_order::Intel)
    ;
  }
} orders;

namespace parsers
{

using x3::char_;
using x3::long_;
using x3::ulong_;
using x3::double_;
using x3::lit;
using x3::attr;
using x3::lexeme;

const auto c_identifier = +char_("a-zA-Z0-9_");
const auto multiplexing_info = (('M' >> attr(true)) | attr(false)) >> (('m' >> long_) | attr(-1));
const auto signal_name = lexeme[c_identifier];
const auto quoted_string = lexeme['"' >> *(latin1::char_ - '"') >> '"'];

x3::rule<class signal, dbc::Signal_def> const signal = "signal";
const auto signal_def =
    lit("SG_")
    >> signal_name >> multiplexing_info >> ':'
    >> ulong_ >> '|' >> ulong_ >> '@'
    >> orders >> signs
    >> '(' >> double_ >> ',' >> double_ >> ')'
    >> '[' >> double_ >> '|' >> double_ >> ']'
    >> quoted_string
    >> c_identifier % ','
    ;
BOOST_SPIRIT_DEFINE(signal);

x3::rule<class message, dbc::Frame_def> const frame = "frame";
const auto frame_def = lit("BO_") >> ulong_ >> c_identifier >> ':' >> ulong_ >> c_identifier
  >> attr(std::vector<dbc::Signal_def>{});
BOOST_SPIRIT_DEFINE(frame);

}  // namespace parsers

namespace
{

inline bool messages_block_done(std::string_view line)
{
  // We're not interested in comments, etc.
  using latin1::space;
  using x3::lit;
  return x3::parse(std::begin(line), std::end(line), *space
      >> (lit("CM_") | lit("BA_DEF_") | lit("BA_")) >> space);
}

std::tuple<bool, dbc::Signal_def> parse_signal_def(std::string_view line)
{
  dbc::Signal_def signal;
  bool success = x3::phrase_parse(std::begin(line), std::end(line), parsers::signal, latin1::space,
      signal);
  return {success, signal};
}

std::tuple<bool, dbc::Frame_def> parse_frame_def(std::string_view line)
{
  dbc::Frame_def message;
  bool success = x3::phrase_parse(std::begin(line), std::end(line), parsers::frame, latin1::space,
      message);
  return {success, message};
}

[[maybe_unused]] void sort_signals(dbc::Frame_def& frame_def)
{
  std::sort(std::begin(frame_def.signal_defs), std::end(frame_def.signal_defs),
      [](const auto& a, const auto& b){ return a.pos < b.pos; });
}

}  // namespace

dbc::File dbc::parse(std::string_view filepath)
{
  File dbc_file;

  std::ifstream fs{std::string{filepath}};
  if (!fs.is_open())
    throw Parse_error{"Could not open file"};

  dbc_file.name = fs::path{filepath}.filename().string();

  std::string line;
  while (std::getline(fs, line)) {
    if (auto [success, signal_def] = parse_signal_def(line); success) {
      if (dbc_file.frame_defs.empty())
        throw Parse_error{"Format error"};
      else
        dbc_file.frame_defs.back().signal_defs.push_back(signal_def);
    }
    else if (auto [success, frame_def] = parse_frame_def(line); success) {
      dbc_file.frame_defs.push_back(frame_def);
    }
    else if (messages_block_done(line)) {
      break;
    }
  }

  return dbc_file;
}
<commit_msg>Add meta data parsing<commit_after>#include "dbcparser.h"

#include <cstdint>
#include <tuple>
#include <fstream>
#include <algorithm>
#include <experimental/filesystem>

#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/adapt_struct.hpp>

#include "dbcmetaparser.h"

BOOST_FUSION_ADAPT_STRUCT(
  dbc::Signal_def,
  (std::string, name)
  (bool, multiplex_switch)
  (std::int32_t, multiplex_value)
  (std::uint32_t, pos)
  (std::uint32_t, len)
  (dbc::Byte_order, order)
  (dbc::Value_sign, sign)
  (double, factor)
  (double, offset)
  (double, minimum)
  (double, maximum)
  (std::string, unit)
  (std::vector<std::string>, receiver)
  (dbc::Signal_meta_data, meta_data)
)

BOOST_FUSION_ADAPT_STRUCT(
  dbc::Frame_def,
  (std::uint32_t, id)
  (std::string, name)
  (std::uint32_t, dlc)
  (std::string, transmitter)
  (std::vector<dbc::Signal_def>, signal_defs)
)

namespace fs = std::experimental::filesystem;
namespace x3 = boost::spirit::x3;
namespace latin1 = boost::spirit::x3::iso8859_1;

namespace
{

struct signs_ : x3::symbols<dbc::Value_sign>
{
  signs_()
  {
    add
      ("+", dbc::Value_sign::Unsigned)
      ("-", dbc::Value_sign::Signed)
    ;
  }
} signs;

struct oders_ : x3::symbols<dbc::Byte_order>
{
  oders_()
  {
    add
      ("0", dbc::Byte_order::Moto)
      ("1", dbc::Byte_order::Intel)
    ;
  }
} orders;

}  // namspace

namespace parsers
{

using x3::char_;
using x3::long_;
using x3::ulong_;
using x3::double_;
using x3::lit;
using x3::attr;
using x3::lexeme;

const auto c_identifier = +char_("a-zA-Z0-9_");
const auto multiplexing_info = (('M' >> attr(true)) | attr(false)) >> (('m' >> long_) | attr(-1));
const auto signal_name = lexeme[c_identifier];
const auto quoted_string = lexeme['"' >> *(latin1::char_ - '"') >> '"'];

x3::rule<class signal, dbc::Signal_def> const signal = "signal";
const auto signal_def =
    lit("SG_")
    >> signal_name >> multiplexing_info >> ':'
    >> ulong_ >> '|' >> ulong_ >> '@'
    >> orders >> signs
    >> '(' >> double_ >> ',' >> double_ >> ')'
    >> '[' >> double_ >> '|' >> double_ >> ']'
    >> quoted_string
    >> c_identifier % ','
    >> attr(dbc::Signal_meta_data{})
    ;
BOOST_SPIRIT_DEFINE(signal);

x3::rule<class message, dbc::Frame_def> const frame = "frame";
const auto frame_def = lit("BO_") >> ulong_ >> c_identifier >> ':' >> ulong_ >> c_identifier
  >> attr(std::vector<dbc::Signal_def>{});
BOOST_SPIRIT_DEFINE(frame);

}  // namespace parsers

namespace
{

inline bool messages_block_done(std::string_view line)
{
  // We're not interested in comments, etc.
  using latin1::space;
  using x3::lit;
  return x3::parse(std::begin(line), std::end(line), *space
      >> (lit("CM_") | lit("BA_DEF_") | lit("BA_")) >> space);
}

std::tuple<bool, dbc::Signal_def> parse_signal_def(std::string_view line)
{
  dbc::Signal_def signal;
  bool success = x3::phrase_parse(std::begin(line), std::end(line), parsers::signal, latin1::space,
      signal);
  if (success) {
    signal.meta_data = dbc::meta::parse_signal(line);
  }
  return {success, signal};
}

std::tuple<bool, dbc::Frame_def> parse_frame_def(std::string_view line)
{
  dbc::Frame_def message;
  bool success = x3::phrase_parse(std::begin(line), std::end(line), parsers::frame, latin1::space,
      message);
  return {success, message};
}

[[maybe_unused]] void sort_signals(dbc::Frame_def& frame_def)
{
  std::sort(std::begin(frame_def.signal_defs), std::end(frame_def.signal_defs),
      [](const auto& a, const auto& b){ return a.pos < b.pos; });
}

}  // namespace

dbc::File dbc::parse(std::string_view filepath)
{
  File dbc_file;

  std::ifstream fs{std::string{filepath}};
  if (!fs.is_open())
    throw Parse_error{"Could not open file"};

  dbc_file.name = fs::path{filepath}.filename().string();

  std::string line;
  while (std::getline(fs, line)) {
    if (auto [success, signal_def] = parse_signal_def(line); success) {
      if (dbc_file.frame_defs.empty())
        throw Parse_error{"Format error"};
      else
        dbc_file.frame_defs.back().signal_defs.push_back(signal_def);
    }
    else if (auto [success, frame_def] = parse_frame_def(line); success) {
      dbc_file.frame_defs.push_back(frame_def);
    }
    else if (messages_block_done(line)) {
      break;
    }
  }

  return dbc_file;
}
<|endoftext|>