<commit_before>#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <thread>

#include "../src/reactor/reactor.h"

using namespace testing;

class pair_socket_wrapper : public socket_wrapper_base
{
public:
	pair_socket_wrapper(const std::shared_ptr<zmq::context_t> context, const std::string &addr)
		: socket_wrapper_base(context, zmq::socket_type::pair, addr, false),
		  local_socket_(*context, zmq::socket_type::pair), context_(context)
	{
		local_socket_.setsockopt(ZMQ_LINGER, 0);
		local_socket_.bind(addr_);
		socket_.close();
	}

	void initialize()
	{
		// called by the reactor
		socket_ = zmq::socket_t(*context_, zmq::socket_type::pair);
		socket_.setsockopt(ZMQ_LINGER, 0);
		socket_.connect(addr_);
	}

	bool send_message(const message_container &message)
	{
		return socket_send(socket_, message);
	}

	bool receive_message(message_container &message)
	{
		return socket_receive(socket_, message);
	}

	bool send_message_local(const message_container &message)
	{
		return socket_send(local_socket_, message);
	}

	bool receive_message_local(message_container &message)
	{
		return socket_receive(local_socket_, message, true);
	}

private:
	zmq::socket_t local_socket_;
	std::shared_ptr<zmq::context_t> context_;

	bool socket_send(zmq::socket_t &socket, const message_container &message)
	{
		bool retval;
		zmq::message_t zmessage(message.identity.c_str(), message.identity.size());
		retval = socket.send(zmessage, ZMQ_SNDMORE);

		if (!retval) {
			return false;
		}

		for (auto it = std::begin(message.data); it != std::end(message.data); ++it) {
			zmessage.rebuild(it->c_str(), it->size());
			retval = socket.send(zmessage, std::next(it) != std::end(message.data) ? ZMQ_SNDMORE : 0);

			if (!retval) {
				return false;
			}
		}

		return true;
	}

	bool socket_receive(zmq::socket_t &socket, message_container &message, bool noblock = false)
	{
		zmq::message_t received_message;
		bool retval;

		retval = socket.recv(&received_message, noblock ? ZMQ_NOBLOCK : 0);

		if (!retval) {
			return false;
		}

		message.identity = std::string((char *) received_message.data(), received_message.size());

		while (received_message.more()) {
			retval = socket.recv(&received_message);

			if (!retval) {
				return false;
			}

			message.data.emplace_back((char *) received_message.data(), received_message.size());
		}

		return true;
	}
};

/**
 * A handler driven by a function passed on creation
 */
class pluggable_handler : public handler_interface
{
public:
	typedef std::function<void(const message_container &message, response_cb response)> fnc_t;

	std::vector<message_container> received;

	pluggable_handler(fnc_t fnc) : fnc_(fnc)
	{
	}

	static std::shared_ptr<pluggable_handler> create(fnc_t fnc)
	{
		return std::make_shared<pluggable_handler>(fnc);
	}

	void on_request(const message_container &message, response_cb response)
	{
		received.push_back(message);
		fnc_(message, response);
	}

private:
	fnc_t fnc_;
};

TEST(reactor, synchronous_handler)
{
	auto context = std::make_shared<zmq::context_t>(1);
	reactor r(context);
	auto socket = std::make_shared<pair_socket_wrapper>(context, "inproc://synchronous_handler_1");
	auto handler = pluggable_handler::create([](const message_container &msg, handler_interface::response_cb respond) {
		respond(message_container("socket", "id1", {"Hello!"}));
	});

	r.add_socket("socket", socket);
	r.add_handler({"socket"}, handler);

	std::thread thread([&r]() { r.start_loop(); });
	std::this_thread::sleep_for(std::chrono::milliseconds(10));

	socket->send_message_local(message_container("", "id1", {"Hello??"}));
	std::this_thread::sleep_for(std::chrono::milliseconds(10));

	EXPECT_THAT(handler->received, ElementsAre(message_container("socket", "id1", {"Hello??"})));

	message_container message;
	socket->receive_message_local(message);

	EXPECT_EQ("id1", message.identity);
	EXPECT_THAT(message.data, ElementsAre("Hello!"));

	r.terminate();
	thread.join();
}

TEST(reactor, asynchronous_handler)
{
	auto context = std::make_shared<zmq::context_t>(1);
	reactor r(context);
	auto socket = std::make_shared<pair_socket_wrapper>(context, "inproc://asynchronous_handler_1");
	auto handler = pluggable_handler::create([](const message_container &msg, handler_interface::response_cb respond) {
		respond(message_container("socket", "id1", {"Hello!"}));
	});

	r.add_socket("socket", socket);
	r.add_async_handler({"socket"}, handler);

	std::thread thread([&r]() { r.start_loop(); });
	std::this_thread::sleep_for(std::chrono::milliseconds(10));

	socket->send_message_local(message_container("", "id1", {"Hello??"}));
	std::this_thread::sleep_for(std::chrono::milliseconds(10));

	EXPECT_THAT(handler->received, ElementsAre(message_container("socket", "id1", {"Hello??"})));

	message_container message;
	socket->receive_message_local(message);

	EXPECT_EQ("id1", message.identity);
	EXPECT_THAT(message.data, ElementsAre("Hello!"));

	r.terminate();
	thread.join();
}

TEST(reactor, timers)
{
	auto context = std::make_shared<zmq::context_t>(1);
	reactor r(context);
	auto socket = std::make_shared<pair_socket_wrapper>(context, "inproc://timers_1");
	auto handler = pluggable_handler::create([](const message_container &msg, handler_interface::response_cb respond) { });

	r.add_async_handler({r.KEY_TIMER}, handler);

	std::thread thread([&r]() { r.start_loop(); });
	std::this_thread::sleep_for(std::chrono::milliseconds(200));

	ASSERT_GE(1, handler->received.size());

	r.terminate();
	thread.join();
}
<commit_msg>add a test to make sure messages to async handlers don't get mixed up<commit_after>#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <thread>

#include "../src/reactor/reactor.h"

using namespace testing;

class pair_socket_wrapper : public socket_wrapper_base
{
public:
	pair_socket_wrapper(const std::shared_ptr<zmq::context_t> context, const std::string &addr)
		: socket_wrapper_base(context, zmq::socket_type::pair, addr, false),
		  local_socket_(*context, zmq::socket_type::pair), context_(context)
	{
		local_socket_.setsockopt(ZMQ_LINGER, 0);
		local_socket_.bind(addr_);
		socket_.close();
	}

	void initialize()
	{
		// called by the reactor
		socket_ = zmq::socket_t(*context_, zmq::socket_type::pair);
		socket_.setsockopt(ZMQ_LINGER, 0);
		socket_.connect(addr_);
	}

	bool send_message(const message_container &message)
	{
		return socket_send(socket_, message);
	}

	bool receive_message(message_container &message)
	{
		return socket_receive(socket_, message);
	}

	bool send_message_local(const message_container &message)
	{
		return socket_send(local_socket_, message);
	}

	bool receive_message_local(message_container &message)
	{
		return socket_receive(local_socket_, message, true);
	}

private:
	zmq::socket_t local_socket_;
	std::shared_ptr<zmq::context_t> context_;

	bool socket_send(zmq::socket_t &socket, const message_container &message)
	{
		bool retval;
		zmq::message_t zmessage(message.identity.c_str(), message.identity.size());
		retval = socket.send(zmessage, ZMQ_SNDMORE);

		if (!retval) {
			return false;
		}

		for (auto it = std::begin(message.data); it != std::end(message.data); ++it) {
			zmessage.rebuild(it->c_str(), it->size());
			retval = socket.send(zmessage, std::next(it) != std::end(message.data) ? ZMQ_SNDMORE : 0);

			if (!retval) {
				return false;
			}
		}

		return true;
	}

	bool socket_receive(zmq::socket_t &socket, message_container &message, bool noblock = false)
	{
		zmq::message_t received_message;
		bool retval;

		retval = socket.recv(&received_message, noblock ? ZMQ_NOBLOCK : 0);

		if (!retval) {
			return false;
		}

		message.identity = std::string((char *) received_message.data(), received_message.size());

		while (received_message.more()) {
			retval = socket.recv(&received_message);

			if (!retval) {
				return false;
			}

			message.data.emplace_back((char *) received_message.data(), received_message.size());
		}

		return true;
	}
};

/**
 * A handler driven by a function passed on creation
 */
class pluggable_handler : public handler_interface
{
public:
	typedef std::function<void(const message_container &message, response_cb response)> fnc_t;

	std::vector<message_container> received;

	pluggable_handler(fnc_t fnc) : fnc_(fnc)
	{
	}

	static std::shared_ptr<pluggable_handler> create(fnc_t fnc)
	{
		return std::make_shared<pluggable_handler>(fnc);
	}

	void on_request(const message_container &message, response_cb response)
	{
		received.push_back(message);
		fnc_(message, response);
	}

private:
	fnc_t fnc_;
};

TEST(reactor, synchronous_handler)
{
	auto context = std::make_shared<zmq::context_t>(1);
	reactor r(context);
	auto socket = std::make_shared<pair_socket_wrapper>(context, "inproc://synchronous_handler_1");
	auto handler = pluggable_handler::create([](const message_container &msg, handler_interface::response_cb respond) {
		respond(message_container("socket", "id1", {"Hello!"}));
	});

	r.add_socket("socket", socket);
	r.add_handler({"socket"}, handler);

	std::thread thread([&r]() { r.start_loop(); });
	std::this_thread::sleep_for(std::chrono::milliseconds(10));

	socket->send_message_local(message_container("", "id1", {"Hello??"}));
	std::this_thread::sleep_for(std::chrono::milliseconds(10));

	EXPECT_THAT(handler->received, ElementsAre(message_container("socket", "id1", {"Hello??"})));

	message_container message;
	socket->receive_message_local(message);

	EXPECT_EQ("id1", message.identity);
	EXPECT_THAT(message.data, ElementsAre("Hello!"));

	r.terminate();
	thread.join();
}

TEST(reactor, asynchronous_handler)
{
	auto context = std::make_shared<zmq::context_t>(1);
	reactor r(context);
	auto socket = std::make_shared<pair_socket_wrapper>(context, "inproc://asynchronous_handler_1");
	auto handler = pluggable_handler::create([](const message_container &msg, handler_interface::response_cb respond) {
		respond(message_container("socket", "id1", {"Hello!"}));
	});

	r.add_socket("socket", socket);
	r.add_async_handler({"socket"}, handler);

	std::thread thread([&r]() { r.start_loop(); });
	std::this_thread::sleep_for(std::chrono::milliseconds(10));

	socket->send_message_local(message_container("", "id1", {"Hello??"}));
	std::this_thread::sleep_for(std::chrono::milliseconds(10));

	EXPECT_THAT(handler->received, ElementsAre(message_container("socket", "id1", {"Hello??"})));

	message_container message;
	socket->receive_message_local(message);

	EXPECT_EQ("id1", message.identity);
	EXPECT_THAT(message.data, ElementsAre("Hello!"));

	r.terminate();
	thread.join();
}

TEST(reactor, timers)
{
	auto context = std::make_shared<zmq::context_t>(1);
	reactor r(context);
	auto handler = pluggable_handler::create([](const message_container &msg, handler_interface::response_cb respond) { });

	r.add_async_handler({r.KEY_TIMER}, handler);

	std::thread thread([&r]() { r.start_loop(); });
	std::this_thread::sleep_for(std::chrono::milliseconds(200));

	ASSERT_GE(1, handler->received.size());

	r.terminate();
	thread.join();
}

// Make sure that messages to asynchronous handlers don't get mixed up
TEST(reactor, multiple_asynchronous_handlers)
{
	auto context = std::make_shared<zmq::context_t>(1);
	reactor r(context);

	auto socket_1 = std::make_shared<pair_socket_wrapper>(context, "inproc://asynchronous_handler_1");
	auto socket_2 = std::make_shared<pair_socket_wrapper>(context, "inproc://asynchronous_handler_2");

	auto handler_1 = pluggable_handler::create([](const message_container &msg, handler_interface::response_cb respond) {});
	auto handler_2a = pluggable_handler::create([](const message_container &msg, handler_interface::response_cb respond) {});
	auto handler_2b = pluggable_handler::create([](const message_container &msg, handler_interface::response_cb respond) {});

	size_t message_count = 100;

	r.add_socket("socket_1", socket_1);
	r.add_socket("socket_2", socket_2);
	r.add_async_handler({"socket_1"}, handler_1);
	r.add_async_handler({"socket_2"}, handler_2a);
	r.add_async_handler({"socket_2"}, handler_2b);

	std::thread thread([&r]() { r.start_loop(); });
	std::this_thread::sleep_for(std::chrono::milliseconds(10));

	for (size_t i = 0; i < message_count; i++) {
		socket_1->send_message_local(message_container("", "id1", {"socket_1"}));
		socket_2->send_message_local(message_container("", "id1", {"socket_2"}));
	}

	std::this_thread::sleep_for(std::chrono::milliseconds(10));

	ASSERT_EQ(message_count, handler_1->received.size());
	ASSERT_EQ(message_count, handler_2a->received.size());
	ASSERT_EQ(message_count, handler_2b->received.size());

	for (size_t i = 0; i < message_count; i++) {
		EXPECT_EQ(handler_1->received.at(i), message_container("socket_1", "id1", {"socket_1"}));
		EXPECT_EQ(handler_2a->received.at(i), message_container("socket_2", "id1", {"socket_2"}));
		EXPECT_EQ(handler_2b->received.at(i), message_container("socket_2", "id1", {"socket_2"}));
	}

	r.terminate();
	thread.join();
}
<|endoftext|>


<commit_before>#include <Render/Pipelining/Pipelines/CustomRenderPass/DeferredOnScreen.hh>

#include <Render/Textures/Texture2D.hh>
#include <Render/OpenGLTask/OpenGLState.hh>
#include <Render/GeometryManagement/Painting/Painter.hh>
#include <Culling/Output/RenderPipeline.hh>
#include <Render/ProgramResources/Types/Uniform/Mat4.hh>
#include <Render/ProgramResources/Types/Uniform/Sampler/Sampler2D.hh>
#include <Render/ProgramResources/Types/Uniform/Vec3.hh>
#include <Render/ProgramResources/Types/Uniform/Vec2.hh>
#include <Render/ProgramResources/Types/Uniform/Vec1.hh>
#include <Threads/RenderThread.hpp>
#include <Threads/ThreadManager.hpp>
#include <Core/ConfigurationManager.hpp>
#include <Core/Engine.hh>
#include <Graphic/DRBCameraDrawableList.hpp>

#include "Render/GeometryManagement/SimpleGeometry.hh"

#define DEFERRED_SHADING_SCREEN_VERTEX "deferred_shading/deferred_shader_screen.vp"
#define DEFERRED_SHADING_SCREEN_FRAG "deferred_shading/deferred_shader_screen.fp"

namespace AGE
{
	enum Programs
	{
		PROGRAM_SCREEN = 0,
		PROGRAM_NBR
	};

	DeferredOnScreen::DeferredOnScreen(glm::uvec2 const &screenSize, std::shared_ptr<PaintingManager> painterManager,
		std::shared_ptr<Texture2D> diffuse) :
		ScreenRender(screenSize, painterManager)
	{
		_diffuseInput = diffuse;

		_programs.resize(PROGRAM_NBR);

		auto confManager = GetEngine()->getInstance<ConfigurationManager>();

		auto shaderPath = confManager->getConfiguration<std::string>("ShadersPath");

		// you have to set shader directory in configuration path
		AGE_ASSERT(shaderPath != nullptr);

		auto vertexShaderPath = shaderPath->getValue() + DEFERRED_SHADING_SCREEN_VERTEX;
		auto fragmentShaderPath = shaderPath->getValue() + DEFERRED_SHADING_SCREEN_FRAG;

		_programs[PROGRAM_SCREEN] = std::make_shared<Program>(Program(std::string("basic_3d_render"),
		{
			std::make_shared<UnitProg>(vertexShaderPath, GL_VERTEX_SHADER),
			std::make_shared<UnitProg>(fragmentShaderPath, GL_FRAGMENT_SHADER)
		}));

		Key<Painter> quadPainterKey;

		GetRenderThread()->getQuadGeometry(_quadVertices, quadPainterKey);
		_quadPainter = _painterManager->get_painter(quadPainterKey);
	}

	void DeferredOnScreen::renderPass(const DRBCameraDrawableList &infos)
	{
		SCOPE_profile_gpu_i("DefferedOnScreen");
		SCOPE_profile_cpu_function("RenderTime", "DefferedOnScreen");
		_programs[PROGRAM_SCREEN]->use();
		_programs[PROGRAM_SCREEN]->get_resource<Sampler2D>("screen").set(_diffuseInput);

		OpenGLState::glDisable(GL_BLEND);
		OpenGLState::glDisable(GL_CULL_FACE);
		OpenGLState::glDisable(GL_DEPTH_TEST);
		OpenGLState::glDisable(GL_STENCIL_TEST);
		{
			SCOPE_profile_gpu_i("Overhead pipeline");
			SCOPE_profile_cpu_function("RenderTime", "Overhead pipeline");
			_programs[PROGRAM_SCREEN]->get_resource<Vec2>("resolution").set(glm::vec2(viewport.x, viewport.y));
			_programs[PROGRAM_SCREEN]->get_resource<Vec1>("activated").set(infos.cameraInfos.data.fxaa == true ? 1.0f : 0.f);
		}

		_quadPainter->uniqueDrawBegin(_programs[PROGRAM_SCREEN]);
		_quadPainter->uniqueDraw(GL_TRIANGLES, _programs[PROGRAM_SCREEN], Properties(), _quadVertices);
		_quadPainter->uniqueDrawEnd();
	}

}
<commit_msg>Warning removed<commit_after>#include <Render/Pipelining/Pipelines/CustomRenderPass/DeferredOnScreen.hh>

#include <Render/Textures/Texture2D.hh>
#include <Render/OpenGLTask/OpenGLState.hh>
#include <Render/GeometryManagement/Painting/Painter.hh>
#include <Culling/Output/RenderPipeline.hh>
#include <Render/ProgramResources/Types/Uniform/Mat4.hh>
#include <Render/ProgramResources/Types/Uniform/Sampler/Sampler2D.hh>
#include <Render/ProgramResources/Types/Uniform/Vec3.hh>
#include <Render/ProgramResources/Types/Uniform/Vec2.hh>
#include <Render/ProgramResources/Types/Uniform/Vec1.hh>
#include <Threads/RenderThread.hpp>
#include <Threads/ThreadManager.hpp>
#include <Core/ConfigurationManager.hpp>
#include <Core/Engine.hh>
#include <Graphic/DRBCameraDrawableList.hpp>

#include "Render/GeometryManagement/SimpleGeometry.hh"

#define DEFERRED_SHADING_SCREEN_VERTEX "deferred_shading/deferred_shader_screen.vp"
#define DEFERRED_SHADING_SCREEN_FRAG "deferred_shading/deferred_shader_screen.fp"

namespace AGE
{
	enum Programs
	{
		PROGRAM_SCREEN = 0,
		PROGRAM_NBR
	};

	DeferredOnScreen::DeferredOnScreen(glm::uvec2 const &screenSize, std::shared_ptr<PaintingManager> painterManager,
		std::shared_ptr<Texture2D> diffuse) :
		ScreenRender(screenSize, painterManager)
	{
		_diffuseInput = diffuse;

		_programs.resize(PROGRAM_NBR);

		auto confManager = GetEngine()->getInstance<ConfigurationManager>();

		auto shaderPath = confManager->getConfiguration<std::string>("ShadersPath");

		// you have to set shader directory in configuration path
		AGE_ASSERT(shaderPath != nullptr);

		auto vertexShaderPath = shaderPath->getValue() + DEFERRED_SHADING_SCREEN_VERTEX;
		auto fragmentShaderPath = shaderPath->getValue() + DEFERRED_SHADING_SCREEN_FRAG;

		_programs[PROGRAM_SCREEN] = std::make_shared<Program>(Program(std::string("basic_3d_render"),
		{
			std::make_shared<UnitProg>(vertexShaderPath, GL_VERTEX_SHADER),
			std::make_shared<UnitProg>(fragmentShaderPath, GL_FRAGMENT_SHADER)
		}));

		Key<Painter> quadPainterKey;

		GetRenderThread()->getQuadGeometry(_quadVertices, quadPainterKey);
		_quadPainter = _painterManager->get_painter(quadPainterKey);
	}

	void DeferredOnScreen::renderPass(const DRBCameraDrawableList &infos)
	{
		SCOPE_profile_gpu_i("DefferedOnScreen");
		SCOPE_profile_cpu_function("RenderTime");
		_programs[PROGRAM_SCREEN]->use();
		_programs[PROGRAM_SCREEN]->get_resource<Sampler2D>("screen").set(_diffuseInput);

		OpenGLState::glDisable(GL_BLEND);
		OpenGLState::glDisable(GL_CULL_FACE);
		OpenGLState::glDisable(GL_DEPTH_TEST);
		OpenGLState::glDisable(GL_STENCIL_TEST);
		{
			SCOPE_profile_gpu_i("Overhead pipeline");
			SCOPE_profile_cpu_i("RenderTime", "Overhead pipeline");
			_programs[PROGRAM_SCREEN]->get_resource<Vec2>("resolution").set(glm::vec2(viewport.x, viewport.y));
			_programs[PROGRAM_SCREEN]->get_resource<Vec1>("activated").set(infos.cameraInfos.data.fxaa == true ? 1.0f : 0.f);
		}

		_quadPainter->uniqueDrawBegin(_programs[PROGRAM_SCREEN]);
		_quadPainter->uniqueDraw(GL_TRIANGLES, _programs[PROGRAM_SCREEN], Properties(), _quadVertices);
		_quadPainter->uniqueDrawEnd();
	}

}
<|endoftext|>


<commit_before>#include <QFile>
#include <QtEndian>
#include <QMessageBox>
#include "level.h"

#define CHUNK_TABLE 0x14

// TODO: spin this shit into an actual file class like KALE/KDCE have
bool bigEndian = false;

typedef uint16_t u16;
typedef int16_t i16;
typedef uint32_t u32;
typedef int32_t i32;

template <typename type> type readNum(QFile &file) {
    type num;
    file.read((char*)&num, sizeof(type));

    if (bigEndian)
        return qFromBigEndian<type>((uchar*)&num);

    return num;
}

uint chunkOffset(QFile &file, uint chunk) {
    file.seek(CHUNK_TABLE + (4 * chunk));
    return readNum<u32>(file);
}

void seekChunk(QFile& file, uint chunk) {
    file.seek(chunkOffset(file, chunk));
}

void LevelData::loadBreakable(QFile &file, uint chunk) {

    seekChunk(file, chunk);
    // read width/height
    uint width = readNum<u32>(file);
    uint height = readNum<u32>(file);

    this->width = width;
    this->height = height;

    this->blocks.resize(height);
    // read info
    for (int y = height - 1; y >= 0; y--) {
        this->blocks[y].resize(width);
        for (uint x = 0; x < width; x++) {
            this->blocks[y][x].breakable = readNum<i16>(file);
        }
    }
}

void LevelData::loadCollision(QFile &file, uint chunk) {

    seekChunk(file, chunk);
    // read pointer
    uint ptr = readNum<u32>(file);
    file.seek(ptr);
    // read info
    uint width = readNum<u32>(file);
    uint height = readNum<u32>(file);

    // is there a mismatch between the width/height given here and elsewhere?
    if (width != this->width || height != this->height)
        printf("data3 size mismatch: (%u, %u) != (%u, %u)\n",
               this->width, this->height, width, height);

    for (int y = height - 1; y >= 0; y--) {
         for (uint x = 0; x < width; x++) {
             this->blocks[y][x].collision = readNum<u32>(file);
         }
    }
}

void LevelData::loadCollisionRTDL(QFile &file, uint chunk) {

    seekChunk(file, chunk);

    // dunno what these are
    // (this is the only difference between this part and its
    //  corresponding part in Triple Deluxe)
    printf("RTDL chunk 3 unknown = 0x%X\n", readNum<u32>(file));

    // read pointer
    uint ptr = readNum<u32>(file);
    file.seek(ptr);

    // read info
    uint width = readNum<u32>(file);
    uint height = readNum<u32>(file);

    // do this here since it's the first chunk read for RTDL right now
    this->width = width;
    this->height = height;
    this->blocks.resize(height);

    // is there a mismatch between the width/height given here and elsewhere?
    if (width != this->width || height != this->height)
        printf("data3 size mismatch: (%u, %u) != (%u, %u)\n",
               this->width, this->height, width, height);

    for (int y = height - 1; y >= 0; y--) {
        // do this here too for now
        this->blocks[y].resize(width);
        for (uint x = 0; x < width; x++) {
            uint tileInfo = readNum<u32>(file);
            // i don't know how this shit works but this should at least give
            // us something to look at (pretty sure it's a bitfield but it doesn't
            // work the same way as TDX's collision data does for drawing purposes
            // but this might give us the same sort of view...
            this->blocks[y][x].collision = tileInfo >> 24;

            // and this (TODO: read the actual breakable data from somewhere)
            this->blocks[y][x].breakable = -1;
        }
    }
}

void LevelData::loadVisual(QFile &file, uint chunk) {
    uint ptrs[3];

    seekChunk(file, chunk);
    // get two unknown values
    this->unknown1 = readNum<u32>(file);
    this->unknown2 = readNum<u32>(file);
    printf("chunk 4 unknown 1 = 0x%X unknown 2 = 0x%X\n", this->unknown1, this->unknown2);
    // get pointers to body
    ptrs[0] = readNum<u32>(file);
    ptrs[1] = readNum<u32>(file);
    ptrs[2] = readNum<u32>(file);
    // get data
    for (uint i = 0; i < 3; i++) {
        file.seek(ptrs[i]);
        uint width = readNum<u32>(file);
        uint height = readNum<u32>(file);

        // is there a mismatch between the width/height given here and elsewhere?
        if (width != this->width || height != this->height)
            printf("data4 size mismatch (body %u): (%u, %u) != (%u, %u)\n",
                   i, this->width, this->height, width, height);

        this->blocks.resize(height);
        for (int y = height - 1; y >= 0; y--) {
             for (uint x = 0; x < width && this->width; x++) {
                 this->blocks[y].resize(width);
                 this->blocks[y][x].visual[i].first = readNum<i16>(file);
                 this->blocks[y][x].visual[i].second = readNum<u16>(file);
             }
        }
    }

}

void LevelData::loadEnemies(QFile &file, uint chunk) {

    seekChunk(file, chunk);
    uint count = readNum<u32>(file);
    for (uint i = 0; i < count; i++) {
        enemy_t enemy;

        // save position, read name
        uint namePtr = readNum<u32>(file);
        uint tempPtr = file.pos();
        file.seek(namePtr);
        uint size = readNum<u32>(file);
        enemy.name = file.read(size);

        // seek back
        file.seek(tempPtr);

        // TODO: update with known fields
        enemy.data1[0] = readNum<i32>(file);
        enemy.data1[1] = readNum<i32>(file);
        enemy.data1[2] = readNum<i32>(file);

        enemy.type = readNum<i32>(file);
        enemy.x = readNum<i32>(file);
        enemy.y = readNum<i32>(file);

        enemy.data2[0] = readNum<i32>(file);
        enemy.data2[1] = readNum<i32>(file);

        this->enemies.push_back(enemy);
    }
}

void LevelData::loadEnemyTypes(QFile &file, uint chunk) {

    seekChunk(file, chunk);
    uint count = readNum<u32>(file);
    for (uint i = 0; i < count; i++) {
        enemytype_t type;
        // name pointer
        uint namePtr = readNum<u32>(file);
        // state pointer
        uint statePtr = readNum<u32>(file);

        // save pos
        uint tempPtr = file.pos();

        // get name
        file.seek(namePtr);
        uint size = readNum<u32>(file);
        type.name = file.read(size);
        // get state
        file.seek(statePtr);
        size = readNum<u32>(file);
        type.state = file.read(size);

        this->enemyTypes.push_back(type);

        // seek back
        file.seek(tempPtr);
    }
}

void LevelData::loadMusic(QFile &file, uint chunk) {

    // TODO: other stuff besides filename, maybe
    seekChunk(file, chunk);
    uint ptr = readNum<u32>(file);
    file.seek(ptr);
    uint count= readNum<u32>(file);
    this->musicName = file.read(count);
}

void LevelData::loadObjects(QFile &file, uint chunk) {

    seekChunk(file, chunk);
    uint objListPtr = readNum<u32>(file);
    uint nameListPtr = readNum<u32>(file);
    // object table
    file.seek(objListPtr);
    uint count = readNum<u32>(file);

    this->objects.resize(count);
    for (uint i = 0; i < count; i++) {
        // TODO: update with known fields
        this->objects[i].x = readNum<u32>(file);
        this->objects[i].y = readNum<u32>(file);
        this->objects[i].type = readNum<u32>(file);

        this->objects[i].unknown = readNum<i32>(file);
        this->objects[i].enabled = readNum<i32>(file);

        for (uint j = 0; j < 8; j++) {
            this->objects[i].params[j] = readNum<i32>(file);
        }
    }
    // object names
    file.seek(nameListPtr);
    count = readNum<u32>(file);

    this->objectNames.resize(count);
    for (uint i = 0; i < count; i++) {
        uint namePtr = readNum<u32>(file);
        uint tempPtr = file.pos();
        file.seek(namePtr);
        uint size = readNum<u32>(file);
        this->objectNames[i] = file.read(size);
        file.seek(tempPtr);
    }
}

void LevelData::loadItems(QFile &file, uint chunk) {

    seekChunk(file, chunk);
    uint count = readNum<u32>(file);
    this->items.resize(count);
    for (uint i = 0; i < count; i++) {
        for (uint j = 0; j < 3; j++) {
            this->items[i].data[j] = readNum<i32>(file);
        }
        this->items[i].x = readNum<u32>(file);
        this->items[i].y = readNum<u32>(file);
        this->items[i].data2 = readNum<u32>(file);
    }
}

bool LevelData::open(QFile& file) {
    this->clear();

    file.seek(4);
    bigEndian = file.read(2) == "\x12\x34";

    if (!bigEndian && chunkOffset(file, 9) == 0x12345678) {
        // main game map
        loadBreakable(file, 0);
        // TODO: check if map data 2 is ever actually used
        loadCollision(file, 2);
        loadVisual(file, 3);
        loadEnemies(file, 4);
        loadEnemyTypes(file, 5);
        loadMusic(file, 6);
        loadObjects(file, 7);
        loadItems(file, 8);
    } else if (!bigEndian && chunkOffset(file, 5) == 0x12345678) {
        // Kirby Fighters map
        loadBreakable(file, 0);
        loadCollision(file, 1);
        loadVisual(file, 2);
        loadMusic(file, 3);
        loadObjects(file, 4);
    } else if (bigEndian && chunkOffset(file, 9) == 0x12345678) {
        // Return to Dream Land map
        // just a test...
        loadCollisionRTDL(file, 2);
        loadVisual(file, 4);
    } else {
        QMessageBox::critical(0, "Load Map", "Unrecognized map format.",
                              QMessageBox::Ok);
        return false;
    }

    fflush(stdout);
    return true;
}

void LevelData::clear() {
    this->width = 0;
    this->height = 0;

    this->musicName = "";

    this->blocks.clear();
    this->enemyTypes.clear();
    this->enemies.clear();
    this->objects.clear();
    this->objectNames.clear();
    this->items.clear();
}
<commit_msg>ten little endians<commit_after>#include <QFile>
#include <QtEndian>
#include <QMessageBox>
#include "level.h"

#define CHUNK_TABLE 0x14

// TODO: spin this shit into an actual file class like KALE/KDCE have
bool bigEndian = false;

typedef uint16_t u16;
typedef int16_t i16;
typedef uint32_t u32;
typedef int32_t i32;

template <typename type> type readNum(QFile &file) {
    type num;
    file.read((char*)&num, sizeof(type));

    if (bigEndian)
        return qFromBigEndian<type>((uchar*)&num);

    return qFromLittleEndian<type>((uchar*)&num);
}

uint chunkOffset(QFile &file, uint chunk) {
    file.seek(CHUNK_TABLE + (4 * chunk));
    return readNum<u32>(file);
}

void seekChunk(QFile& file, uint chunk) {
    file.seek(chunkOffset(file, chunk));
}

void LevelData::loadBreakable(QFile &file, uint chunk) {

    seekChunk(file, chunk);
    // read width/height
    uint width = readNum<u32>(file);
    uint height = readNum<u32>(file);

    this->width = width;
    this->height = height;

    this->blocks.resize(height);
    // read info
    for (int y = height - 1; y >= 0; y--) {
        this->blocks[y].resize(width);
        for (uint x = 0; x < width; x++) {
            this->blocks[y][x].breakable = readNum<i16>(file);
        }
    }
}

void LevelData::loadCollision(QFile &file, uint chunk) {

    seekChunk(file, chunk);
    // read pointer
    uint ptr = readNum<u32>(file);
    file.seek(ptr);
    // read info
    uint width = readNum<u32>(file);
    uint height = readNum<u32>(file);

    // is there a mismatch between the width/height given here and elsewhere?
    if (width != this->width || height != this->height)
        printf("data3 size mismatch: (%u, %u) != (%u, %u)\n",
               this->width, this->height, width, height);

    for (int y = height - 1; y >= 0; y--) {
         for (uint x = 0; x < width; x++) {
             this->blocks[y][x].collision = readNum<u32>(file);
         }
    }
}

void LevelData::loadCollisionRTDL(QFile &file, uint chunk) {

    seekChunk(file, chunk);

    // dunno what these are
    // (this is the only difference between this part and its
    //  corresponding part in Triple Deluxe)
    printf("RTDL chunk 3 unknown = 0x%X\n", readNum<u32>(file));

    // read pointer
    uint ptr = readNum<u32>(file);
    file.seek(ptr);

    // read info
    uint width = readNum<u32>(file);
    uint height = readNum<u32>(file);

    // do this here since it's the first chunk read for RTDL right now
    this->width = width;
    this->height = height;
    this->blocks.resize(height);

    // is there a mismatch between the width/height given here and elsewhere?
    if (width != this->width || height != this->height)
        printf("data3 size mismatch: (%u, %u) != (%u, %u)\n",
               this->width, this->height, width, height);

    for (int y = height - 1; y >= 0; y--) {
        // do this here too for now
        this->blocks[y].resize(width);
        for (uint x = 0; x < width; x++) {
            uint tileInfo = readNum<u32>(file);
            // i don't know how this shit works but this should at least give
            // us something to look at (pretty sure it's a bitfield but it doesn't
            // work the same way as TDX's collision data does for drawing purposes
            // but this might give us the same sort of view...
            this->blocks[y][x].collision = tileInfo >> 24;

            // and this (TODO: read the actual breakable data from somewhere)
            this->blocks[y][x].breakable = -1;
        }
    }
}

void LevelData::loadVisual(QFile &file, uint chunk) {
    uint ptrs[3];

    seekChunk(file, chunk);
    // get two unknown values
    this->unknown1 = readNum<u32>(file);
    this->unknown2 = readNum<u32>(file);
    printf("chunk 4 unknown 1 = 0x%X unknown 2 = 0x%X\n", this->unknown1, this->unknown2);
    // get pointers to body
    ptrs[0] = readNum<u32>(file);
    ptrs[1] = readNum<u32>(file);
    ptrs[2] = readNum<u32>(file);
    // get data
    for (uint i = 0; i < 3; i++) {
        file.seek(ptrs[i]);
        uint width = readNum<u32>(file);
        uint height = readNum<u32>(file);

        // is there a mismatch between the width/height given here and elsewhere?
        if (width != this->width || height != this->height)
            printf("data4 size mismatch (body %u): (%u, %u) != (%u, %u)\n",
                   i, this->width, this->height, width, height);

        this->blocks.resize(height);
        for (int y = height - 1; y >= 0; y--) {
             for (uint x = 0; x < width && this->width; x++) {
                 this->blocks[y].resize(width);
                 this->blocks[y][x].visual[i].first = readNum<i16>(file);
                 this->blocks[y][x].visual[i].second = readNum<u16>(file);
             }
        }
    }

}

void LevelData::loadEnemies(QFile &file, uint chunk) {

    seekChunk(file, chunk);
    uint count = readNum<u32>(file);
    for (uint i = 0; i < count; i++) {
        enemy_t enemy;

        // save position, read name
        uint namePtr = readNum<u32>(file);
        uint tempPtr = file.pos();
        file.seek(namePtr);
        uint size = readNum<u32>(file);
        enemy.name = file.read(size);

        // seek back
        file.seek(tempPtr);

        // TODO: update with known fields
        enemy.data1[0] = readNum<i32>(file);
        enemy.data1[1] = readNum<i32>(file);
        enemy.data1[2] = readNum<i32>(file);

        enemy.type = readNum<i32>(file);
        enemy.x = readNum<i32>(file);
        enemy.y = readNum<i32>(file);

        enemy.data2[0] = readNum<i32>(file);
        enemy.data2[1] = readNum<i32>(file);

        this->enemies.push_back(enemy);
    }
}

void LevelData::loadEnemyTypes(QFile &file, uint chunk) {

    seekChunk(file, chunk);
    uint count = readNum<u32>(file);
    for (uint i = 0; i < count; i++) {
        enemytype_t type;
        // name pointer
        uint namePtr = readNum<u32>(file);
        // state pointer
        uint statePtr = readNum<u32>(file);

        // save pos
        uint tempPtr = file.pos();

        // get name
        file.seek(namePtr);
        uint size = readNum<u32>(file);
        type.name = file.read(size);
        // get state
        file.seek(statePtr);
        size = readNum<u32>(file);
        type.state = file.read(size);

        this->enemyTypes.push_back(type);

        // seek back
        file.seek(tempPtr);
    }
}

void LevelData::loadMusic(QFile &file, uint chunk) {

    // TODO: other stuff besides filename, maybe
    seekChunk(file, chunk);
    uint ptr = readNum<u32>(file);
    file.seek(ptr);
    uint count= readNum<u32>(file);
    this->musicName = file.read(count);
}

void LevelData::loadObjects(QFile &file, uint chunk) {

    seekChunk(file, chunk);
    uint objListPtr = readNum<u32>(file);
    uint nameListPtr = readNum<u32>(file);
    // object table
    file.seek(objListPtr);
    uint count = readNum<u32>(file);

    this->objects.resize(count);
    for (uint i = 0; i < count; i++) {
        // TODO: update with known fields
        this->objects[i].x = readNum<u32>(file);
        this->objects[i].y = readNum<u32>(file);
        this->objects[i].type = readNum<u32>(file);

        this->objects[i].unknown = readNum<i32>(file);
        this->objects[i].enabled = readNum<i32>(file);

        for (uint j = 0; j < 8; j++) {
            this->objects[i].params[j] = readNum<i32>(file);
        }
    }
    // object names
    file.seek(nameListPtr);
    count = readNum<u32>(file);

    this->objectNames.resize(count);
    for (uint i = 0; i < count; i++) {
        uint namePtr = readNum<u32>(file);
        uint tempPtr = file.pos();
        file.seek(namePtr);
        uint size = readNum<u32>(file);
        this->objectNames[i] = file.read(size);
        file.seek(tempPtr);
    }
}

void LevelData::loadItems(QFile &file, uint chunk) {

    seekChunk(file, chunk);
    uint count = readNum<u32>(file);
    this->items.resize(count);
    for (uint i = 0; i < count; i++) {
        for (uint j = 0; j < 3; j++) {
            this->items[i].data[j] = readNum<i32>(file);
        }
        this->items[i].x = readNum<u32>(file);
        this->items[i].y = readNum<u32>(file);
        this->items[i].data2 = readNum<u32>(file);
    }
}

bool LevelData::open(QFile& file) {
    this->clear();

    file.seek(4);
    bigEndian = file.read(2) == "\x12\x34";

    if (!bigEndian && chunkOffset(file, 9) == 0x12345678) {
        // main game map
        loadBreakable(file, 0);
        // TODO: check if map data 2 is ever actually used
        loadCollision(file, 2);
        loadVisual(file, 3);
        loadEnemies(file, 4);
        loadEnemyTypes(file, 5);
        loadMusic(file, 6);
        loadObjects(file, 7);
        loadItems(file, 8);
    } else if (!bigEndian && chunkOffset(file, 5) == 0x12345678) {
        // Kirby Fighters map
        loadBreakable(file, 0);
        loadCollision(file, 1);
        loadVisual(file, 2);
        loadMusic(file, 3);
        loadObjects(file, 4);
    } else if (bigEndian && chunkOffset(file, 9) == 0x12345678) {
        // Return to Dream Land map
        // just a test...
        loadCollisionRTDL(file, 2);
        loadVisual(file, 4);
    } else {
        QMessageBox::critical(0, "Load Map", "Unrecognized map format.",
                              QMessageBox::Ok);
        return false;
    }

    fflush(stdout);
    return true;
}

void LevelData::clear() {
    this->width = 0;
    this->height = 0;

    this->musicName = "";

    this->blocks.clear();
    this->enemyTypes.clear();
    this->enemies.clear();
    this->objects.clear();
    this->objectNames.clear();
    this->items.clear();
}
<|endoftext|>


<commit_before>/**
 *  Function.cpp
 *
 *  Implementation for the function class
 *
 *  @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
 *  @copyright 2013 Copernica BV
 */
#include "includes.h"

/**
 *  Set up namespace
 */
namespace Php {

/**
 *  Function that is called by the Zend engine every time that a function gets called
 *  @param  ht
 *  @param  return_value
 *  @param  return_value_ptr
 *  @param  this_ptr
 *  @param  return_value_used
 *  @param  tsrm_ls
 *  @return integer
 */
void Callable::invoke(INTERNAL_FUNCTION_PARAMETERS)
{
    // find the function name
    const char *name = get_active_function_name(TSRMLS_C);

    // uncover the hidden pointer inside the function name
    Callable *callable = HiddenPointer<Callable>(name);

    // construct parameters
    ParametersImpl params(this_ptr, ZEND_NUM_ARGS() TSRMLS_CC);

    // the function could throw an exception
    try
    {
        // get the result
        Value result(callable->invoke(params));

        // detach the zval (we don't want it to be destructed)
        zval *val = result.detach();

        // @todo php 5.6 has a RETVAL_ZVAL_FAST macro that can be used instead (and is faster)

        // return a full copy of the zval, and do not destruct it
        RETVAL_ZVAL(val, 1, 0);
    }
    catch (Exception &exception)
    {
        // process the exception
        process(exception TSRMLS_CC);
    }
}

/**
 *  Fill a function entry
 *
 *  This method is called when the extension is registering itself, when the
 *  function or method introces himself
 *
 *  @param  entry       Entry to be filled
 *  @param  classname   Optional class name
 *  @param  flags       Is this a public property?
 */
void Callable::initialize(zend_function_entry *entry, const char *classname, int flags) const
{
    // fill the members of the entity, and hide a pointer to the current object in the name
    entry->fname = (const char *)_ptr;
    entry->handler = &Callable::invoke;
    entry->arg_info = _argv;
    entry->num_args = _argc;
    entry->flags = flags;

    // we should fill the first argument as well
    initialize((zend_arg_info *)entry->arg_info, classname);
}

/**
 *  Fill a function entry
 *  @param  info        Info to be filled
 *  @param  classname   Optional classname
 */
void Callable::initialize(zend_arg_info *info, const char *classname) const
{
#if PHP_VERSION_ID >= 50400
    // up until php 5.3, the first info object is filled with alternative information,
    // later it is casted to a zend_internal_function object
    auto *finfo = (zend_internal_function_info *)info;

    // fill in all the members, note that return reference is false by default,
    // because we do not support returning references in PHP-CPP, although Zend
    // engine allows it. Inside the name we hide a pointer to the current object
    finfo->_name = _ptr;
    finfo->_name_len = ::strlen(_ptr);
    finfo->_class_name = classname;

    // number of required arguments, and the expected return type
    finfo->required_num_args = _required;
    finfo->_type_hint = (unsigned char)_return;

    // we do not support return-by-reference
    finfo->return_reference = false;

# if PHP_VERSION_ID >= 50600
    // since php 5.6 there are _allow_null and _is_variadic properties. It's
    // not exactly clear what they do (@todo find this out) so for now we set
    // them to false
    finfo->_allow_null = false;
    finfo->_is_variadic = false;

# else
    // passing by reference is not used (only for php 5.4 and php 5.5)
    finfo->pass_rest_by_reference = false;
# endif

#else
    // php 5.3 code
    info->name = nullptr;
    info->name_len = 0;
    info->class_name = nullptr;
    info->class_name_len = 0;
    info->array_type_hint = false;
    info->allow_null = false;
    info->pass_by_reference = false;
    info->return_reference = false;
    info->required_num_args = _required;
#endif
}

/**
 *  End of namespace
 */
}

<commit_msg>generate warning when a function is called with not enough parameters<commit_after>/**
 *  Function.cpp
 *
 *  Implementation for the function class
 *
 *  @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
 *  @copyright 2013 Copernica BV
 */
#include "includes.h"

/**
 *  Set up namespace
 */
namespace Php {

/**
 *  Function that is called by the Zend engine every time that a function gets called
 *  @param  ht
 *  @param  return_value
 *  @param  return_value_ptr
 *  @param  this_ptr
 *  @param  return_value_used
 *  @param  tsrm_ls
 *  @return integer
 */
void Callable::invoke(INTERNAL_FUNCTION_PARAMETERS)
{
    // find the function name
    const char *name = get_active_function_name(TSRMLS_C);

    // uncover the hidden pointer inside the function name
    Callable *callable = HiddenPointer<Callable>(name);

    // check if sufficient parameters were passed (for some reason this check
    // is not done by Zend, so we do it here ourselves)
    if (ZEND_NUM_ARGS() < callable->_required)
    {
        // PHP itself only generates a warning when this happens, so we do the same too
        Php::warning << name << "() expects at least " << callable->_required << " parameters, " << ZEND_NUM_ARGS() << " given" << std::flush;

        // and we return null
        RETURN_NULL();
    }
    else
    {
        // construct parameters
        ParametersImpl params(this_ptr, ZEND_NUM_ARGS() TSRMLS_CC);

        // the function could throw an exception
        try
        {
            // get the result
            Value result(callable->invoke(params));

            // detach the zval (we don't want it to be destructed)
            zval *val = result.detach();

            // @todo php 5.6 has a RETVAL_ZVAL_FAST macro that can be used instead (and is faster)

            // return a full copy of the zval, and do not destruct it
            RETVAL_ZVAL(val, 1, 0);
        }
        catch (Exception &exception)
        {
            // process the exception
            process(exception TSRMLS_CC);
        }
    }
}

/**
 *  Fill a function entry
 *
 *  This method is called when the extension is registering itself, when the
 *  function or method introces himself
 *
 *  @param  entry       Entry to be filled
 *  @param  classname   Optional class name
 *  @param  flags       Is this a public property?
 */
void Callable::initialize(zend_function_entry *entry, const char *classname, int flags) const
{
    // fill the members of the entity, and hide a pointer to the current object in the name
    entry->fname = (const char *)_ptr;
    entry->handler = &Callable::invoke;
    entry->arg_info = _argv;
    entry->num_args = _argc;
    entry->flags = flags;

    // we should fill the first argument as well
    initialize((zend_arg_info *)entry->arg_info, classname);
}

/**
 *  Fill a function entry
 *  @param  info        Info to be filled
 *  @param  classname   Optional classname
 */
void Callable::initialize(zend_arg_info *info, const char *classname) const
{
#if PHP_VERSION_ID >= 50400
    // up until php 5.3, the first info object is filled with alternative information,
    // later it is casted to a zend_internal_function object
    auto *finfo = (zend_internal_function_info *)info;

    // fill in all the members, note that return reference is false by default,
    // because we do not support returning references in PHP-CPP, although Zend
    // engine allows it. Inside the name we hide a pointer to the current object
    finfo->_name = _ptr;
    finfo->_name_len = ::strlen(_ptr);
    finfo->_class_name = classname;

    // number of required arguments, and the expected return type
    finfo->required_num_args = _required;
    finfo->_type_hint = (unsigned char)_return;

    // we do not support return-by-reference
    finfo->return_reference = false;

# if PHP_VERSION_ID >= 50600
    // since php 5.6 there are _allow_null and _is_variadic properties. It's
    // not exactly clear what they do (@todo find this out) so for now we set
    // them to false
    finfo->_allow_null = false;
    finfo->_is_variadic = false;

# else
    // passing by reference is not used (only for php 5.4 and php 5.5)
    finfo->pass_rest_by_reference = false;
# endif

#else
    // php 5.3 code
    info->name = nullptr;
    info->name_len = 0;
    info->class_name = nullptr;
    info->class_name_len = 0;
    info->array_type_hint = false;
    info->allow_null = false;
    info->pass_by_reference = false;
    info->return_reference = false;
    info->required_num_args = _required;
#endif
}

/**
 *  End of namespace
 */
}

<|endoftext|>


<commit_before>#include "lexer.h"

#include <string>

#include "lexerexception.h"
#include "log.h"
#include "scriptsource.h"
#include "token.h"
#include "tokenfactory.h"

namespace peachy {

  Lexer::~Lexer() {
    logger->debug("Lexer destructor");
  }

  Token * Lexer::nextToken() {
    logger->debug("Lexer::nextToken()");

    Token * token;
    bool gotToken = false;

    while(!gotToken) {
      if(atEndOfLine()) {
        logger->debug("Overflowed input buffer");
        throw LexerException("Overflowed input buffer");
      }
      currentChar = currentLine[currentPos];
      switch(state) {
        case LEXER_COMPLETE:
          logger->debug("In state LEXER_COMPLETE");
          token = tokenFactory->createToken(TOKEN_EOF);
          resetToken();
          gotToken = true;
          break;
        case LEXER_DEFAULT:
          logger->debug("In state LEXER_DEFAULT");
          switch(currentChar) {
            case ' ':
            case '\t':
              logger->debug("Whitespace");
              consume(false);
              break;
            case '\n':
            case '\r':
            case NULL:
              logger->debug("End of line");
              resetLine();
              break;
            case ':':
              logger->debug("Colon");
              token = tokenFactory->createToken(TOKEN_COLON);
              resetToken();
              gotToken = true;
              break;
            case '{':
              logger->debug("Left brace");
              token = tokenFactory->createToken(TOKEN_LEFT_BRACE);
              resetToken();
              gotToken = true;
              break;
            case '[':
              logger->debug("Left bracket");
              token = tokenFactory->createToken(TOKEN_LEFT_BRACKET);
              resetToken();
              gotToken = true;
              break;
            case '(':
              logger->debug("Left paren");
              token = tokenFactory->createToken(TOKEN_LEFT_PARENTHESIS);
              resetToken();
              gotToken = true;
              break;
            case '}':
              logger->debug("Right brace");
              token = tokenFactory->createToken(TOKEN_RIGHT_BRACE);
              resetToken();
              gotToken = true;
              break;
            case ']':
              logger->debug("Right bracket");
              token = tokenFactory->createToken(TOKEN_RIGHT_BRACKET);
              resetToken();
              gotToken = true;
              break;
            case ')':
              logger->debug("Right paren");
              token = tokenFactory->createToken(TOKEN_RIGHT_PARENTHESIS);
              resetToken();
              gotToken = true;
              break;
            case '#':
              logger->debug("Comment line");
              setState(LEXER_IN_COMMENT_LINE);
              consume(false);
              break;
            case '"':
              logger->debug("Start of string");
              setState(LEXER_IN_STRING);
              consume(false);
              break;
            default:
              if(isNumeric(currentChar)) {
                logger->debug("Current char is a number");
                setState(LEXER_IN_NUMBER);
                consume(true);
              } else if(isLetter(currentChar)) {
                logger->debug("Current char is a letter");
                setState(LEXER_IN_IDENTIFIER);
                consume(true);
              } else if(isOperatorChar(currentChar)) {
                logger->debug("Current char is an operator");
                setState(LEXER_IN_OPERATOR);
                consume(true);
              } else {
                throw LexerException(
                  std::string("Invalid character encountered: ").append(
                    1, currentChar));
              }
          }
          break;
        case LEXER_IN_STRING:
          logger->debug("In state LEXER_IN_STRING");
          switch(currentChar) {
            case '"':
              logger->debug("End quote");
              token = tokenFactory->createToken(TOKEN_STRING, currentSequence);
              consume(false);
              resetToken();
              gotToken = true;
              break;
            case 0:
              logger->debug("Newline found");
              currentSequence.append(1, '\n');
              logger->debug(currentSequence);
              if(!scriptSource->hasMoreLines()) {
                logger->debug("End of input but still inside string");
                throw LexerException("Input terminated inside open string");
              } else {
                logger->debug("Getting a new line from script source");
                setCurrentLine(scriptSource->getLine());
                currentPos = 0;
              }
              break;
            default:
              logger->debug("Ordinary character");
              consume(true);
              logger->debug(currentSequence);
              break;
          }
          break;
        case LEXER_IN_NUMBER:
          logger->debug("In state LEXER_IN_NUMBER");
          if(isNumeric(currentChar)) {
            logger->debug("Another digit of the current number");
            consume(true);
          } else {
            logger->debug("End of number");
            token = tokenFactory->createToken(TOKEN_NUMBER, currentSequence);
            resetToken();
            gotToken = true;
          }
          break;
        case LEXER_IN_IDENTIFIER:
          logger->debug("In state LEXER_IN_IDENTIFIER");
          if(isIdentifier(currentChar)) {
            logger->debug("Another character of the current identifier");
            consume(true);
          } else {
            logger->debug("End of identifier");
            if(isKeyword(currentSequence)) {
              token = tokenFactory->createToken(TOKEN_KEYWORD, currentSequence);
            } else {
              token = tokenFactory->createToken(TOKEN_IDENTIFIER,
                currentSequence);
            }
            resetToken();
            gotToken = true;
          }
          break;
        case LEXER_IN_OPERATOR:
          logger->debug("In state LEXER_IN_OPERATOR");
          if(isOperatorChar(currentChar)) {
            logger->debug("Another character of the current operator");
            consume(true);
          } else if(isNumeric(currentChar) &&
            currentSequence.compare("-") == 0) {
            logger->debug("Looks like a negative number");
            setState(LEXER_IN_NUMBER);
            consume(true);
          } else {
            if(isOperator(currentSequence)) {
              logger->debug("End of operator");
              token = tokenFactory->createToken(TOKEN_OPERATOR, currentSequence);
              resetToken();
              gotToken = true;
            } else {
              logger->debug("Invalid operator sequence");
              throw LexerException("Invalid operator sequence");
            }
          }
          break;
        case LEXER_IN_COMMENT_LINE:
          logger->debug("In state LEXER_IN_COMMENT_LINE");
          if(isLineEnding(currentChar)) {
            logger->debug("End of comment");
            token = tokenFactory->createToken(TOKEN_COMMENT_LINE,
              currentSequence);
            resetToken();
            gotToken = true;
          } else {
            logger->debug("Another character of the current comment");
            consume(true);
          }
          break;
        case LEXER_NEED_INPUT:
          logger->debug("In state LEXER_NEED_INPUT");
          if(!scriptSource->hasMoreLines()) {
            logger->debug("End of input");
            setState(LEXER_COMPLETE);
          } else {
            setCurrentLine(scriptSource->getLine());
            logger->debug("Got new line from script source");
            resetToken();
          }
          break;
        case LEXER_ERROR:
          logger->debug("In state LEXER_ERROR");
          throw LexerException("Invalid lexer state");
          break;
        default:
          logger->debug("In an unknown state");
          setState(LEXER_ERROR);
          break;
      }
    }

    return token;
  }

  void Lexer::consume(bool appendChar) {
    logger->debug("Lexer::consume()");
    if(appendChar) {
      currentSequence.append(1, currentChar);
    }
    currentPos++;
  }

  bool Lexer::atEndOfLine() {
    return(currentPos > currentLine.length());
  }

  void Lexer::resetLine() {
    logger->debug("Lexer::resetLine()");
    setState(LEXER_NEED_INPUT);
    currentPos = 0;
  }

  void Lexer::resetToken() {
    logger->debug("Lexer::resetToken()");
    setState(LEXER_DEFAULT);
    currentSequence = std::string("");
  }

  void Lexer::setState(LexerState state) {
    logger->debug("Lexer::setState()");
    this->state = state;
  }

  void Lexer::setCurrentLine(std::string line) {
    logger->debug("Lexer::setCurrentLine()");
    this->currentLine = line;
  }

  LexerState Lexer::getState() {
    logger->debug("Lexer::getState()");
    return this->state;
  }

  bool Lexer::isNumeric(char c) {
    return (
      c >= '0' &&
      c <= '9'
    );
  }

  bool Lexer::isLetter(char c) {
    return (
      ( c >= 'a' && c <= 'z' ) ||
      ( c >= 'A' && c <= 'Z' )
    );
  }

  bool Lexer::isOperatorChar(char c) {
    return (
      c == '=' ||
      c == '-' ||
      c == '+' ||
      c == '>' ||
      c == '<' ||
      c == '|'
    );
  }

  bool Lexer::isOperator(std::string s) {
    return (
      s.compare("<-") == 0 ||
      s.compare("=") == 0 ||
      s.compare(">=") == 0 ||
      s.compare("<=") == 0 ||
      s.compare("<") == 0 ||
      s.compare(">") == 0 ||
      s.compare("+") == 0 ||
      s.compare("-") == 0 ||
      s.compare("|") == 0 ||
      s.compare("&") == 0
    );
  }

  bool Lexer::isIdentifier(char c) {
    return (
      isLetter(c) ||
      isNumeric(c) ||
      c == '_'
    );
  }

  bool Lexer::isKeyword(std::string s) {
    return (
      s.compare("while") == 0 ||
      s.compare("function") == 0 ||
      s.compare("return") == 0 ||
      s.compare("class") == 0 ||
      s.compare("for") == 0 ||
      s.compare("print") == 0
    );
  }

  bool Lexer::isLineEnding(char c) {
    return (
      c == '\r' ||
      c == '\n'
    );
  }
}

<commit_msg>Allow multiple calls to Lexer::nextToken to return EOF once EOF is reached<commit_after>#include "lexer.h"

#include <string>

#include "lexerexception.h"
#include "log.h"
#include "scriptsource.h"
#include "token.h"
#include "tokenfactory.h"

namespace peachy {

  Lexer::~Lexer() {
    logger->debug("Lexer destructor");
  }

  Token * Lexer::nextToken() {
    logger->debug("Lexer::nextToken()");

    Token * token;
    bool gotToken = false;

    while(!gotToken) {
      if(atEndOfLine()) {
        logger->debug("Overflowed input buffer");
        throw LexerException("Overflowed input buffer");
      }
      currentChar = currentLine[currentPos];
      switch(state) {
        case LEXER_COMPLETE:
          logger->debug("In state LEXER_COMPLETE");
          token = tokenFactory->createToken(TOKEN_EOF);
          gotToken = true;
          break;
        case LEXER_DEFAULT:
          logger->debug("In state LEXER_DEFAULT");
          switch(currentChar) {
            case ' ':
            case '\t':
              logger->debug("Whitespace");
              consume(false);
              break;
            case '\n':
            case '\r':
            case NULL:
              logger->debug("End of line");
              resetLine();
              break;
            case ':':
              logger->debug("Colon");
              token = tokenFactory->createToken(TOKEN_COLON);
              resetToken();
              gotToken = true;
              break;
            case '{':
              logger->debug("Left brace");
              token = tokenFactory->createToken(TOKEN_LEFT_BRACE);
              resetToken();
              gotToken = true;
              break;
            case '[':
              logger->debug("Left bracket");
              token = tokenFactory->createToken(TOKEN_LEFT_BRACKET);
              resetToken();
              gotToken = true;
              break;
            case '(':
              logger->debug("Left paren");
              token = tokenFactory->createToken(TOKEN_LEFT_PARENTHESIS);
              resetToken();
              gotToken = true;
              break;
            case '}':
              logger->debug("Right brace");
              token = tokenFactory->createToken(TOKEN_RIGHT_BRACE);
              resetToken();
              gotToken = true;
              break;
            case ']':
              logger->debug("Right bracket");
              token = tokenFactory->createToken(TOKEN_RIGHT_BRACKET);
              resetToken();
              gotToken = true;
              break;
            case ')':
              logger->debug("Right paren");
              token = tokenFactory->createToken(TOKEN_RIGHT_PARENTHESIS);
              resetToken();
              gotToken = true;
              break;
            case '#':
              logger->debug("Comment line");
              setState(LEXER_IN_COMMENT_LINE);
              consume(false);
              break;
            case '"':
              logger->debug("Start of string");
              setState(LEXER_IN_STRING);
              consume(false);
              break;
            default:
              if(isNumeric(currentChar)) {
                logger->debug("Current char is a number");
                setState(LEXER_IN_NUMBER);
                consume(true);
              } else if(isLetter(currentChar)) {
                logger->debug("Current char is a letter");
                setState(LEXER_IN_IDENTIFIER);
                consume(true);
              } else if(isOperatorChar(currentChar)) {
                logger->debug("Current char is an operator");
                setState(LEXER_IN_OPERATOR);
                consume(true);
              } else {
                throw LexerException(
                  std::string("Invalid character encountered: ").append(
                    1, currentChar));
              }
          }
          break;
        case LEXER_IN_STRING:
          logger->debug("In state LEXER_IN_STRING");
          switch(currentChar) {
            case '"':
              logger->debug("End quote");
              token = tokenFactory->createToken(TOKEN_STRING, currentSequence);
              consume(false);
              resetToken();
              gotToken = true;
              break;
            case 0:
              logger->debug("Newline found");
              currentSequence.append(1, '\n');
              logger->debug(currentSequence);
              if(!scriptSource->hasMoreLines()) {
                logger->debug("End of input but still inside string");
                throw LexerException("Input terminated inside open string");
              } else {
                logger->debug("Getting a new line from script source");
                setCurrentLine(scriptSource->getLine());
                currentPos = 0;
              }
              break;
            default:
              logger->debug("Ordinary character");
              consume(true);
              logger->debug(currentSequence);
              break;
          }
          break;
        case LEXER_IN_NUMBER:
          logger->debug("In state LEXER_IN_NUMBER");
          if(isNumeric(currentChar)) {
            logger->debug("Another digit of the current number");
            consume(true);
          } else {
            logger->debug("End of number");
            token = tokenFactory->createToken(TOKEN_NUMBER, currentSequence);
            resetToken();
            gotToken = true;
          }
          break;
        case LEXER_IN_IDENTIFIER:
          logger->debug("In state LEXER_IN_IDENTIFIER");
          if(isIdentifier(currentChar)) {
            logger->debug("Another character of the current identifier");
            consume(true);
          } else {
            logger->debug("End of identifier");
            if(isKeyword(currentSequence)) {
              token = tokenFactory->createToken(TOKEN_KEYWORD, currentSequence);
            } else {
              token = tokenFactory->createToken(TOKEN_IDENTIFIER,
                currentSequence);
            }
            resetToken();
            gotToken = true;
          }
          break;
        case LEXER_IN_OPERATOR:
          logger->debug("In state LEXER_IN_OPERATOR");
          if(isOperatorChar(currentChar)) {
            logger->debug("Another character of the current operator");
            consume(true);
          } else if(isNumeric(currentChar) &&
            currentSequence.compare("-") == 0) {
            logger->debug("Looks like a negative number");
            setState(LEXER_IN_NUMBER);
            consume(true);
          } else {
            if(isOperator(currentSequence)) {
              logger->debug("End of operator");
              token = tokenFactory->createToken(TOKEN_OPERATOR, currentSequence);
              resetToken();
              gotToken = true;
            } else {
              logger->debug("Invalid operator sequence");
              throw LexerException("Invalid operator sequence");
            }
          }
          break;
        case LEXER_IN_COMMENT_LINE:
          logger->debug("In state LEXER_IN_COMMENT_LINE");
          if(isLineEnding(currentChar)) {
            logger->debug("End of comment");
            token = tokenFactory->createToken(TOKEN_COMMENT_LINE,
              currentSequence);
            resetToken();
            gotToken = true;
          } else {
            logger->debug("Another character of the current comment");
            consume(true);
          }
          break;
        case LEXER_NEED_INPUT:
          logger->debug("In state LEXER_NEED_INPUT");
          if(!scriptSource->hasMoreLines()) {
            logger->debug("End of input");
            setState(LEXER_COMPLETE);
          } else {
            setCurrentLine(scriptSource->getLine());
            logger->debug("Got new line from script source");
            resetToken();
          }
          break;
        case LEXER_ERROR:
          logger->debug("In state LEXER_ERROR");
          throw LexerException("Invalid lexer state");
          break;
        default:
          logger->debug("In an unknown state");
          setState(LEXER_ERROR);
          break;
      }
    }

    return token;
  }

  void Lexer::consume(bool appendChar) {
    logger->debug("Lexer::consume()");
    if(appendChar) {
      currentSequence.append(1, currentChar);
    }
    currentPos++;
  }

  bool Lexer::atEndOfLine() {
    return(currentPos > currentLine.length());
  }

  void Lexer::resetLine() {
    logger->debug("Lexer::resetLine()");
    setState(LEXER_NEED_INPUT);
    currentPos = 0;
  }

  void Lexer::resetToken() {
    logger->debug("Lexer::resetToken()");
    setState(LEXER_DEFAULT);
    currentSequence = std::string("");
  }

  void Lexer::setState(LexerState state) {
    logger->debug("Lexer::setState()");
    this->state = state;
  }

  void Lexer::setCurrentLine(std::string line) {
    logger->debug("Lexer::setCurrentLine()");
    this->currentLine = line;
  }

  LexerState Lexer::getState() {
    logger->debug("Lexer::getState()");
    return this->state;
  }

  bool Lexer::isNumeric(char c) {
    return (
      c >= '0' &&
      c <= '9'
    );
  }

  bool Lexer::isLetter(char c) {
    return (
      ( c >= 'a' && c <= 'z' ) ||
      ( c >= 'A' && c <= 'Z' )
    );
  }

  bool Lexer::isOperatorChar(char c) {
    return (
      c == '=' ||
      c == '-' ||
      c == '+' ||
      c == '>' ||
      c == '<' ||
      c == '|'
    );
  }

  bool Lexer::isOperator(std::string s) {
    return (
      s.compare("<-") == 0 ||
      s.compare("=") == 0 ||
      s.compare(">=") == 0 ||
      s.compare("<=") == 0 ||
      s.compare("<") == 0 ||
      s.compare(">") == 0 ||
      s.compare("+") == 0 ||
      s.compare("-") == 0 ||
      s.compare("|") == 0 ||
      s.compare("&") == 0
    );
  }

  bool Lexer::isIdentifier(char c) {
    return (
      isLetter(c) ||
      isNumeric(c) ||
      c == '_'
    );
  }

  bool Lexer::isKeyword(std::string s) {
    return (
      s.compare("while") == 0 ||
      s.compare("function") == 0 ||
      s.compare("return") == 0 ||
      s.compare("class") == 0 ||
      s.compare("for") == 0 ||
      s.compare("print") == 0
    );
  }

  bool Lexer::isLineEnding(char c) {
    return (
      c == '\r' ||
      c == '\n'
    );
  }
}

<|endoftext|>


<commit_before>//===--- SILWitnessTable.cpp - Defines the SILWitnessTable class ----------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file defines the SILWitnessTable class, which is used to map a protocol
// conformance for a type to its implementing SILFunctions. This information is
// (FIXME will be) used by IRGen to create witness tables for protocol dispatch.
// It can also be used by generic specialization and existential
// devirtualization passes to promote witness_method and protocol_method
// instructions to static function_refs.
//
//===----------------------------------------------------------------------===//

#include "swift/SIL/SILWitnessTable.h"
#include "swift/AST/ASTMangler.h"
#include "swift/AST/Module.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/ClangImporter/ClangModule.h"
#include "swift/SIL/SILModule.h"
#include "llvm/ADT/SmallString.h"

using namespace swift;

static std::string mangleConstant(RootProtocolConformance *C) {
  Mangle::ASTMangler Mangler;
  return Mangler.mangleWitnessTable(C);
}

DeclContext *SILWitnessTable::getDeclContext() const {
  return getConformance()->getDeclContext();
}

ProtocolDecl *SILWitnessTable::getProtocol() const {
  return getConformance()->getProtocol();
}

CanType SILWitnessTable::getConformingType() const {
  return getConformance()->getType()->getCanonicalType();
}

void SILWitnessTable::addWitnessTable() {
  // Make sure we have not seen this witness table yet.
  assert(Mod.WitnessTableMap.find(Conformance) ==
         Mod.WitnessTableMap.end() && "Attempting to create duplicate "
         "witness table.");
  Mod.WitnessTableMap[Conformance] = this;
  Mod.witnessTables.push_back(this);
}

SILWitnessTable *SILWitnessTable::create(
    SILModule &M, SILLinkage Linkage, IsSerialized_t Serialized,
    RootProtocolConformance *Conformance,
    ArrayRef<SILWitnessTable::Entry> entries,
    ArrayRef<ConditionalConformance> conditionalConformances) {
  assert(Conformance && "Cannot create a witness table for a null "
         "conformance.");

  // Create the mangled name of our witness table...
  Identifier Name = M.getASTContext().getIdentifier(mangleConstant(Conformance));

  // Allocate the witness table and initialize it.
  void *buf = M.allocate(sizeof(SILWitnessTable), alignof(SILWitnessTable));
  SILWitnessTable *wt = ::new (buf)
      SILWitnessTable(M, Linkage, Serialized, Name.str(), Conformance, entries,
                      conditionalConformances);

  wt->addWitnessTable();

  // Return the resulting witness table.
  return wt;
}

SILWitnessTable *
SILWitnessTable::create(SILModule &M, SILLinkage Linkage,
                        RootProtocolConformance *Conformance) {
  assert(Conformance && "Cannot create a witness table for a null "
         "conformance.");

  // Create the mangled name of our witness table...
  Identifier Name = M.getASTContext().getIdentifier(mangleConstant(Conformance));

  // Allocate the witness table and initialize it.
  void *buf = M.allocate(sizeof(SILWitnessTable), alignof(SILWitnessTable));
  SILWitnessTable *wt = ::new (buf) SILWitnessTable(M, Linkage, Name.str(),
                                                    Conformance);

  wt->addWitnessTable();

  // Return the resulting witness table.
  return wt;
}

SILWitnessTable::SILWitnessTable(
    SILModule &M, SILLinkage Linkage, IsSerialized_t Serialized, StringRef N,
    RootProtocolConformance *Conformance, ArrayRef<Entry> entries,
    ArrayRef<ConditionalConformance> conditionalConformances)
    : Mod(M), Name(N), Linkage(Linkage), Conformance(Conformance), Entries(),
      ConditionalConformances(), IsDeclaration(true), Serialized(false) {
  convertToDefinition(entries, conditionalConformances, Serialized);
}

SILWitnessTable::SILWitnessTable(SILModule &M, SILLinkage Linkage, StringRef N,
                                 RootProtocolConformance *Conformance)
    : Mod(M), Name(N), Linkage(Linkage), Conformance(Conformance), Entries(),
      ConditionalConformances(), IsDeclaration(true), Serialized(false) {}

SILWitnessTable::~SILWitnessTable() {
  if (isDeclaration())
    return;

  // Drop the reference count of witness functions referenced by this table.
  for (auto entry : getEntries()) {
    switch (entry.getKind()) {
    case Method:
      if (entry.getMethodWitness().Witness) {
        entry.getMethodWitness().Witness->decrementRefCount();
      }
      break;
    case AssociatedType:
    case AssociatedTypeProtocol:
    case BaseProtocol:
    case Invalid:
      break;
    }
  }
}

void SILWitnessTable::convertToDefinition(
    ArrayRef<Entry> entries,
    ArrayRef<ConditionalConformance> conditionalConformances,
    IsSerialized_t isSerialized) {
  assert(isDeclaration() && "Definitions should never call this method.");
  IsDeclaration = false;
  assert(isSerialized != IsSerializable);
  Serialized = (isSerialized == IsSerialized);

  Entries = Mod.allocateCopy(entries);
  ConditionalConformances = Mod.allocateCopy(conditionalConformances);

  // Bump the reference count of witness functions referenced by this table.
  for (auto entry : getEntries()) {
    switch (entry.getKind()) {
    case Method:
      if (entry.getMethodWitness().Witness) {
        entry.getMethodWitness().Witness->incrementRefCount();
      }
      break;
    case AssociatedType:
    case AssociatedTypeProtocol:
    case BaseProtocol:
    case Invalid:
      break;
    }
  }
}

bool SILWitnessTable::conformanceIsSerialized(
    const RootProtocolConformance *conformance) {
  auto normalConformance = dyn_cast<NormalProtocolConformance>(conformance);
  if (normalConformance && normalConformance->isResilient())
    return false;

  // Serialize witness tables for conformances synthesized by
  // the ClangImporter.
  if (isa<ClangModuleUnit>(conformance->getDeclContext()->getModuleScopeContext()))
    return true;

  if (conformance->getProtocol()->getEffectiveAccess() < AccessLevel::Public)
    return false;

  auto *nominal = conformance->getType()->getAnyNominal();
  return nominal->getEffectiveAccess() >= AccessLevel::Public;
}

bool SILWitnessTable::enumerateWitnessTableConditionalConformances(
    const ProtocolConformance *conformance,
    llvm::function_ref<bool(unsigned, CanType, ProtocolDecl *)> fn) {
  unsigned conformanceIndex = 0;
  for (auto req : conformance->getConditionalRequirements()) {
    if (req.getKind() != RequirementKind::Conformance)
      continue;

    auto proto = req.getSecondType()->castTo<ProtocolType>()->getDecl();

    if (Lowering::TypeConverter::protocolRequiresWitnessTable(proto)) {
      if (fn(conformanceIndex, req.getFirstType()->getCanonicalType(), proto))
        return true;

      conformanceIndex++;
    }
  }
  return false;
}
<commit_msg>SIL: add a trace for the construction of witness tables<commit_after>//===--- SILWitnessTable.cpp - Defines the SILWitnessTable class ----------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file defines the SILWitnessTable class, which is used to map a protocol
// conformance for a type to its implementing SILFunctions. This information is
// (FIXME will be) used by IRGen to create witness tables for protocol dispatch.
// It can also be used by generic specialization and existential
// devirtualization passes to promote witness_method and protocol_method
// instructions to static function_refs.
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "sil-witness-table"
#include "swift/SIL/SILWitnessTable.h"
#include "swift/AST/ASTMangler.h"
#include "swift/AST/Module.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/ClangImporter/ClangModule.h"
#include "swift/SIL/SILModule.h"
#include "llvm/ADT/SmallString.h"

using namespace swift;

static std::string mangleConstant(RootProtocolConformance *C) {
  Mangle::ASTMangler Mangler;
  return Mangler.mangleWitnessTable(C);
}

DeclContext *SILWitnessTable::getDeclContext() const {
  return getConformance()->getDeclContext();
}

ProtocolDecl *SILWitnessTable::getProtocol() const {
  return getConformance()->getProtocol();
}

CanType SILWitnessTable::getConformingType() const {
  return getConformance()->getType()->getCanonicalType();
}

void SILWitnessTable::addWitnessTable() {
  // Make sure we have not seen this witness table yet.
  assert(Mod.WitnessTableMap.find(Conformance) ==
         Mod.WitnessTableMap.end() && "Attempting to create duplicate "
         "witness table.");
  Mod.WitnessTableMap[Conformance] = this;
  Mod.witnessTables.push_back(this);
}

SILWitnessTable *SILWitnessTable::create(
    SILModule &M, SILLinkage Linkage, IsSerialized_t Serialized,
    RootProtocolConformance *Conformance,
    ArrayRef<SILWitnessTable::Entry> entries,
    ArrayRef<ConditionalConformance> conditionalConformances) {
  assert(Conformance && "Cannot create a witness table for a null "
         "conformance.");

  // Create the mangled name of our witness table...
  Identifier Name = M.getASTContext().getIdentifier(mangleConstant(Conformance));

  LLVM_DEBUG(llvm::dbgs() << "SILWitnessTable Creating: " << Name.str() << '\n');

  // Allocate the witness table and initialize it.
  void *buf = M.allocate(sizeof(SILWitnessTable), alignof(SILWitnessTable));
  SILWitnessTable *wt = ::new (buf)
      SILWitnessTable(M, Linkage, Serialized, Name.str(), Conformance, entries,
                      conditionalConformances);

  wt->addWitnessTable();

  // Return the resulting witness table.
  return wt;
}

SILWitnessTable *
SILWitnessTable::create(SILModule &M, SILLinkage Linkage,
                        RootProtocolConformance *Conformance) {
  assert(Conformance && "Cannot create a witness table for a null "
         "conformance.");

  // Create the mangled name of our witness table...
  Identifier Name = M.getASTContext().getIdentifier(mangleConstant(Conformance));

  // Allocate the witness table and initialize it.
  void *buf = M.allocate(sizeof(SILWitnessTable), alignof(SILWitnessTable));
  SILWitnessTable *wt = ::new (buf) SILWitnessTable(M, Linkage, Name.str(),
                                                    Conformance);

  wt->addWitnessTable();

  // Return the resulting witness table.
  return wt;
}

SILWitnessTable::SILWitnessTable(
    SILModule &M, SILLinkage Linkage, IsSerialized_t Serialized, StringRef N,
    RootProtocolConformance *Conformance, ArrayRef<Entry> entries,
    ArrayRef<ConditionalConformance> conditionalConformances)
    : Mod(M), Name(N), Linkage(Linkage), Conformance(Conformance), Entries(),
      ConditionalConformances(), IsDeclaration(true), Serialized(false) {
  convertToDefinition(entries, conditionalConformances, Serialized);
}

SILWitnessTable::SILWitnessTable(SILModule &M, SILLinkage Linkage, StringRef N,
                                 RootProtocolConformance *Conformance)
    : Mod(M), Name(N), Linkage(Linkage), Conformance(Conformance), Entries(),
      ConditionalConformances(), IsDeclaration(true), Serialized(false) {}

SILWitnessTable::~SILWitnessTable() {
  if (isDeclaration())
    return;

  // Drop the reference count of witness functions referenced by this table.
  for (auto entry : getEntries()) {
    switch (entry.getKind()) {
    case Method:
      if (entry.getMethodWitness().Witness) {
        entry.getMethodWitness().Witness->decrementRefCount();
      }
      break;
    case AssociatedType:
    case AssociatedTypeProtocol:
    case BaseProtocol:
    case Invalid:
      break;
    }
  }
}

void SILWitnessTable::convertToDefinition(
    ArrayRef<Entry> entries,
    ArrayRef<ConditionalConformance> conditionalConformances,
    IsSerialized_t isSerialized) {
  assert(isDeclaration() && "Definitions should never call this method.");
  IsDeclaration = false;
  assert(isSerialized != IsSerializable);
  Serialized = (isSerialized == IsSerialized);

  Entries = Mod.allocateCopy(entries);
  ConditionalConformances = Mod.allocateCopy(conditionalConformances);

  // Bump the reference count of witness functions referenced by this table.
  for (auto entry : getEntries()) {
    switch (entry.getKind()) {
    case Method:
      if (entry.getMethodWitness().Witness) {
        entry.getMethodWitness().Witness->incrementRefCount();
      }
      break;
    case AssociatedType:
    case AssociatedTypeProtocol:
    case BaseProtocol:
    case Invalid:
      break;
    }
  }
}

bool SILWitnessTable::conformanceIsSerialized(
    const RootProtocolConformance *conformance) {
  auto normalConformance = dyn_cast<NormalProtocolConformance>(conformance);
  if (normalConformance && normalConformance->isResilient())
    return false;

  // Serialize witness tables for conformances synthesized by
  // the ClangImporter.
  if (isa<ClangModuleUnit>(conformance->getDeclContext()->getModuleScopeContext()))
    return true;

  if (conformance->getProtocol()->getEffectiveAccess() < AccessLevel::Public)
    return false;

  auto *nominal = conformance->getType()->getAnyNominal();
  return nominal->getEffectiveAccess() >= AccessLevel::Public;
}

bool SILWitnessTable::enumerateWitnessTableConditionalConformances(
    const ProtocolConformance *conformance,
    llvm::function_ref<bool(unsigned, CanType, ProtocolDecl *)> fn) {
  unsigned conformanceIndex = 0;
  for (auto req : conformance->getConditionalRequirements()) {
    if (req.getKind() != RequirementKind::Conformance)
      continue;

    auto proto = req.getSecondType()->castTo<ProtocolType>()->getDecl();

    if (Lowering::TypeConverter::protocolRequiresWitnessTable(proto)) {
      if (fn(conformanceIndex, req.getFirstType()->getCanonicalType(), proto))
        return true;

      conformanceIndex++;
    }
  }
  return false;
}
<|endoftext|>


<commit_before>//===- SystemUtils.h - Utilities to do low-level system stuff --*- C++ -*--===//
//
// This file contains functions used to do a variety of low-level, often
// system-specific, tasks.
//
//===----------------------------------------------------------------------===//

#include "Support/SystemUtils.h"
#include <algorithm>
#include <fstream>
#include <iostream>
#include <cstdlib>
#include "Config/sys/types.h"
#include "Config/sys/stat.h"
#include "Config/fcntl.h"
#include "Config/sys/wait.h"
#include "Config/unistd.h"
#include "Config/errno.h"

/// isExecutableFile - This function returns true if the filename specified
/// exists and is executable.
///
bool isExecutableFile(const std::string &ExeFileName) {
  struct stat Buf;
  if (stat(ExeFileName.c_str(), &Buf))
    return false;  // Must not be executable!

  if (!(Buf.st_mode & S_IFREG))
    return false;                    // Not a regular file?

  if (Buf.st_uid == getuid())        // Owner of file?
    return Buf.st_mode & S_IXUSR;
  else if (Buf.st_gid == getgid())   // In group of file?
    return Buf.st_mode & S_IXGRP;
  else                               // Unrelated to file?
    return Buf.st_mode & S_IXOTH;
}

/// FindExecutable - Find a named executable, giving the argv[0] of program
/// being executed. This allows us to find another LLVM tool if it is built
/// into the same directory, but that directory is neither the current
/// directory, nor in the PATH.  If the executable cannot be found, return an
/// empty string.
///
std::string FindExecutable(const std::string &ExeName,
                           const std::string &ProgramPath) {
  // First check the directory that bugpoint is in.  We can do this if
  // BugPointPath contains at least one / character, indicating that it is a
  // relative path to bugpoint itself.
  //
  std::string Result = ProgramPath;
  while (!Result.empty() && Result[Result.size()-1] != '/')
    Result.erase(Result.size()-1, 1);

  if (!Result.empty()) {
    Result += ExeName;
    if (isExecutableFile(Result)) return Result; // Found it?
  }

  // Okay, if the path to the program didn't tell us anything, try using the
  // PATH environment variable.
  const char *PathStr = getenv("PATH");
  if (PathStr == 0) return "";

  // Now we have a colon separated list of directories to search... try them...
  unsigned PathLen = strlen(PathStr);
  while (PathLen) {
    // Find the first colon...
    const char *Colon = std::find(PathStr, PathStr+PathLen, ':');

    // Check to see if this first directory contains the executable...
    std::string FilePath = std::string(PathStr, Colon) + '/' + ExeName;
    if (isExecutableFile(FilePath))
      return FilePath;                    // Found the executable!

    // Nope it wasn't in this directory, check the next range!
    PathLen -= Colon-PathStr;
    PathStr = Colon;
    while (*PathStr == ':') {   // Advance past colons
      PathStr++;
      PathLen--;
    }
  }

  // If we fell out, we ran out of directories in PATH to search, return failure
  return "";
}

static void RedirectFD(const std::string &File, int FD) {
  if (File.empty()) return;  // Noop

  // Open the file
  int InFD = open(File.c_str(), FD == 0 ? O_RDONLY : O_WRONLY|O_CREAT, 0666);
  if (InFD == -1) {
    std::cerr << "Error opening file '" << File << "' for "
              << (FD == 0 ? "input" : "output") << "!\n";
    exit(1);
  }

  dup2(InFD, FD);   // Install it as the requested FD
  close(InFD);      // Close the original FD
}

/// RunProgramWithTimeout - This function executes the specified program, with
/// the specified null-terminated argument array, with the stdin/out/err fd's
/// redirected, with a timeout specified on the command line.  This terminates
/// the calling program if there is an error executing the specified program.
/// It returns the return value of the program, or -1 if a timeout is detected.
///
int RunProgramWithTimeout(const std::string &ProgramPath, const char **Args,
                          const std::string &StdInFile,
                          const std::string &StdOutFile,
                          const std::string &StdErrFile) {

  // FIXME: install sigalarm handler here for timeout...

  int Child = fork();
  switch (Child) {
  case -1:
    std::cerr << "ERROR forking!\n";
    exit(1);
  case 0:               // Child
    RedirectFD(StdInFile, 0);      // Redirect file descriptors...
    RedirectFD(StdOutFile, 1);
    RedirectFD(StdErrFile, 2);

    execv(ProgramPath.c_str(), (char *const *)Args);
    std::cerr << "Error executing program '" << ProgramPath;
    for (; *Args; ++Args)
      std::cerr << " " << *Args;
    exit(1);

  default: break;
  }

  // Make sure all output has been written while waiting
  std::cout << std::flush;

  int Status;
  if (wait(&Status) != Child) {
    if (errno == EINTR) {
      static bool FirstTimeout = true;
      if (FirstTimeout) {
        std::cout <<
 "*** Program execution timed out!  This mechanism is designed to handle\n"
 "    programs stuck in infinite loops gracefully.  The -timeout option\n"
 "    can be used to change the timeout threshold or disable it completely\n"
 "    (with -timeout=0).  This message is only displayed once.\n";
        FirstTimeout = false;
      }
      return -1;   // Timeout detected
    }

    std::cerr << "Error waiting for child process!\n";
    exit(1);
  }
  return Status;
}

//
// Function: ExecWait ()
//
// Description:
//  This function executes a program with the specified arguments and
//  environment.  It then waits for the progarm to termiante and then returns
//  to the caller.
//
// Inputs:
//  argv - The arguments to the program as an array of C strings.  The first
//         argument should be the name of the program to execute, and the
//         last argument should be a pointer to NULL.
//
//  envp - The environment passes to the program as an array of C strings in
//         the form of "name=value" pairs.  The last element should be a
//         pointer to NULL.
//
// Outputs:
//  None.
//
// Return value:
//  0 - No errors.
//  1 - The program could not be executed.
//  1 - The program returned a non-zero exit status.
//  1 - The program terminated abnormally.
//
// Notes:
//  The program will inherit the stdin, stdout, and stderr file descriptors
//  as well as other various configuration settings (umask).
//
//  This function should not print anything to stdout/stderr on its own.  It is
//  a generic library function.  The caller or executed program should report
//  errors in the way it sees fit.
//
//  This function does not use $PATH to find programs.
//
int
ExecWait (const char * const old_argv[], const char * const old_envp[])
{
  // Child process ID
  register int child;

  // Status from child process when it exits
  int status;

  //
  // Create local versions of the parameters that can be passed into execve()
  // without creating const problems.
  //
  char ** const argv = (char ** const) old_argv;
  char ** const envp = (char ** const) old_envp;

  //
  // Create a child process.
  //
  switch (child=fork())
  {
    //
    // An error occured:  Return to the caller.
    //
    case -1:
      return 1;
      break;

    //
    // Child process: Execute the program.
    //
    case 0:
      execve (argv[0], argv, envp);

      //
      // If the execve() failed, we should exit and let the parent pick up
      // our non-zero exit status.
      //
      exit (1);
      break;

    //
    // Parent process: Break out of the switch to do our processing.
    //
    default:
      break;
  }

  //
  // Parent process: Wait for the child process to termiante.
  //
  if ((wait (&status)) == -1)
  {
    return 1;
  }

  //
  // If the program exited normally with a zero exit status, return success!
  //
  if (WIFEXITED (status) && (WEXITSTATUS(status) == 0))
  {
    return 0;
  }

  //
  // Otherwise, return failure.
  //
  return 1;
}

<commit_msg>Fix up error message.<commit_after>//===- SystemUtils.h - Utilities to do low-level system stuff --*- C++ -*--===//
//
// This file contains functions used to do a variety of low-level, often
// system-specific, tasks.
//
//===----------------------------------------------------------------------===//

#include "Support/SystemUtils.h"
#include <algorithm>
#include <fstream>
#include <iostream>
#include <cstdlib>
#include "Config/sys/types.h"
#include "Config/sys/stat.h"
#include "Config/fcntl.h"
#include "Config/sys/wait.h"
#include "Config/unistd.h"
#include "Config/errno.h"

/// isExecutableFile - This function returns true if the filename specified
/// exists and is executable.
///
bool isExecutableFile(const std::string &ExeFileName) {
  struct stat Buf;
  if (stat(ExeFileName.c_str(), &Buf))
    return false;  // Must not be executable!

  if (!(Buf.st_mode & S_IFREG))
    return false;                    // Not a regular file?

  if (Buf.st_uid == getuid())        // Owner of file?
    return Buf.st_mode & S_IXUSR;
  else if (Buf.st_gid == getgid())   // In group of file?
    return Buf.st_mode & S_IXGRP;
  else                               // Unrelated to file?
    return Buf.st_mode & S_IXOTH;
}

/// FindExecutable - Find a named executable, giving the argv[0] of program
/// being executed. This allows us to find another LLVM tool if it is built
/// into the same directory, but that directory is neither the current
/// directory, nor in the PATH.  If the executable cannot be found, return an
/// empty string.
///
std::string FindExecutable(const std::string &ExeName,
                           const std::string &ProgramPath) {
  // First check the directory that bugpoint is in.  We can do this if
  // BugPointPath contains at least one / character, indicating that it is a
  // relative path to bugpoint itself.
  //
  std::string Result = ProgramPath;
  while (!Result.empty() && Result[Result.size()-1] != '/')
    Result.erase(Result.size()-1, 1);

  if (!Result.empty()) {
    Result += ExeName;
    if (isExecutableFile(Result)) return Result; // Found it?
  }

  // Okay, if the path to the program didn't tell us anything, try using the
  // PATH environment variable.
  const char *PathStr = getenv("PATH");
  if (PathStr == 0) return "";

  // Now we have a colon separated list of directories to search... try them...
  unsigned PathLen = strlen(PathStr);
  while (PathLen) {
    // Find the first colon...
    const char *Colon = std::find(PathStr, PathStr+PathLen, ':');

    // Check to see if this first directory contains the executable...
    std::string FilePath = std::string(PathStr, Colon) + '/' + ExeName;
    if (isExecutableFile(FilePath))
      return FilePath;                    // Found the executable!

    // Nope it wasn't in this directory, check the next range!
    PathLen -= Colon-PathStr;
    PathStr = Colon;
    while (*PathStr == ':') {   // Advance past colons
      PathStr++;
      PathLen--;
    }
  }

  // If we fell out, we ran out of directories in PATH to search, return failure
  return "";
}

static void RedirectFD(const std::string &File, int FD) {
  if (File.empty()) return;  // Noop

  // Open the file
  int InFD = open(File.c_str(), FD == 0 ? O_RDONLY : O_WRONLY|O_CREAT, 0666);
  if (InFD == -1) {
    std::cerr << "Error opening file '" << File << "' for "
              << (FD == 0 ? "input" : "output") << "!\n";
    exit(1);
  }

  dup2(InFD, FD);   // Install it as the requested FD
  close(InFD);      // Close the original FD
}

/// RunProgramWithTimeout - This function executes the specified program, with
/// the specified null-terminated argument array, with the stdin/out/err fd's
/// redirected, with a timeout specified on the command line.  This terminates
/// the calling program if there is an error executing the specified program.
/// It returns the return value of the program, or -1 if a timeout is detected.
///
int RunProgramWithTimeout(const std::string &ProgramPath, const char **Args,
                          const std::string &StdInFile,
                          const std::string &StdOutFile,
                          const std::string &StdErrFile) {

  // FIXME: install sigalarm handler here for timeout...

  int Child = fork();
  switch (Child) {
  case -1:
    std::cerr << "ERROR forking!\n";
    exit(1);
  case 0:               // Child
    RedirectFD(StdInFile, 0);      // Redirect file descriptors...
    RedirectFD(StdOutFile, 1);
    RedirectFD(StdErrFile, 2);

    execv(ProgramPath.c_str(), (char *const *)Args);
    std::cerr << "Error executing program: '" << ProgramPath;
    for (; *Args; ++Args)
      std::cerr << " " << *Args;
    std::cerr << "'\n";
    exit(1);

  default: break;
  }

  // Make sure all output has been written while waiting
  std::cout << std::flush;

  int Status;
  if (wait(&Status) != Child) {
    if (errno == EINTR) {
      static bool FirstTimeout = true;
      if (FirstTimeout) {
        std::cout <<
 "*** Program execution timed out!  This mechanism is designed to handle\n"
 "    programs stuck in infinite loops gracefully.  The -timeout option\n"
 "    can be used to change the timeout threshold or disable it completely\n"
 "    (with -timeout=0).  This message is only displayed once.\n";
        FirstTimeout = false;
      }
      return -1;   // Timeout detected
    }

    std::cerr << "Error waiting for child process!\n";
    exit(1);
  }
  return Status;
}

//
// Function: ExecWait ()
//
// Description:
//  This function executes a program with the specified arguments and
//  environment.  It then waits for the progarm to termiante and then returns
//  to the caller.
//
// Inputs:
//  argv - The arguments to the program as an array of C strings.  The first
//         argument should be the name of the program to execute, and the
//         last argument should be a pointer to NULL.
//
//  envp - The environment passes to the program as an array of C strings in
//         the form of "name=value" pairs.  The last element should be a
//         pointer to NULL.
//
// Outputs:
//  None.
//
// Return value:
//  0 - No errors.
//  1 - The program could not be executed.
//  1 - The program returned a non-zero exit status.
//  1 - The program terminated abnormally.
//
// Notes:
//  The program will inherit the stdin, stdout, and stderr file descriptors
//  as well as other various configuration settings (umask).
//
//  This function should not print anything to stdout/stderr on its own.  It is
//  a generic library function.  The caller or executed program should report
//  errors in the way it sees fit.
//
//  This function does not use $PATH to find programs.
//
int
ExecWait (const char * const old_argv[], const char * const old_envp[])
{
  // Child process ID
  register int child;

  // Status from child process when it exits
  int status;

  //
  // Create local versions of the parameters that can be passed into execve()
  // without creating const problems.
  //
  char ** const argv = (char ** const) old_argv;
  char ** const envp = (char ** const) old_envp;

  //
  // Create a child process.
  //
  switch (child=fork())
  {
    //
    // An error occured:  Return to the caller.
    //
    case -1:
      return 1;
      break;

    //
    // Child process: Execute the program.
    //
    case 0:
      execve (argv[0], argv, envp);

      //
      // If the execve() failed, we should exit and let the parent pick up
      // our non-zero exit status.
      //
      exit (1);
      break;

    //
    // Parent process: Break out of the switch to do our processing.
    //
    default:
      break;
  }

  //
  // Parent process: Wait for the child process to termiante.
  //
  if ((wait (&status)) == -1)
  {
    return 1;
  }

  //
  // If the program exited normally with a zero exit status, return success!
  //
  if (WIFEXITED (status) && (WEXITSTATUS(status) == 0))
  {
    return 0;
  }

  //
  // Otherwise, return failure.
  //
  return 1;
}

<|endoftext|>