<commit_before>// This file is part of the AliceVision project.
// Copyright (c) 2017 AliceVision contributors.
// This Source Code Form is subject to the terms of the Mozilla Public License,
// v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

#include <aliceVision/system/cmdline.hpp>
#include <aliceVision/system/Logger.hpp>
#include <aliceVision/system/Timer.hpp>
#include <aliceVision/mesh/MeshEnergyOpt.hpp>
#include <aliceVision/mesh/Texturing.hpp>
#include <aliceVision/mvsUtils/common.hpp>

#include <boost/program_options.hpp>
#include <boost/filesystem.hpp>

// These constants define the current software version.
// They must be updated when the command line is changed.
#define ALICEVISION_SOFTWARE_VERSION_MAJOR 2
#define ALICEVISION_SOFTWARE_VERSION_MINOR 0

using namespace aliceVision;

namespace bfs = boost::filesystem;
namespace po = boost::program_options;

int main(int argc, char* argv[])
{
    system::Timer timer;

    std::string verboseLevel = system::EVerboseLevel_enumToString(system::Logger::getDefaultVerboseLevel());
    std::string inputMeshPath;
    std::string outputMeshPath;

    bool keepLargestMeshOnly = true;
    double removeLargeTrianglesFactor = 60.0;

    int smoothNIter = 10;
    float lambda = 1.0f;

    po::options_description allParams("AliceVision meshFiltering");

    po::options_description requiredParams("Required parameters");
    requiredParams.add_options()
        ("input,i", po::value<std::string>(&inputMeshPath)->required(),
            "Input Mesh (OBJ file format).")
        ("output,o", po::value<std::string>(&outputMeshPath)->required(),
            "Output mesh (OBJ file format).");

    po::options_description optionalParams("Optional parameters");
    optionalParams.add_options()
        ("keepLargestMeshOnly", po::value<bool>(&keepLargestMeshOnly)->default_value(keepLargestMeshOnly),
            "Keep only the largest connected triangles group.")
        ("removeLargeTrianglesFactor", po::value<double>(&removeLargeTrianglesFactor)->default_value(removeLargeTrianglesFactor),
            "Remove all large triangles. We consider a triangle as large if one edge is bigger than N times the average edge length. Put zero to disable it.")
        ("iterations", po::value<int>(&smoothNIter)->default_value(smoothNIter),
            "Number of smoothing iterations.")
        ("lambda", po::value<float>(&lambda)->default_value(lambda),
            "Smoothing size.");

    po::options_description logParams("Log parameters");
    logParams.add_options()
      ("verboseLevel,v", po::value<std::string>(&verboseLevel)->default_value(verboseLevel),
        "verbosity level (fatal, error, warning, info, debug, trace).");

    allParams.add(requiredParams).add(optionalParams).add(logParams);

    po::variables_map vm;

    try
    {
      po::store(po::parse_command_line(argc, argv, allParams), vm);

      if(vm.count("help") || (argc == 1))
      {
        ALICEVISION_COUT(allParams);
        return EXIT_SUCCESS;
      }

      po::notify(vm);
    }
    catch(boost::program_options::required_option& e)
    {
      ALICEVISION_CERR("ERROR: " << e.what() << std::endl);
      ALICEVISION_COUT("Usage:\n\n" << allParams);
      return EXIT_FAILURE;
    }
    catch(boost::program_options::error& e)
    {
      ALICEVISION_CERR("ERROR: " << e.what() << std::endl);
      ALICEVISION_COUT("Usage:\n\n" << allParams);
      return EXIT_FAILURE;
    }

    ALICEVISION_COUT("Program called with the following parameters:");
    ALICEVISION_COUT(vm);

    // set verbose level
    system::Logger::get()->setLogLevel(verboseLevel);

    bfs::path outDirectory = bfs::path(outputMeshPath).parent_path();
    if(!bfs::is_directory(outDirectory))
        bfs::create_directory(outDirectory);

    mesh::Texturing texturing;
    texturing.loadFromOBJ(inputMeshPath);
    mesh::Mesh* mesh = texturing.me;

    if(!mesh)
    {
        ALICEVISION_LOG_ERROR("Unable to read input mesh from the file: " << inputMeshPath);
        return EXIT_FAILURE;
    }

    if(mesh->pts->empty() || mesh->tris->empty())
    {
        ALICEVISION_LOG_ERROR("Error: empty mesh from the file " << inputMeshPath);
        ALICEVISION_LOG_ERROR("Input mesh: " << mesh->pts->size() << " vertices and " << mesh->tris->size() << " facets.");
        return EXIT_FAILURE;
    }

    ALICEVISION_LOG_INFO("Mesh file: \"" << inputMeshPath << "\" loaded.");
    ALICEVISION_LOG_INFO("Input mesh: " << mesh->pts->size() << " vertices and " << mesh->tris->size() << " facets.");

    if(removeLargeTrianglesFactor != 0.0)
    {
        mesh->filterLargeEdgeTriangles(removeLargeTrianglesFactor);
        ALICEVISION_LOG_INFO("Mesh after large triangles removal: " << mesh->pts->size() << " vertices and " << mesh->tris->size() << " facets.");
    }

    mesh::MeshEnergyOpt meOpt(nullptr);
    {
        ALICEVISION_LOG_INFO("Start mesh filtering.");
        meOpt.addMesh(mesh);
        meOpt.init();
        meOpt.cleanMesh(10);

        StaticVectorBool* ptsCanMove = nullptr;
        meOpt.optimizeSmooth(lambda, smoothNIter, ptsCanMove);

        ALICEVISION_LOG_INFO("Mesh filtering done: " << meOpt.pts->size() << " vertices and " << meOpt.tris->size() << " facets.");
    }

    if(keepLargestMeshOnly)
    {
        StaticVector<int>* trisIdsToStay = meOpt.getLargestConnectedComponentTrisIds();
        meOpt.letJustTringlesIdsInMesh(trisIdsToStay);
        delete trisIdsToStay;
        ALICEVISION_LOG_INFO("Mesh after keepLargestMeshOnly: " << meOpt.pts->size() << " vertices and " << meOpt.tris->size() << " facets.");
    }

    mesh::Mesh outMesh;
    outMesh.addMesh(&meOpt);

    ALICEVISION_COUT("Output mesh: " << mesh->pts->size() << " vertices and " << mesh->tris->size() << " facets.");

    if(outMesh.pts->empty() || outMesh.tris->empty())
    {
        ALICEVISION_CERR("Failed: the output mesh is empty.");
        ALICEVISION_LOG_INFO("Output mesh: " << outMesh.pts->size() << " vertices and " << outMesh.tris->size() << " facets.");
        return EXIT_FAILURE;
    }

    ALICEVISION_LOG_INFO("Save mesh.");

    // Save output mesh
    outMesh.saveToObj(outputMeshPath);

    ALICEVISION_LOG_INFO("Mesh file: \"" << outputMeshPath << "\" saved.");

    ALICEVISION_LOG_INFO("Task done in (s): " + std::to_string(timer.elapsed()));
    return EXIT_SUCCESS;
}
<commit_msg>[software] `MeshFiltering` option `keepLargestMeshOnly` to false<commit_after>// This file is part of the AliceVision project.
// Copyright (c) 2017 AliceVision contributors.
// This Source Code Form is subject to the terms of the Mozilla Public License,
// v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

#include <aliceVision/system/cmdline.hpp>
#include <aliceVision/system/Logger.hpp>
#include <aliceVision/system/Timer.hpp>
#include <aliceVision/mesh/MeshEnergyOpt.hpp>
#include <aliceVision/mesh/Texturing.hpp>
#include <aliceVision/mvsUtils/common.hpp>

#include <boost/program_options.hpp>
#include <boost/filesystem.hpp>

// These constants define the current software version.
// They must be updated when the command line is changed.
#define ALICEVISION_SOFTWARE_VERSION_MAJOR 2
#define ALICEVISION_SOFTWARE_VERSION_MINOR 0

using namespace aliceVision;

namespace bfs = boost::filesystem;
namespace po = boost::program_options;

int main(int argc, char* argv[])
{
    system::Timer timer;

    std::string verboseLevel = system::EVerboseLevel_enumToString(system::Logger::getDefaultVerboseLevel());
    std::string inputMeshPath;
    std::string outputMeshPath;

    bool keepLargestMeshOnly = false;
    double removeLargeTrianglesFactor = 60.0;

    int smoothNIter = 10;
    float lambda = 1.0f;

    po::options_description allParams("AliceVision meshFiltering");

    po::options_description requiredParams("Required parameters");
    requiredParams.add_options()
        ("input,i", po::value<std::string>(&inputMeshPath)->required(),
            "Input Mesh (OBJ file format).")
        ("output,o", po::value<std::string>(&outputMeshPath)->required(),
            "Output mesh (OBJ file format).");

    po::options_description optionalParams("Optional parameters");
    optionalParams.add_options()
        ("keepLargestMeshOnly", po::value<bool>(&keepLargestMeshOnly)->default_value(keepLargestMeshOnly),
            "Keep only the largest connected triangles group.")
        ("removeLargeTrianglesFactor", po::value<double>(&removeLargeTrianglesFactor)->default_value(removeLargeTrianglesFactor),
            "Remove all large triangles. We consider a triangle as large if one edge is bigger than N times the average edge length. Put zero to disable it.")
        ("iterations", po::value<int>(&smoothNIter)->default_value(smoothNIter),
            "Number of smoothing iterations.")
        ("lambda", po::value<float>(&lambda)->default_value(lambda),
            "Smoothing size.");

    po::options_description logParams("Log parameters");
    logParams.add_options()
      ("verboseLevel,v", po::value<std::string>(&verboseLevel)->default_value(verboseLevel),
        "verbosity level (fatal, error, warning, info, debug, trace).");

    allParams.add(requiredParams).add(optionalParams).add(logParams);

    po::variables_map vm;

    try
    {
      po::store(po::parse_command_line(argc, argv, allParams), vm);

      if(vm.count("help") || (argc == 1))
      {
        ALICEVISION_COUT(allParams);
        return EXIT_SUCCESS;
      }

      po::notify(vm);
    }
    catch(boost::program_options::required_option& e)
    {
      ALICEVISION_CERR("ERROR: " << e.what() << std::endl);
      ALICEVISION_COUT("Usage:\n\n" << allParams);
      return EXIT_FAILURE;
    }
    catch(boost::program_options::error& e)
    {
      ALICEVISION_CERR("ERROR: " << e.what() << std::endl);
      ALICEVISION_COUT("Usage:\n\n" << allParams);
      return EXIT_FAILURE;
    }

    ALICEVISION_COUT("Program called with the following parameters:");
    ALICEVISION_COUT(vm);

    // set verbose level
    system::Logger::get()->setLogLevel(verboseLevel);

    bfs::path outDirectory = bfs::path(outputMeshPath).parent_path();
    if(!bfs::is_directory(outDirectory))
        bfs::create_directory(outDirectory);

    mesh::Texturing texturing;
    texturing.loadFromOBJ(inputMeshPath);
    mesh::Mesh* mesh = texturing.me;

    if(!mesh)
    {
        ALICEVISION_LOG_ERROR("Unable to read input mesh from the file: " << inputMeshPath);
        return EXIT_FAILURE;
    }

    if(mesh->pts->empty() || mesh->tris->empty())
    {
        ALICEVISION_LOG_ERROR("Error: empty mesh from the file " << inputMeshPath);
        ALICEVISION_LOG_ERROR("Input mesh: " << mesh->pts->size() << " vertices and " << mesh->tris->size() << " facets.");
        return EXIT_FAILURE;
    }

    ALICEVISION_LOG_INFO("Mesh file: \"" << inputMeshPath << "\" loaded.");
    ALICEVISION_LOG_INFO("Input mesh: " << mesh->pts->size() << " vertices and " << mesh->tris->size() << " facets.");

    if(removeLargeTrianglesFactor != 0.0)
    {
        mesh->filterLargeEdgeTriangles(removeLargeTrianglesFactor);
        ALICEVISION_LOG_INFO("Mesh after large triangles removal: " << mesh->pts->size() << " vertices and " << mesh->tris->size() << " facets.");
    }

    mesh::MeshEnergyOpt meOpt(nullptr);
    {
        ALICEVISION_LOG_INFO("Start mesh filtering.");
        meOpt.addMesh(mesh);
        meOpt.init();
        meOpt.cleanMesh(10);

        StaticVectorBool* ptsCanMove = nullptr;
        meOpt.optimizeSmooth(lambda, smoothNIter, ptsCanMove);

        ALICEVISION_LOG_INFO("Mesh filtering done: " << meOpt.pts->size() << " vertices and " << meOpt.tris->size() << " facets.");
    }

    if(keepLargestMeshOnly)
    {
        StaticVector<int>* trisIdsToStay = meOpt.getLargestConnectedComponentTrisIds();
        meOpt.letJustTringlesIdsInMesh(trisIdsToStay);
        delete trisIdsToStay;
        ALICEVISION_LOG_INFO("Mesh after keepLargestMeshOnly: " << meOpt.pts->size() << " vertices and " << meOpt.tris->size() << " facets.");
    }

    mesh::Mesh outMesh;
    outMesh.addMesh(&meOpt);

    ALICEVISION_COUT("Output mesh: " << mesh->pts->size() << " vertices and " << mesh->tris->size() << " facets.");

    if(outMesh.pts->empty() || outMesh.tris->empty())
    {
        ALICEVISION_CERR("Failed: the output mesh is empty.");
        ALICEVISION_LOG_INFO("Output mesh: " << outMesh.pts->size() << " vertices and " << outMesh.tris->size() << " facets.");
        return EXIT_FAILURE;
    }

    ALICEVISION_LOG_INFO("Save mesh.");

    // Save output mesh
    outMesh.saveToObj(outputMeshPath);

    ALICEVISION_LOG_INFO("Mesh file: \"" << outputMeshPath << "\" saved.");

    ALICEVISION_LOG_INFO("Task done in (s): " + std::to_string(timer.elapsed()));
    return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2016, Nils Asmussen
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * The views and conclusions contained in the software and documentation are
 * those of the authors and should not be interpreted as representing official
 * policies, either expressed or implied, of the FreeBSD Project.
 */

#include "cpu/dtu-accel-aladdin/accelerator.hh"
#include "debug/DtuAccelAladdin.hh"
#include "debug/DtuAccelAladdinState.hh"
#include "mem/dtu/dtu.hh"
#include "mem/dtu/regfile.hh"
#include "sim/dtu_memory.hh"

#include <iomanip>

static const char *stateNames[] =
{
    "IDLE",

    "FETCH_MSG",
    "READ_MSG_ADDR",
    "READ_MSG",
    "COMPUTE",
    "STORE_REPLY",
    "SEND_REPLY",
    "REPLY_WAIT",
    "REPLY_ERROR",

    "CTXSW",

    "SYSCALL",
};

DtuAccelAladdin::DtuAccelAladdin(const DtuAccelAladdinParams *p)
  : DtuAccel(p),
    irqPending(false),
    ctxSwPending(false),
    memPending(false),
    state(State::IDLE),
    lastState(State::CTXSW),    // something different
    ctx(),
    accelId(p->accel_id),
    sysc(this),
    yield(this, &sysc),
    ctxsw(this),
    ctxSwPerformed()
{
    static_assert((sizeof(stateNames) / sizeof(stateNames[0]) ==
                  static_cast<size_t>(State::SYSCALL) + 1), "Missmatch");

    yield.start();
}

std::string DtuAccelAladdin::getStateName() const
{
    std::ostringstream os;
    os << stateNames[static_cast<size_t>(state)];
    if (state == State::IDLE)
        os << ":" << yield.stateName();
    else if (state == State::SYSCALL)
        os << ":" << sysc.stateName();
    else if (state == State::CTXSW)
        os << ":" << ctxsw.stateName();
    return os.str();
}

void
DtuAccelAladdin::completeRequest(PacketPtr pkt)
{
    Request* req = pkt->req;

    if (state != lastState ||
        (state == State::CTXSW && ctxsw.hasStateChanged()) ||
        (state == State::SYSCALL && sysc.hasStateChanged()) ||
        (state == State::IDLE && yield.hasStateChanged()))
    {
        DPRINTF(DtuAccelAladdinState, "[%s] Got response from memory\n",
            getStateName().c_str());
        lastState = state;
    }

    const uint8_t *pkt_data = pkt->getConstPtr<uint8_t>();

    Cycles delay(1);
    if (pkt->isError())
    {
        warn("%s access failed at %#x\n",
             pkt->isWrite() ? "Write" : "Read", req->getPaddr());
    }
    else
    {
        switch(state)
        {
            case State::IDLE:
            {
                if (yield.handleMemResp(pkt))
                {
                    if (irqPending)
                        irqPending = false;
                    state = State::CTXSW;
                }
                break;
            }

            case State::CTXSW:
            {
                if(ctxsw.handleMemResp(pkt))
                    state = State::FETCH_MSG;
                break;
            }

            case State::FETCH_MSG:
            {
                state = State::READ_MSG_ADDR;
                break;
            }
            case State::READ_MSG_ADDR:
            {
                const RegFile::reg_t *regs = pkt->getConstPtr<RegFile::reg_t>();
                if(regs[0])
                {
                    ctx.msgAddr = regs[0];
                    ctx.msgOff = 0;
                    DPRINTF(DtuAccelAladdin, "Received message @ %p\n", ctx.msgAddr);
                    state = State::READ_MSG;
                }
                else
                {
                    yield.start();
                    state = State::IDLE;
                }
                break;
            }
            case State::READ_MSG:
            {
                size_t maxOff = sizeof(ctx.msg) + sizeof(MessageHeader);
                if (ctx.msgOff < maxOff)
                {
                    char *dst = (char*)&ctx.msg;
                    size_t amount = std::min<size_t>(maxOff - ctx.msgOff, pkt->getSize());
                    if (ctx.msgOff == 0)
                    {
                        pkt_data += sizeof(MessageHeader);
                        amount -= sizeof(MessageHeader);
                    }
                    else
                        dst += ctx.msgOff - sizeof(MessageHeader);
                    memcpy(dst, pkt_data, amount);
                }

                ctx.msgOff += pkt->getSize();
                if (ctx.msgOff == MSG_SIZE)
                {
                    DPRINTF(DtuAccelAladdin,
                        "  invokeMsg(iters=%llu, arrays=%llu: [\n",
                        ctx.msg.iterations, ctx.msg.array_count);
                    for(uint64_t i = 0; i < ctx.msg.array_count; ++i)
                    {
                        DPRINTFR(DtuAccelAladdin, "    addr=%#llx, size=%#llx\n",
                            ctx.msg.arrays[i].addr, ctx.msg.arrays[i].size);
                    }
                    DPRINTFR(DtuAccelAladdin, "  ])\n");

                    ctx.iteration = 0;
                    ctx.interrupted = false;
                    state = State::COMPUTE;
                }
                break;
            }
            case State::COMPUTE:
            {
                assert(false);
            }
            case State::STORE_REPLY:
            {
                state = State::SEND_REPLY;
                break;
            }
            case State::SEND_REPLY:
            {
                state = State::REPLY_WAIT;
                break;
            }
            case State::REPLY_WAIT:
            {
                Dtu::Command::Bits cmd =
                    *reinterpret_cast<const RegFile::reg_t*>(pkt_data);
                if (cmd.opcode == 0)
                {
                    if (cmd.error == 0)
                        state = State::CTXSW;
                    else
                        state = State::REPLY_ERROR;
                }
                break;
            }
            case State::REPLY_ERROR:
            {
                sysc.start(sizeof(reply));
                syscNext = State::CTXSW;
                state = State::SYSCALL;
                break;
            }

            case State::SYSCALL:
            {
                if(sysc.handleMemResp(pkt))
                    state = syscNext;
                break;
            }
        }
    }

    memPending = false;
    freePacket(pkt);

    // kick things into action again
    schedule(tickEvent, clockEdge(delay));
}

void
DtuAccelAladdin::interrupt()
{
    irqPending = true;

    if (ctxsw.isWaiting())
    {
        ctxsw.restart();
        if (!memPending && !tickEvent.scheduled())
            schedule(tickEvent, clockEdge(Cycles(1)));
    }
    else if(state == State::COMPUTE)
    {
        if (!memPending && !tickEvent.scheduled())
            schedule(tickEvent, clockEdge(Cycles(1)));
    }
}

void
DtuAccelAladdin::wakeup()
{
    sysc.retryFetch();

    if (!memPending && !tickEvent.scheduled())
        schedule(tickEvent, clockEdge(Cycles(1)));
}

void
DtuAccelAladdin::reset()
{
    irqPending = false;

    yield.start(false);
    state = State::IDLE;
    memset(&ctx, 0, sizeof(ctx));
}

void
DtuAccelAladdin::signalFinished(size_t off)
{
    ctx.trace_off = off;
    ctx.iteration += 1;

    assert(state == State::COMPUTE);
    if (ctx.iteration == ctx.msg.iterations)
        state = State::STORE_REPLY;

    if (!memPending && !tickEvent.scheduled())
        schedule(tickEvent, clockEdge(Cycles(1)));
}

void
DtuAccelAladdin::tick()
{
    PacketPtr pkt = nullptr;

    // after a context switch, continue at then position we left off
    if (ctxSwPerformed)
    {
        DPRINTF(DtuAccelAladdin,
            "Resuming %swork at %llu/%llu, off=%llu\n",
            ctx.interrupted ? "interrupted " : "",
            ctx.iteration, ctx.msg.iterations, ctx.trace_off);

        state = ctx.interrupted ? State::COMPUTE : State::FETCH_MSG;
        ctxSwPerformed = false;
        ctx.interrupted = false;
        irqPending = false;
    }

    if (state != lastState ||
        (state == State::CTXSW && ctxsw.hasStateChanged()) ||
        (state == State::SYSCALL && sysc.hasStateChanged()) ||
        (state == State::IDLE && yield.hasStateChanged()))
    {
        DPRINTF(DtuAccelAladdinState, "[%s] tick\n",
            getStateName().c_str());
        lastState = state;
    }

    switch(state)
    {
        case State::IDLE:
        {
            pkt = yield.tick();
            break;
        }

        case State::CTXSW:
        {
            pkt = ctxsw.tick();
            break;
        }

        case State::FETCH_MSG:
        {
            if (irqPending)
            {
                irqPending = false;
                state = State::CTXSW;
                schedule(tickEvent, clockEdge(Cycles(1)));
            }
            else
            {
                Addr regAddr = getRegAddr(CmdReg::COMMAND);
                uint64_t value = Dtu::Command::FETCH_MSG | (EP_RECV << 4);
                pkt = createDtuRegPkt(regAddr, value, MemCmd::WriteReq);
            }
            break;
        }
        case State::READ_MSG_ADDR:
        {
            Addr regAddr = getRegAddr(CmdReg::OFFSET);
            pkt = createDtuRegPkt(regAddr, 0, MemCmd::ReadReq);
            break;
        }
        case State::READ_MSG:
        {
            size_t size = std::min(chunkSize, MSG_SIZE - ctx.msgOff);
            pkt = createPacket(ctx.msgAddr + ctx.msgOff, size, MemCmd::ReadReq);
            break;
        }

        case State::COMPUTE:
        {
            if (irqPending)
            {
                DPRINTF(DtuAccelAladdin,
                    "Interrupting work at %llu/%llu, off=%llu\n",
                    ctx.iteration, ctx.msg.iterations, ctx.trace_off);

                irqPending = false;
                ctx.interrupted = true;
                system->resetAccelerator(accelId);
                state = State::CTXSW;
                schedule(tickEvent, clockEdge(Cycles(1)));
                break;
            }

            auto addArray = [this](const char *name, Addr addr, Addr size) {
                system->insertArrayLabelMapping(
                    accelId, name, addr, size
                );
                // establish 1:1 mapping
                Addr page_off = addr & (TheISA::PageBytes - 1);
                int num_pages = ceil(((float)size + page_off) / TheISA::PageBytes);
                for(int i = 0; i < num_pages; i++)
                {
                    system->insertAddressTranslationMapping(
                        accelId,
                        addr + i * TheISA::PageBytes,
                        addr + i * TheISA::PageBytes
                    );
                }
            };

            // store the parameter names here instead of letting the client
            // send them, because we don't want to pay for the performance
            // overhead (they wouldn't be there in real life)
            static const char *stencil_arrays[] = {
                "orig", "sol", "C"
            };
            static const char *md_arrays[] = {
                "position_x", "position_y", "position_z",
                "force_x", "force_y", "force_z", "NL"
            };
            static const char *spmv_arrays[] = {
                "val", "cols", "rowDelimiters", "vec", "out"
            };
            static const char *fft_arrays[] = {
                "in_x", "in_y", "out_x", "out_y"
            };

            const char **names = nullptr;
            if (accelId == 0x00000120)
                names = stencil_arrays;
            else if (accelId == 0x000000B0)
                names = md_arrays;
            else if (accelId == 0x000000F0)
                names = spmv_arrays;
            else if (accelId == 0x00000060)
                names = fft_arrays;
            else
                fatal("Unknown accelerator id %d", accelId);

            for (uint64_t i = 0; i < ctx.msg.array_count; ++i)
                addArray(names[i], ctx.msg.arrays[i].addr, ctx.msg.arrays[i].size);
            system->activateAccelerator(accelId, 0x1000, 0, 0, ctx.trace_off);

            pkt = nullptr;
            break;
        }

        case State::STORE_REPLY:
        {
            pkt = createPacket(BUF_ADDR,
                               sizeof(reply.msg),
                               MemCmd::WriteReq);
            memcpy(pkt->getPtr<uint8_t>(), (char*)&reply.msg, sizeof(reply.msg));
            break;
        }
        case State::SEND_REPLY:
        {
            pkt = createDtuCmdPkt(Dtu::Command::REPLY,
                                  EP_RECV,
                                  BUF_ADDR,
                                  sizeof(reply.msg),
                                  ctx.msgAddr);
            break;
        }
        case State::REPLY_WAIT:
        {
            Addr regAddr = getRegAddr(CmdReg::COMMAND);
            pkt = createDtuRegPkt(regAddr, 0, MemCmd::ReadReq);
            break;
        }
        case State::REPLY_ERROR:
        {
            reply.sys.opcode = SyscallSM::Operation::FORWARD_REPLY;
            reply.sys.cap = CAP_RBUF;
            reply.sys.msgaddr = ctx.msgAddr;
            reply.sys.event = 0;
            reply.sys.len = sizeof(reply.msg);
            reply.sys.event = 0;

            pkt = createPacket(BUF_ADDR, sizeof(reply), MemCmd::WriteReq);
            memcpy(pkt->getPtr<void>(), &reply, sizeof(reply));
            break;
        }

        case State::SYSCALL:
        {
            pkt = sysc.tick();
            break;
        }
    }

    if (pkt != nullptr)
    {
        memPending = true;
        sendPkt(pkt);
    }
}

DtuAccelAladdin*
DtuAccelAladdinParams::create()
{
    return new DtuAccelAladdin(this);
}
<commit_msg>DtuAccelAladdin: fixed bug in interruptions.<commit_after>/*
 * Copyright (c) 2016, Nils Asmussen
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * The views and conclusions contained in the software and documentation are
 * those of the authors and should not be interpreted as representing official
 * policies, either expressed or implied, of the FreeBSD Project.
 */

#include "cpu/dtu-accel-aladdin/accelerator.hh"
#include "debug/DtuAccelAladdin.hh"
#include "debug/DtuAccelAladdinState.hh"
#include "mem/dtu/dtu.hh"
#include "mem/dtu/regfile.hh"
#include "sim/dtu_memory.hh"

#include <iomanip>

static const char *stateNames[] =
{
    "IDLE",

    "FETCH_MSG",
    "READ_MSG_ADDR",
    "READ_MSG",
    "COMPUTE",
    "STORE_REPLY",
    "SEND_REPLY",
    "REPLY_WAIT",
    "REPLY_ERROR",

    "CTXSW",

    "SYSCALL",
};

DtuAccelAladdin::DtuAccelAladdin(const DtuAccelAladdinParams *p)
  : DtuAccel(p),
    irqPending(false),
    ctxSwPending(false),
    memPending(false),
    state(State::IDLE),
    lastState(State::CTXSW),    // something different
    ctx(),
    accelId(p->accel_id),
    sysc(this),
    yield(this, &sysc),
    ctxsw(this),
    ctxSwPerformed()
{
    static_assert((sizeof(stateNames) / sizeof(stateNames[0]) ==
                  static_cast<size_t>(State::SYSCALL) + 1), "Missmatch");

    yield.start();
}

std::string DtuAccelAladdin::getStateName() const
{
    std::ostringstream os;
    os << stateNames[static_cast<size_t>(state)];
    if (state == State::IDLE)
        os << ":" << yield.stateName();
    else if (state == State::SYSCALL)
        os << ":" << sysc.stateName();
    else if (state == State::CTXSW)
        os << ":" << ctxsw.stateName();
    return os.str();
}

void
DtuAccelAladdin::completeRequest(PacketPtr pkt)
{
    Request* req = pkt->req;

    if (state != lastState ||
        (state == State::CTXSW && ctxsw.hasStateChanged()) ||
        (state == State::SYSCALL && sysc.hasStateChanged()) ||
        (state == State::IDLE && yield.hasStateChanged()))
    {
        DPRINTF(DtuAccelAladdinState, "[%s] Got response from memory\n",
            getStateName().c_str());
        lastState = state;
    }

    const uint8_t *pkt_data = pkt->getConstPtr<uint8_t>();

    Cycles delay(1);
    if (pkt->isError())
    {
        warn("%s access failed at %#x\n",
             pkt->isWrite() ? "Write" : "Read", req->getPaddr());
    }
    else
    {
        switch(state)
        {
            case State::IDLE:
            {
                if (yield.handleMemResp(pkt))
                {
                    if (irqPending)
                        irqPending = false;
                    state = State::CTXSW;
                }
                break;
            }

            case State::CTXSW:
            {
                if(ctxsw.handleMemResp(pkt))
                {
                    if(!ctxSwPerformed && ctx.interrupted)
                    {
                        ctx.interrupted = false;
                        state = State::COMPUTE;
                    }
                    else
                        state = State::FETCH_MSG;
                }
                break;
            }

            case State::FETCH_MSG:
            {
                state = State::READ_MSG_ADDR;
                break;
            }
            case State::READ_MSG_ADDR:
            {
                const RegFile::reg_t *regs = pkt->getConstPtr<RegFile::reg_t>();
                if(regs[0])
                {
                    ctx.msgAddr = regs[0];
                    ctx.msgOff = 0;
                    DPRINTF(DtuAccelAladdin, "Received message @ %p\n", ctx.msgAddr);
                    state = State::READ_MSG;
                }
                else
                {
                    yield.start();
                    state = State::IDLE;
                }
                break;
            }
            case State::READ_MSG:
            {
                size_t maxOff = sizeof(ctx.msg) + sizeof(MessageHeader);
                if (ctx.msgOff < maxOff)
                {
                    char *dst = (char*)&ctx.msg;
                    size_t amount = std::min<size_t>(maxOff - ctx.msgOff, pkt->getSize());
                    if (ctx.msgOff == 0)
                    {
                        pkt_data += sizeof(MessageHeader);
                        amount -= sizeof(MessageHeader);
                    }
                    else
                        dst += ctx.msgOff - sizeof(MessageHeader);
                    memcpy(dst, pkt_data, amount);
                }

                ctx.msgOff += pkt->getSize();
                if (ctx.msgOff == MSG_SIZE)
                {
                    DPRINTF(DtuAccelAladdin,
                        "  invokeMsg(iters=%llu, arrays=%llu: [\n",
                        ctx.msg.iterations, ctx.msg.array_count);
                    for(uint64_t i = 0; i < ctx.msg.array_count; ++i)
                    {
                        DPRINTFR(DtuAccelAladdin, "    addr=%#llx, size=%#llx\n",
                            ctx.msg.arrays[i].addr, ctx.msg.arrays[i].size);
                    }
                    DPRINTFR(DtuAccelAladdin, "  ])\n");

                    ctx.iteration = 0;
                    ctx.interrupted = false;
                    state = State::COMPUTE;
                }
                break;
            }
            case State::COMPUTE:
            {
                assert(false);
            }
            case State::STORE_REPLY:
            {
                state = State::SEND_REPLY;
                break;
            }
            case State::SEND_REPLY:
            {
                state = State::REPLY_WAIT;
                break;
            }
            case State::REPLY_WAIT:
            {
                Dtu::Command::Bits cmd =
                    *reinterpret_cast<const RegFile::reg_t*>(pkt_data);
                if (cmd.opcode == 0)
                {
                    if (cmd.error == 0)
                        state = State::CTXSW;
                    else
                        state = State::REPLY_ERROR;
                }
                break;
            }
            case State::REPLY_ERROR:
            {
                sysc.start(sizeof(reply));
                syscNext = State::CTXSW;
                state = State::SYSCALL;
                break;
            }

            case State::SYSCALL:
            {
                if(sysc.handleMemResp(pkt))
                    state = syscNext;
                break;
            }
        }
    }

    memPending = false;
    freePacket(pkt);

    // kick things into action again
    schedule(tickEvent, clockEdge(delay));
}

void
DtuAccelAladdin::interrupt()
{
    irqPending = true;

    if (ctxsw.isWaiting())
    {
        ctxsw.restart();
        if (!memPending && !tickEvent.scheduled())
            schedule(tickEvent, clockEdge(Cycles(1)));
    }
    // don't interrupt the accelerator logic; this seems to cause trouble in Aladdin
    // else if(state == State::COMPUTE)
    // {
    //     if (!memPending && !tickEvent.scheduled())
    //         schedule(tickEvent, clockEdge(Cycles(1)));
    // }
}

void
DtuAccelAladdin::wakeup()
{
    // don't interrupt the accelerator logic
    if (state != State::COMPUTE)
    {
        sysc.retryFetch();

        if (!memPending && !tickEvent.scheduled())
            schedule(tickEvent, clockEdge(Cycles(1)));
    }
}

void
DtuAccelAladdin::reset()
{
    irqPending = false;

    yield.start(false);
    state = State::IDLE;
    memset(&ctx, 0, sizeof(ctx));
}

void
DtuAccelAladdin::signalFinished(size_t off)
{
    ctx.trace_off = off;
    ctx.iteration += 1;

    assert(state == State::COMPUTE);
    if (ctx.iteration == ctx.msg.iterations)
        state = State::STORE_REPLY;

    if (!memPending && !tickEvent.scheduled())
        schedule(tickEvent, clockEdge(Cycles(1)));
}

void
DtuAccelAladdin::tick()
{
    PacketPtr pkt = nullptr;

    // after a context switch, continue at then position we left off
    if (ctxSwPerformed)
    {
        DPRINTF(DtuAccelAladdin,
            "Resuming %swork at %llu/%llu, off=%llu\n",
            ctx.interrupted ? "interrupted " : "",
            ctx.iteration, ctx.msg.iterations, ctx.trace_off);

        state = ctx.interrupted ? State::COMPUTE : State::FETCH_MSG;
        ctxSwPerformed = false;
        ctx.interrupted = false;
        irqPending = false;
    }

    if (state != lastState ||
        (state == State::CTXSW && ctxsw.hasStateChanged()) ||
        (state == State::SYSCALL && sysc.hasStateChanged()) ||
        (state == State::IDLE && yield.hasStateChanged()))
    {
        DPRINTF(DtuAccelAladdinState, "[%s] tick\n",
            getStateName().c_str());
        lastState = state;
    }

    switch(state)
    {
        case State::IDLE:
        {
            pkt = yield.tick();
            break;
        }

        case State::CTXSW:
        {
            pkt = ctxsw.tick();
            break;
        }

        case State::FETCH_MSG:
        {
            if (irqPending)
            {
                irqPending = false;
                state = State::CTXSW;
                schedule(tickEvent, clockEdge(Cycles(1)));
            }
            else
            {
                Addr regAddr = getRegAddr(CmdReg::COMMAND);
                uint64_t value = Dtu::Command::FETCH_MSG | (EP_RECV << 4);
                pkt = createDtuRegPkt(regAddr, value, MemCmd::WriteReq);
            }
            break;
        }
        case State::READ_MSG_ADDR:
        {
            Addr regAddr = getRegAddr(CmdReg::OFFSET);
            pkt = createDtuRegPkt(regAddr, 0, MemCmd::ReadReq);
            break;
        }
        case State::READ_MSG:
        {
            size_t size = std::min(chunkSize, MSG_SIZE - ctx.msgOff);
            pkt = createPacket(ctx.msgAddr + ctx.msgOff, size, MemCmd::ReadReq);
            break;
        }

        case State::COMPUTE:
        {
            if (irqPending)
            {
                DPRINTF(DtuAccelAladdin,
                    "Interrupting work at %llu/%llu, off=%llu\n",
                    ctx.iteration, ctx.msg.iterations, ctx.trace_off);

                irqPending = false;
                ctx.interrupted = true;
                system->resetAccelerator(accelId);
                state = State::CTXSW;
                schedule(tickEvent, clockEdge(Cycles(1)));
                break;
            }

            auto addArray = [this](const char *name, Addr addr, Addr size) {
                system->insertArrayLabelMapping(
                    accelId, name, addr, size
                );
                // establish 1:1 mapping
                Addr page_off = addr & (TheISA::PageBytes - 1);
                int num_pages = ceil(((float)size + page_off) / TheISA::PageBytes);
                for(int i = 0; i < num_pages; i++)
                {
                    system->insertAddressTranslationMapping(
                        accelId,
                        addr + i * TheISA::PageBytes,
                        addr + i * TheISA::PageBytes
                    );
                }
            };

            // store the parameter names here instead of letting the client
            // send them, because we don't want to pay for the performance
            // overhead (they wouldn't be there in real life)
            static const char *stencil_arrays[] = {
                "orig", "sol", "C"
            };
            static const char *md_arrays[] = {
                "position_x", "position_y", "position_z",
                "force_x", "force_y", "force_z", "NL"
            };
            static const char *spmv_arrays[] = {
                "val", "cols", "rowDelimiters", "vec", "out"
            };
            static const char *fft_arrays[] = {
                "in_x", "in_y", "out_x", "out_y"
            };

            const char **names = nullptr;
            if (accelId == 0x00000120)
                names = stencil_arrays;
            else if (accelId == 0x000000B0)
                names = md_arrays;
            else if (accelId == 0x000000F0)
                names = spmv_arrays;
            else if (accelId == 0x00000060)
                names = fft_arrays;
            else
                fatal("Unknown accelerator id %d", accelId);

            for (uint64_t i = 0; i < ctx.msg.array_count; ++i)
                addArray(names[i], ctx.msg.arrays[i].addr, ctx.msg.arrays[i].size);
            system->activateAccelerator(accelId, 0x1000, 0, 0, ctx.trace_off);

            pkt = nullptr;
            break;
        }

        case State::STORE_REPLY:
        {
            pkt = createPacket(BUF_ADDR,
                               sizeof(reply.msg),
                               MemCmd::WriteReq);
            memcpy(pkt->getPtr<uint8_t>(), (char*)&reply.msg, sizeof(reply.msg));
            break;
        }
        case State::SEND_REPLY:
        {
            pkt = createDtuCmdPkt(Dtu::Command::REPLY,
                                  EP_RECV,
                                  BUF_ADDR,
                                  sizeof(reply.msg),
                                  ctx.msgAddr);
            break;
        }
        case State::REPLY_WAIT:
        {
            Addr regAddr = getRegAddr(CmdReg::COMMAND);
            pkt = createDtuRegPkt(regAddr, 0, MemCmd::ReadReq);
            break;
        }
        case State::REPLY_ERROR:
        {
            reply.sys.opcode = SyscallSM::Operation::FORWARD_REPLY;
            reply.sys.cap = CAP_RBUF;
            reply.sys.msgaddr = ctx.msgAddr;
            reply.sys.event = 0;
            reply.sys.len = sizeof(reply.msg);
            reply.sys.event = 0;

            pkt = createPacket(BUF_ADDR, sizeof(reply), MemCmd::WriteReq);
            memcpy(pkt->getPtr<void>(), &reply, sizeof(reply));
            break;
        }

        case State::SYSCALL:
        {
            pkt = sysc.tick();
            break;
        }
    }

    if (pkt != nullptr)
    {
        memPending = true;
        sendPkt(pkt);
    }
}

DtuAccelAladdin*
DtuAccelAladdinParams::create()
{
    return new DtuAccelAladdin(this);
}
<|endoftext|>


<commit_before>#include "builtin/compactlookuptable.hpp"
#include "builtin/lookuptable.hpp"
#include "builtin/object.hpp"
#include "builtin/string.hpp"
#include "builtin/symbol.hpp"

#include "capi/capi.hpp"
#include "capi/18/include/ruby.h"

#include "configuration.hpp"

using namespace rubinius;
using namespace rubinius::capi;

extern "C" {

  void rb_error_frozen(const char* what) {
    if(LANGUAGE_18_ENABLED(NativeMethodEnvironment::get()->state())){
      rb_raise(rb_eTypeError, "can't modify frozen %s", what);
    } else {
      rb_raise(rb_eRuntimeError, "can't modify frozen %s", what);
    }
  }

  VALUE rb_obj_frozen_p(VALUE obj) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();
    if(env->get_object(obj)->frozen_p(env->state()) == RBX_Qtrue) {
      return Qtrue;
    }

    return Qfalse;
  }

  void rb_check_frozen(VALUE obj_handle) {
    if(rb_obj_frozen_p(obj_handle)){
      const char *class_name = rb_obj_classname(obj_handle);
      rb_error_frozen(class_name);
    }
  }

  VALUE rb_obj_freeze(VALUE hndl) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();
    env->get_object(hndl)->freeze(env->state());
    return hndl;
  }

  // Copied from MRI
  static struct types {
    int type;
    const char *name;
  } builtin_types[] = {
    {T_NIL,      "nil"},
    {T_OBJECT,  "Object"},
    {T_CLASS,    "Class"},
    {T_ICLASS,  "iClass"},    /* internal use: mixed-in module holder */
    {T_MODULE,  "Module"},
    {T_FLOAT,   "Float"},
    {T_STRING,  "String"},
    {T_REGEXP,  "Regexp"},
    {T_ARRAY,   "Array"},
    {T_FIXNUM,  "Fixnum"},
    {T_HASH,    "Hash"},
    {T_STRUCT,  "Struct"},
    {T_BIGNUM,  "Bignum"},
    {T_FILE,    "File"},
    {T_TRUE,    "true"},
    {T_FALSE,   "false"},
    {T_SYMBOL,  "Symbol"},    /* :symbol */
    {T_DATA,    "Data"},      /* internal use: wrapped C pointers */
    {T_MATCH,   "MatchData"}, /* data of $~ */
    {T_VARMAP,  "Varmap"},    /* internal use: dynamic variables */
    {T_SCOPE,   "Scope"},     /* internal use: variable scope */
    {T_NODE,    "Node"},      /* internal use: syntax tree node */
    {T_UNDEF,   "undef"},     /* internal use: #undef; should not happen */
    {T_RATIONAL, "Rational" },
    {T_COMPLEX,  "Complex" },
    {-1,  0}
  };

  // Copied from MRI
  void rb_check_type(VALUE x, int t) {
    struct types *type = builtin_types;

    if (x == Qundef) {
      rb_bug("undef leaked to the Ruby space");
    }

    if (TYPE(x) != t) {
      while (type->type >= 0) {
        if (type->type == t) {
          const char *etype;

          if (NIL_P(x)) {
            etype = "nil";
          } else if (FIXNUM_P(x)) {
            etype = "Fixnum";
          } else if (SYMBOL_P(x)) {
            etype = "Symbol";
          } else if (rb_special_const_p(x)) {
            etype = RSTRING_PTR(rb_obj_as_string(x));
          } else {
            etype = rb_obj_classname(x);
          }

          rb_raise(rb_eTypeError, "wrong argument type %s (expected %s)",
              etype, type->name);
        }
        type++;
      }

      rb_raise(rb_eRuntimeError, "unknown type 0x%x", t);
    }
  }

  VALUE rb_check_array_type(VALUE object_handle) {
    return rb_check_convert_type(object_handle, 0, "Array", "to_ary");
  }

  VALUE rb_check_string_type(VALUE object_handle) {
    return rb_check_convert_type(object_handle, 0, "String", "to_str");
  }

  VALUE rb_check_convert_type(VALUE object_handle, int /*type*/,
                              const char* type_name, const char* method_name)
  {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();
    VALUE name = env->get_handle(String::create(env->state(), method_name));

    if(RTEST(rb_funcall(object_handle, rb_intern("respond_to?"), 1, name)) ) {
      return rb_funcall2(object_handle, rb_intern(method_name), 0, NULL);
    }

    return Qnil;
  }

  VALUE rb_check_to_integer(VALUE object_handle, const char *method_name) {
    if(FIXNUM_P(object_handle)) {
      return object_handle;
    }
    VALUE result = rb_check_convert_type(object_handle, 0, "Integer", method_name);
    if(rb_obj_is_kind_of(result, rb_cInteger)) {
      return result;
    }
    return Qnil;
  }

  /** @todo   This is horrible. Refactor. --rue */
  VALUE rb_convert_type(VALUE object_handle, int type,
                        const char* type_name, const char* method_name)
  {
    VALUE return_handle = rb_check_convert_type(object_handle, type,
                                                type_name, method_name);

    if(NIL_P(return_handle)) {
      rb_raise(rb_eTypeError, "can't convert %s into %s",
               RBX_NIL_P(object_handle) ? "nil" :
                RBX_TRUE_P(object_handle) ? "true" :
                  RBX_FALSE_P(object_handle) ? "false" :
                    rb_obj_classname(object_handle),
               type_name);
    }

    VALUE klass = rb_const_get(rb_cObject, rb_intern(type_name));

    if(!RTEST(rb_obj_is_kind_of(return_handle, klass))) {
      rb_raise(rb_eTypeError, "%s#%s should return %s",
               rb_obj_classname(object_handle), method_name, type_name);
    }

    return return_handle;
  }

  int rb_type(VALUE obj) {
    if (FIXNUM_P(obj)) return T_FIXNUM;
    if (obj == Qnil) return T_NIL;
    if (obj == Qfalse) return T_FALSE;
    if (obj == Qtrue) return T_TRUE;
    if (obj == Qundef) return T_UNDEF;
    if (SYMBOL_P(obj)) return T_SYMBOL;

    NativeMethodEnvironment* env = NativeMethodEnvironment::get();
    Object* object = env->get_object(obj);
    switch(object->type_id()) {
    case ArrayType:
      return T_ARRAY;
    case BignumType:
      return T_BIGNUM;
    case ClassType:
      return T_CLASS;
    case DataType:
      return T_DATA;
    case FloatType:
      return T_FLOAT;
    case ModuleType:
      return T_MODULE;
    case RegexpType:
      return T_REGEXP;
    case StringType:
      return T_STRING;
    case TimeType:
      return T_DATA;
    default:
      // This is in the default branch to avoid compiler warnings
      // about other enum values for type_id() not being present.
      if(rb_obj_is_kind_of(obj, rb_cHash)) return T_HASH;
      if(rb_obj_is_kind_of(obj, rb_cStruct)) return T_STRUCT;
      if(rb_obj_is_kind_of(obj, rb_cIO)) return T_FILE;
      if(rb_obj_is_kind_of(obj, rb_cMatch)) return T_MATCH;
      if(!LANGUAGE_18_ENABLED(env->state())) {
        if(rb_obj_is_kind_of(obj, rb_cRational)) return T_RATIONAL;
        if(rb_obj_is_kind_of(obj, rb_cComplex)) return T_COMPLEX;
        if(rb_obj_is_kind_of(obj, rb_cEncoding)) return T_ENCODING;
      }
    }

    return T_OBJECT;
  }

  ID rb_to_id(VALUE object_handle) {
    return SYM2ID(rb_funcall2(object_handle, rb_intern("to_sym"), 0, NULL));
  }

  VALUE rb_obj_alloc(VALUE class_handle) {
    return rb_funcall(class_handle, rb_intern("allocate"), 0);
  }

  VALUE rb_obj_dup(VALUE obj) {
    if(rb_special_const_p(obj))
      rb_raise(rb_eTypeError, "can't dup %s", rb_obj_classname(obj));

    return rb_funcall(obj, rb_intern("dup"), 0);
  }

  VALUE rb_obj_as_string(VALUE obj_handle) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    Object* object = env->get_object(obj_handle);

    if (kind_of<String>(object)) {
      return obj_handle;
    }

    return rb_funcall2(obj_handle, rb_intern("to_s"), 0, NULL);
  }

  VALUE rb_obj_clone(VALUE obj_handle) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    env->flush_cached_data();
    return rb_funcall(obj_handle, rb_intern("clone"), 0);
  }

  VALUE rb_inspect(VALUE obj_handle) {
    return rb_obj_as_string(rb_funcall(obj_handle, rb_intern("inspect"), 0, NULL));
  }

  void rb_obj_call_init(VALUE object_handle, int arg_count, VALUE* args) {
    (void) rb_funcall2(object_handle, rb_intern("initialize"), arg_count, args);
  }

  const char* rb_obj_classname(VALUE object_handle) {
    return rb_class2name(rb_class_of(object_handle));
  }

  VALUE rb_obj_is_instance_of(VALUE object_handle, VALUE class_handle) {
    return rb_funcall(object_handle, rb_intern("instance_of?"), 1, class_handle);
  }

  VALUE rb_obj_is_kind_of(VALUE object_handle, VALUE module_handle) {
    return rb_funcall(object_handle, rb_intern("kind_of?"), 1, module_handle);
  }

  ID rb_intern(const char* string) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    return reinterpret_cast<ID>(env->state()->symbol(string));
  }

  VALUE rb_iv_get(VALUE self_handle, const char* name) {
    return rb_ivar_get(self_handle, rb_intern(name));
  }

  VALUE rb_iv_set(VALUE self_handle, const char* name, VALUE value) {
    return rb_ivar_set(self_handle, rb_intern(name), value);
  }

  VALUE rb_ivar_get(VALUE self_handle, ID ivar_name) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    Object* object = env->get_object(self_handle);

    Symbol* sym = reinterpret_cast<Symbol*>(ivar_name);
    return env->get_handle(object->get_ivar(env->state(), sym));
  }

  VALUE rb_ivar_set(VALUE self_handle, ID ivar_name, VALUE value) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    Object* receiver = env->get_object(self_handle);

    Symbol* sym = reinterpret_cast<Symbol*>(ivar_name);
    receiver->set_ivar(env->state(), sym,
                       env->get_object(value));

    return value;
  }

  VALUE rb_ivar_defined(VALUE obj_handle, ID ivar_name) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    Symbol* ivar = reinterpret_cast<Symbol*>(ivar_name);
    Object* obj = env->get_object(obj_handle);
    Object* ret = obj->ivar_defined(env->state(), ivar);

    return env->get_handle(ret);
  }

  VALUE rb_attr_get(VALUE obj_handle, ID attr_name) {
    return rb_ivar_get(obj_handle, attr_name);
  }

  int rb_respond_to(VALUE obj_handle, ID method_name) {
    return RTEST(rb_funcall(obj_handle, rb_intern("respond_to?"),
          1, ID2SYM(method_name)));
  }

  void rb_extend_object(VALUE obj, VALUE mod) {
    rb_funcall(obj, rb_intern("extend"), 1, mod);
  }

  VALUE rb_obj_id(VALUE self) {
    return rb_funcall(self, rb_intern("object_id"), 0);
  }

  VALUE rb_obj_taint(VALUE obj) {
    if(!OBJ_TAINTED(obj)) {
      rb_check_frozen(obj);
      OBJ_TAINT(obj);
    }

    return obj;
  }

  void rb_check_safe_obj(VALUE obj) {
  }

  void rb_check_safe_str(VALUE obj) {
  }

  void rb_secure_update(VALUE obj) {
  }

  VALUE rb_any_to_s(VALUE obj) {
    return rb_obj_as_string(obj);
  }

  VALUE rb_obj_instance_eval(int argc, VALUE* argv, VALUE self) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();
    VALUE block = env->get_handle(env->block());

    return rb_funcall2b(self, rb_intern("instance_eval"), argc,
                        (const VALUE*)argv, block);
  }

  VALUE rb_to_int(VALUE object_handle) {
    return rb_convert_type(object_handle, 0, "Integer", "to_int");
  }

  VALUE rb_hash(VALUE obj) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    Object* hash = env->get_object(rb_funcall(obj, rb_intern("hash"), 0));

    retry:

    if(try_as<Fixnum>(hash)) {
      return env->get_handle(hash);
    } else if(Bignum* big = try_as<Bignum>(hash)) {
      return LONG2FIX(big->to_native());
    } else {
      hash = env->get_object(rb_to_int(env->get_handle(hash)));
      goto retry;
    }
  }

  VALUE rb_equal(VALUE obj1, VALUE obj2) {
    if(obj1 == obj2) return Qtrue;
    VALUE result = rb_funcall(obj1, rb_intern("=="), 1, obj2);
    if(RTEST(result)) return Qtrue;
    return Qfalse;
  }

  VALUE rb_class_inherited_p(VALUE mod, VALUE arg) {
    if(TYPE(arg) != T_MODULE && TYPE(arg) != T_CLASS) {
      rb_raise(rb_eTypeError, "compared with non class/module");
    }

    return rb_funcall(mod, rb_intern("<="), 1, arg);
  }

  int rb_obj_respond_to(VALUE obj, ID method_name, int priv) {
    VALUE include_private = priv == 1 ? Qtrue : Qfalse;
    return RTEST(rb_funcall(obj, rb_intern("respond_to?"), 2, ID2SYM(method_name), include_private));
  }
}
<commit_msg>Forward rb_check_{array,string}_type to Rubinius::Type.try_convert<commit_after>#include "builtin/compactlookuptable.hpp"
#include "builtin/lookuptable.hpp"
#include "builtin/object.hpp"
#include "builtin/string.hpp"
#include "builtin/symbol.hpp"

#include "capi/capi.hpp"
#include "capi/18/include/ruby.h"

#include "configuration.hpp"

using namespace rubinius;
using namespace rubinius::capi;

extern "C" {

  void rb_error_frozen(const char* what) {
    if(LANGUAGE_18_ENABLED(NativeMethodEnvironment::get()->state())){
      rb_raise(rb_eTypeError, "can't modify frozen %s", what);
    } else {
      rb_raise(rb_eRuntimeError, "can't modify frozen %s", what);
    }
  }

  VALUE rb_obj_frozen_p(VALUE obj) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();
    if(env->get_object(obj)->frozen_p(env->state()) == RBX_Qtrue) {
      return Qtrue;
    }

    return Qfalse;
  }

  void rb_check_frozen(VALUE obj_handle) {
    if(rb_obj_frozen_p(obj_handle)){
      const char *class_name = rb_obj_classname(obj_handle);
      rb_error_frozen(class_name);
    }
  }

  VALUE rb_obj_freeze(VALUE hndl) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();
    env->get_object(hndl)->freeze(env->state());
    return hndl;
  }

  // Copied from MRI
  static struct types {
    int type;
    const char *name;
  } builtin_types[] = {
    {T_NIL,      "nil"},
    {T_OBJECT,  "Object"},
    {T_CLASS,    "Class"},
    {T_ICLASS,  "iClass"},    /* internal use: mixed-in module holder */
    {T_MODULE,  "Module"},
    {T_FLOAT,   "Float"},
    {T_STRING,  "String"},
    {T_REGEXP,  "Regexp"},
    {T_ARRAY,   "Array"},
    {T_FIXNUM,  "Fixnum"},
    {T_HASH,    "Hash"},
    {T_STRUCT,  "Struct"},
    {T_BIGNUM,  "Bignum"},
    {T_FILE,    "File"},
    {T_TRUE,    "true"},
    {T_FALSE,   "false"},
    {T_SYMBOL,  "Symbol"},    /* :symbol */
    {T_DATA,    "Data"},      /* internal use: wrapped C pointers */
    {T_MATCH,   "MatchData"}, /* data of $~ */
    {T_VARMAP,  "Varmap"},    /* internal use: dynamic variables */
    {T_SCOPE,   "Scope"},     /* internal use: variable scope */
    {T_NODE,    "Node"},      /* internal use: syntax tree node */
    {T_UNDEF,   "undef"},     /* internal use: #undef; should not happen */
    {T_RATIONAL, "Rational" },
    {T_COMPLEX,  "Complex" },
    {-1,  0}
  };

  // Copied from MRI
  void rb_check_type(VALUE x, int t) {
    struct types *type = builtin_types;

    if (x == Qundef) {
      rb_bug("undef leaked to the Ruby space");
    }

    if (TYPE(x) != t) {
      while (type->type >= 0) {
        if (type->type == t) {
          const char *etype;

          if (NIL_P(x)) {
            etype = "nil";
          } else if (FIXNUM_P(x)) {
            etype = "Fixnum";
          } else if (SYMBOL_P(x)) {
            etype = "Symbol";
          } else if (rb_special_const_p(x)) {
            etype = RSTRING_PTR(rb_obj_as_string(x));
          } else {
            etype = rb_obj_classname(x);
          }

          rb_raise(rb_eTypeError, "wrong argument type %s (expected %s)",
              etype, type->name);
        }
        type++;
      }

      rb_raise(rb_eRuntimeError, "unknown type 0x%x", t);
    }
  }

  VALUE rb_check_array_type(VALUE object_handle) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();
    return rb_funcall(env->get_handle(env->state()->globals().type.get()), rb_intern("try_convert"), 3, object_handle, rb_cArray, rb_intern("to_ary"));
  }

  VALUE rb_check_string_type(VALUE object_handle) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();
    return rb_funcall(env->get_handle(env->state()->globals().type.get()), rb_intern("try_convert"), 3, object_handle, rb_cString, rb_intern("to_str"));
  }

  VALUE rb_check_convert_type(VALUE object_handle, int /*type*/,
                              const char* type_name, const char* method_name)
  {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();
    VALUE name = env->get_handle(String::create(env->state(), method_name));

    if(RTEST(rb_funcall(object_handle, rb_intern("respond_to?"), 1, name)) ) {
      return rb_funcall2(object_handle, rb_intern(method_name), 0, NULL);
    }

    return Qnil;
  }

  VALUE rb_check_to_integer(VALUE object_handle, const char *method_name) {
    if(FIXNUM_P(object_handle)) {
      return object_handle;
    }
    VALUE result = rb_check_convert_type(object_handle, 0, "Integer", method_name);
    if(rb_obj_is_kind_of(result, rb_cInteger)) {
      return result;
    }
    return Qnil;
  }

  /** @todo   This is horrible. Refactor. --rue */
  VALUE rb_convert_type(VALUE object_handle, int type,
                        const char* type_name, const char* method_name)
  {
    VALUE return_handle = rb_check_convert_type(object_handle, type,
                                                type_name, method_name);

    if(NIL_P(return_handle)) {
      rb_raise(rb_eTypeError, "can't convert %s into %s",
               RBX_NIL_P(object_handle) ? "nil" :
                RBX_TRUE_P(object_handle) ? "true" :
                  RBX_FALSE_P(object_handle) ? "false" :
                    rb_obj_classname(object_handle),
               type_name);
    }

    VALUE klass = rb_const_get(rb_cObject, rb_intern(type_name));

    if(!RTEST(rb_obj_is_kind_of(return_handle, klass))) {
      rb_raise(rb_eTypeError, "%s#%s should return %s",
               rb_obj_classname(object_handle), method_name, type_name);
    }

    return return_handle;
  }

  int rb_type(VALUE obj) {
    if (FIXNUM_P(obj)) return T_FIXNUM;
    if (obj == Qnil) return T_NIL;
    if (obj == Qfalse) return T_FALSE;
    if (obj == Qtrue) return T_TRUE;
    if (obj == Qundef) return T_UNDEF;
    if (SYMBOL_P(obj)) return T_SYMBOL;

    NativeMethodEnvironment* env = NativeMethodEnvironment::get();
    Object* object = env->get_object(obj);
    switch(object->type_id()) {
    case ArrayType:
      return T_ARRAY;
    case BignumType:
      return T_BIGNUM;
    case ClassType:
      return T_CLASS;
    case DataType:
      return T_DATA;
    case FloatType:
      return T_FLOAT;
    case ModuleType:
      return T_MODULE;
    case RegexpType:
      return T_REGEXP;
    case StringType:
      return T_STRING;
    case TimeType:
      return T_DATA;
    default:
      // This is in the default branch to avoid compiler warnings
      // about other enum values for type_id() not being present.
      if(rb_obj_is_kind_of(obj, rb_cHash)) return T_HASH;
      if(rb_obj_is_kind_of(obj, rb_cStruct)) return T_STRUCT;
      if(rb_obj_is_kind_of(obj, rb_cIO)) return T_FILE;
      if(rb_obj_is_kind_of(obj, rb_cMatch)) return T_MATCH;
      if(!LANGUAGE_18_ENABLED(env->state())) {
        if(rb_obj_is_kind_of(obj, rb_cRational)) return T_RATIONAL;
        if(rb_obj_is_kind_of(obj, rb_cComplex)) return T_COMPLEX;
        if(rb_obj_is_kind_of(obj, rb_cEncoding)) return T_ENCODING;
      }
    }

    return T_OBJECT;
  }

  ID rb_to_id(VALUE object_handle) {
    return SYM2ID(rb_funcall2(object_handle, rb_intern("to_sym"), 0, NULL));
  }

  VALUE rb_obj_alloc(VALUE class_handle) {
    return rb_funcall(class_handle, rb_intern("allocate"), 0);
  }

  VALUE rb_obj_dup(VALUE obj) {
    if(rb_special_const_p(obj))
      rb_raise(rb_eTypeError, "can't dup %s", rb_obj_classname(obj));

    return rb_funcall(obj, rb_intern("dup"), 0);
  }

  VALUE rb_obj_as_string(VALUE obj_handle) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    Object* object = env->get_object(obj_handle);

    if (kind_of<String>(object)) {
      return obj_handle;
    }

    return rb_funcall2(obj_handle, rb_intern("to_s"), 0, NULL);
  }

  VALUE rb_obj_clone(VALUE obj_handle) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    env->flush_cached_data();
    return rb_funcall(obj_handle, rb_intern("clone"), 0);
  }

  VALUE rb_inspect(VALUE obj_handle) {
    return rb_obj_as_string(rb_funcall(obj_handle, rb_intern("inspect"), 0, NULL));
  }

  void rb_obj_call_init(VALUE object_handle, int arg_count, VALUE* args) {
    (void) rb_funcall2(object_handle, rb_intern("initialize"), arg_count, args);
  }

  const char* rb_obj_classname(VALUE object_handle) {
    return rb_class2name(rb_class_of(object_handle));
  }

  VALUE rb_obj_is_instance_of(VALUE object_handle, VALUE class_handle) {
    return rb_funcall(object_handle, rb_intern("instance_of?"), 1, class_handle);
  }

  VALUE rb_obj_is_kind_of(VALUE object_handle, VALUE module_handle) {
    return rb_funcall(object_handle, rb_intern("kind_of?"), 1, module_handle);
  }

  ID rb_intern(const char* string) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    return reinterpret_cast<ID>(env->state()->symbol(string));
  }

  VALUE rb_iv_get(VALUE self_handle, const char* name) {
    return rb_ivar_get(self_handle, rb_intern(name));
  }

  VALUE rb_iv_set(VALUE self_handle, const char* name, VALUE value) {
    return rb_ivar_set(self_handle, rb_intern(name), value);
  }

  VALUE rb_ivar_get(VALUE self_handle, ID ivar_name) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    Object* object = env->get_object(self_handle);

    Symbol* sym = reinterpret_cast<Symbol*>(ivar_name);
    return env->get_handle(object->get_ivar(env->state(), sym));
  }

  VALUE rb_ivar_set(VALUE self_handle, ID ivar_name, VALUE value) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    Object* receiver = env->get_object(self_handle);

    Symbol* sym = reinterpret_cast<Symbol*>(ivar_name);
    receiver->set_ivar(env->state(), sym,
                       env->get_object(value));

    return value;
  }

  VALUE rb_ivar_defined(VALUE obj_handle, ID ivar_name) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    Symbol* ivar = reinterpret_cast<Symbol*>(ivar_name);
    Object* obj = env->get_object(obj_handle);
    Object* ret = obj->ivar_defined(env->state(), ivar);

    return env->get_handle(ret);
  }

  VALUE rb_attr_get(VALUE obj_handle, ID attr_name) {
    return rb_ivar_get(obj_handle, attr_name);
  }

  int rb_respond_to(VALUE obj_handle, ID method_name) {
    return RTEST(rb_funcall(obj_handle, rb_intern("respond_to?"),
          1, ID2SYM(method_name)));
  }

  void rb_extend_object(VALUE obj, VALUE mod) {
    rb_funcall(obj, rb_intern("extend"), 1, mod);
  }

  VALUE rb_obj_id(VALUE self) {
    return rb_funcall(self, rb_intern("object_id"), 0);
  }

  VALUE rb_obj_taint(VALUE obj) {
    if(!OBJ_TAINTED(obj)) {
      rb_check_frozen(obj);
      OBJ_TAINT(obj);
    }

    return obj;
  }

  void rb_check_safe_obj(VALUE obj) {
  }

  void rb_check_safe_str(VALUE obj) {
  }

  void rb_secure_update(VALUE obj) {
  }

  VALUE rb_any_to_s(VALUE obj) {
    return rb_obj_as_string(obj);
  }

  VALUE rb_obj_instance_eval(int argc, VALUE* argv, VALUE self) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();
    VALUE block = env->get_handle(env->block());

    return rb_funcall2b(self, rb_intern("instance_eval"), argc,
                        (const VALUE*)argv, block);
  }

  VALUE rb_to_int(VALUE object_handle) {
    return rb_convert_type(object_handle, 0, "Integer", "to_int");
  }

  VALUE rb_hash(VALUE obj) {
    NativeMethodEnvironment* env = NativeMethodEnvironment::get();

    Object* hash = env->get_object(rb_funcall(obj, rb_intern("hash"), 0));

    retry:

    if(try_as<Fixnum>(hash)) {
      return env->get_handle(hash);
    } else if(Bignum* big = try_as<Bignum>(hash)) {
      return LONG2FIX(big->to_native());
    } else {
      hash = env->get_object(rb_to_int(env->get_handle(hash)));
      goto retry;
    }
  }

  VALUE rb_equal(VALUE obj1, VALUE obj2) {
    if(obj1 == obj2) return Qtrue;
    VALUE result = rb_funcall(obj1, rb_intern("=="), 1, obj2);
    if(RTEST(result)) return Qtrue;
    return Qfalse;
  }

  VALUE rb_class_inherited_p(VALUE mod, VALUE arg) {
    if(TYPE(arg) != T_MODULE && TYPE(arg) != T_CLASS) {
      rb_raise(rb_eTypeError, "compared with non class/module");
    }

    return rb_funcall(mod, rb_intern("<="), 1, arg);
  }

  int rb_obj_respond_to(VALUE obj, ID method_name, int priv) {
    VALUE include_private = priv == 1 ? Qtrue : Qfalse;
    return RTEST(rb_funcall(obj, rb_intern("respond_to?"), 2, ID2SYM(method_name), include_private));
  }
}
<|endoftext|>


<commit_before>#include "Destruct.h"

namespace dale
{
namespace Operation
{
bool
Destruct(Context *ctx, ParseResult *pr, ParseResult *pr_ret,
         llvm::IRBuilder<> *builder, bool value_is_ptr)
{
    pr->copyTo(pr_ret);

    if (pr->do_not_destruct) {
        return true;
    }

    assert(pr->type);

    /* If it's an array with a known size, call this function for
     * each element in the array in order from last to first. */
    if (pr->type->is_array && pr->type->array_size) {
        Type *mine =
            pr->type->array_type;
        llvm::BasicBlock   *mbl  = pr->block;
        int i = pr->type->array_size;
        llvm::Value *actual_value = pr->value;
        if (!pr->value) {
            return true;
        }
        if (!pr->value->getType()) {
            return true;
        }

        std::vector<Type *> types;
        if (!mine->is_array) {
            types.push_back(ctx->tr->getPointerType(mine));
            Function *fn = ctx->getFunction("destroy", &types, NULL, 0);
            if (!fn) {
                return true;
            }
        }

        /* Hmph: array literals are stored in the variable table as
         * actual arrays, rather than pointers to arrays. This should
         * be fixed at some point, but for now, if this value is not a
         * pointer, then store it in a temporary location. */

        if (!(pr->value->getType()->isPointerTy())) {
            if (builder) {
                llvm::Value *new_ptr2 = llvm::cast<llvm::Value>(
                        builder->CreateAlloca(
                            ctx->toLLVMType(pr->type, NULL, false))
                        );
                builder->CreateStore(pr->value, new_ptr2);
                actual_value = new_ptr2;
            } else {
                llvm::IRBuilder<> builder(mbl);
                llvm::Value *new_ptr2 = llvm::cast<llvm::Value>(
                        builder.CreateAlloca(
                            ctx->toLLVMType(pr->type, NULL, false))
                        );
                builder.CreateStore(pr->value, new_ptr2);
                actual_value = new_ptr2;
            }
        }

        for (i = (pr->type->array_size - 1); i >= 0; i--) {
            ParseResult temp;
            temp.type  = mine;
            temp.block = mbl;
            std::vector<llvm::Value *> indices;
            STL::push_back2(&indices,
                            ctx->nt->getLLVMZero(),
                            llvm::cast<llvm::Value>(
                                llvm::ConstantInt::get(
                                    ctx->nt->getNativeIntType(),
                                    i
                                )
                            ));
            ParseResult mnew;

            if (builder) {
                llvm::Value *res = builder->Insert(
                                       llvm::GetElementPtrInst::Create(
                                           actual_value,
                                           llvm::ArrayRef<llvm::Value*>(indices)
                                       ),
                                       "asdf"
                                   );
                if (!mine->is_array) {
                    temp.value = builder->CreateLoad(res);
                } else {
                    temp.value = res;
                }
                Destruct(ctx, &temp, &mnew, builder);
            }
            else {
                llvm::IRBuilder<> builder(mbl);
                llvm::Value *res = builder.Insert(
                                       llvm::GetElementPtrInst::Create(
                                           actual_value,
                                           llvm::ArrayRef<llvm::Value*>(indices)
                                       ),
                                       "asdf"
                                   );
                if (!mine->is_array) {
                    temp.value = builder.CreateLoad(res);
                } else {
                    temp.value = res;
                }
                Destruct(ctx, &temp, &mnew, &builder);
            }
            mbl = mnew.block;
        }
        pr_ret->block = mbl;
        return true;
    }

    std::vector<Type *> types;
    types.push_back(ctx->tr->getPointerType(pr->type));
    Function *fn = ctx->getFunction("destroy", &types,
                            NULL, 0);
    if (!fn) {
        /* If this is a struct, call Destruct on each of the elements,
         * in the absence of a destructor for the struct as a whole.
         * */
        Type *type = pr->type;
        if (type->struct_name.size()) {
            Struct *st = ctx->getStruct(type->struct_name.c_str(),
                                                 &(type->namespaces));
            std::vector<Type*> *st_types = &(st->member_types);
            int i = 0;
            llvm::Value *actual_value = pr->value;

            if (!value_is_ptr) {
                if (builder) {
                    llvm::Value *new_ptr2 = llvm::cast<llvm::Value>(
                            builder->CreateAlloca(
                                ctx->toLLVMType(pr->type, NULL, false))
                            );
                    builder->CreateStore(pr->value, new_ptr2);
                    actual_value = new_ptr2;
                } else {
                    llvm::IRBuilder<> builder(pr->block);
                    llvm::Value *new_ptr2 = llvm::cast<llvm::Value>(
                            builder.CreateAlloca(
                                ctx->toLLVMType(pr->type, NULL, false))
                            );
                    builder.CreateStore(pr->value, new_ptr2);
                    actual_value = new_ptr2;
                }
            }

            for (std::vector<Type*>::iterator
                    b = st_types->begin(),
                    e = st_types->end();
                    b != e;
                    ++b) {
                ParseResult element;
                ParseResult mnew;
                std::string ts;
                (*b)->toString(&ts);
                element.set(pr->block, *b, actual_value);
                std::vector<llvm::Value *> indices;
                STL::push_back2(&indices,
                                ctx->nt->getLLVMZero(),
                                llvm::cast<llvm::Value>(
                                    llvm::ConstantInt::get(
                                        ctx->nt->getNativeIntType(),
                                        i++
                                    )
                                ));
                if (builder) {
                    llvm::Value *res =
                        builder->Insert(
                            llvm::GetElementPtrInst::Create(
                                actual_value,
                                llvm::ArrayRef<llvm::Value*>(indices)
                            ),
                            "asdf"
                        );
                    element.value = res;
                    Destruct(ctx, &element, &mnew, builder, true);
                } else {
                    llvm::IRBuilder<> builder(pr->block);
                    llvm::Value *res =
                        builder.Insert(
                            llvm::GetElementPtrInst::Create(
                                actual_value,
                                llvm::ArrayRef<llvm::Value*>(indices)
                            ),
                            "asdf"
                        );
                    element.value = res;
                    Destruct(ctx, &element, &mnew, &builder, true);
                }
            }
        }
        return true;
    }
    int destroy_builder = 0;
    if (!builder) {
        destroy_builder = 1;
        builder = new llvm::IRBuilder<>(pr->block);
    }
    std::vector<llvm::Value *> call_args;
    llvm::Value *new_ptr2;
    if (value_is_ptr) {
        new_ptr2 = pr->value;
    } else {
        new_ptr2 = llvm::cast<llvm::Value>(
            builder->CreateAlloca(ctx->toLLVMType(pr->type, NULL, false))
        );
        builder->CreateStore(pr->value, new_ptr2);
    }

    call_args.push_back(new_ptr2);
    builder->CreateCall(
        fn->llvm_function,
        llvm::ArrayRef<llvm::Value*>(call_args));
    if (destroy_builder) {
        delete builder;
    }
    return true;
}
}
}
<commit_msg>[master] tidying<commit_after>#include "Destruct.h"

namespace dale
{
namespace Operation
{
Function *
getDestructor(Context *ctx, Type *type)
{
    std::vector<Type *> types;
    types.push_back(ctx->tr->getPointerType(type));
    Function *fn = ctx->getFunction("destroy", &types, NULL, 0);
    return fn;
}

bool
destructArray(Context *ctx, ParseResult *pr, ParseResult *pr_ret,
              llvm::IRBuilder<> *builder, bool value_is_ptr)
{
    Type *array_type = pr->type->array_type;
    llvm::BasicBlock *block = pr->block;
    llvm::Value *array_value = pr->value;

    if (!array_value) {
        return true;
    }
    if (!array_value->getType()) {
        return true;
    }

    if (!array_type->is_array) {
        Function *fn = getDestructor(ctx, array_type);
        if (!fn) {
            return true;
        }
    }

    llvm::Type *llvm_array_type = ctx->toLLVMType(pr->type, NULL, false);

    /* Array literals are stored in the variable table as actual
     * arrays, rather than pointers to arrays.  This should be fixed at
     * some point, but for now, if this value is not a pointer, then
     * store it in a temporary location. */

    llvm::IRBuilder<> internal_builder(block);
    if (!builder) {
        builder = &internal_builder;
    }

    if (!pr->value->getType()->isPointerTy()) {
        array_value = llvm::cast<llvm::Value>(
            builder->CreateAlloca(llvm_array_type)
        );
        builder->CreateStore(pr->value, array_value);
    }

    for (int i = (pr->type->array_size - 1); i >= 0; i--) {
        ParseResult temp;
        temp.type  = array_type;
        temp.block = block;
        std::vector<llvm::Value *> indices;
        STL::push_back2(&indices,
                        ctx->nt->getLLVMZero(),
                        llvm::cast<llvm::Value>(
                            llvm::ConstantInt::get(
                                ctx->nt->getNativeIntType(), i
                            )
                        ));
        ParseResult mnew;

        llvm::Value *res = builder->Insert(
                                llvm::GetElementPtrInst::Create(
                                    array_value,
                                    llvm::ArrayRef<llvm::Value*>(indices)
                                ),
                                "asdf"
                            );
        if (!array_type->is_array) {
            temp.value = builder->CreateLoad(res);
        } else {
            temp.value = res;
        }
        Destruct(ctx, &temp, &mnew, builder);
    }

    pr_ret->block = block;
    return true;
}

bool
Destruct(Context *ctx, ParseResult *pr, ParseResult *pr_ret,
         llvm::IRBuilder<> *builder, bool value_is_ptr)
{
    pr->copyTo(pr_ret);

    if (pr->do_not_destruct) {
        return true;
    }

    assert(pr->type);

    /* If it's an array with a known size, call this function for
     * each element in the array, in order from last to first. */
    if (pr->type->is_array && pr->type->array_size) {
        return destructArray(ctx, pr, pr_ret, builder, value_is_ptr);
    }

    Function *fn = getDestructor(ctx, pr->type);
    if (!fn) {
        /* If this is a struct, call Destruct on each of the elements,
         * in the absence of a destructor for the struct as a whole.
         * */
        Type *type = pr->type;
        if (type->struct_name.size()) {
            Struct *st = ctx->getStruct(type->struct_name.c_str(),
                                                 &(type->namespaces));
            std::vector<Type*> *st_types = &(st->member_types);
            int i = 0;
            llvm::Value *array_value = pr->value;

            if (!value_is_ptr) {
                if (builder) {
                    llvm::Value *new_ptr2 = llvm::cast<llvm::Value>(
                            builder->CreateAlloca(
                                ctx->toLLVMType(pr->type, NULL, false))
                            );
                    builder->CreateStore(pr->value, new_ptr2);
                    array_value = new_ptr2;
                } else {
                    llvm::IRBuilder<> builder(pr->block);
                    llvm::Value *new_ptr2 = llvm::cast<llvm::Value>(
                            builder.CreateAlloca(
                                ctx->toLLVMType(pr->type, NULL, false))
                            );
                    builder.CreateStore(pr->value, new_ptr2);
                    array_value = new_ptr2;
                }
            }

            for (std::vector<Type*>::iterator
                    b = st_types->begin(),
                    e = st_types->end();
                    b != e;
                    ++b) {
                ParseResult element;
                ParseResult mnew;
                std::string ts;
                (*b)->toString(&ts);
                element.set(pr->block, *b, array_value);
                std::vector<llvm::Value *> indices;
                STL::push_back2(&indices,
                                ctx->nt->getLLVMZero(),
                                llvm::cast<llvm::Value>(
                                    llvm::ConstantInt::get(
                                        ctx->nt->getNativeIntType(),
                                        i++
                                    )
                                ));
                if (builder) {
                    llvm::Value *res =
                        builder->Insert(
                            llvm::GetElementPtrInst::Create(
                                array_value,
                                llvm::ArrayRef<llvm::Value*>(indices)
                            ),
                            "asdf"
                        );
                    element.value = res;
                    Destruct(ctx, &element, &mnew, builder, true);
                } else {
                    llvm::IRBuilder<> builder(pr->block);
                    llvm::Value *res =
                        builder.Insert(
                            llvm::GetElementPtrInst::Create(
                                array_value,
                                llvm::ArrayRef<llvm::Value*>(indices)
                            ),
                            "asdf"
                        );
                    element.value = res;
                    Destruct(ctx, &element, &mnew, &builder, true);
                }
            }
        }
        return true;
    }
    int destroy_builder = 0;
    if (!builder) {
        destroy_builder = 1;
        builder = new llvm::IRBuilder<>(pr->block);
    }
    std::vector<llvm::Value *> call_args;
    llvm::Value *new_ptr2;
    if (value_is_ptr) {
        new_ptr2 = pr->value;
    } else {
        new_ptr2 = llvm::cast<llvm::Value>(
            builder->CreateAlloca(ctx->toLLVMType(pr->type, NULL, false))
        );
        builder->CreateStore(pr->value, new_ptr2);
    }

    call_args.push_back(new_ptr2);
    builder->CreateCall(
        fn->llvm_function,
        llvm::ArrayRef<llvm::Value*>(call_args));
    if (destroy_builder) {
        delete builder;
    }
    return true;
}
}
}
<|endoftext|>


<commit_before><commit_msg>Reformat (tabs and C++ comments).<commit_after><|endoftext|>


<commit_before>namespace factor
{

/* The compiled code heap is structured into blocks. */
struct code_block
{
	cell header;
	cell owner; /* tagged pointer to word, quotation or f */
	cell parameters; /* tagged pointer to array or f */
	cell relocation; /* tagged pointer to byte-array or f */

	bool free_p() const
	{
		return header & 1 == 1;
	}

	code_block_type type() const
	{
		return (code_block_type)((header >> 1) & 0x3);
	}

	void set_type(code_block_type type)
	{
		header = ((header & ~0x7) | (type << 1));
	}

	bool pic_p() const
	{
		return type() == code_block_pic;
	}

	bool optimized_p() const
	{
		return type() == code_block_optimized;
	}

	cell size() const
	{
		return header & ~7;
	}

	void *xt() const
	{
		return (void *)(this + 1);
	}

	void flush_icache()
	{
		factor::flush_icache((cell)this,size());
	}

	template<typename Iterator> void each_instruction_operand(Iterator &iter)
	{
		if(to_boolean(relocation))
		{
			byte_array *rels = (byte_array *)UNTAG(relocation);

			cell index = 0;
			cell length = (rels->capacity >> TAG_BITS) / sizeof(relocation_entry);

			for(cell i = 0; i < length; i++)
			{
				relocation_entry rel = rels->data<relocation_entry>()[i];
				iter(instruction_operand(rel,this,index));
				index += rel.number_of_parameters();
			}
		}
	}
};

}
<commit_msg>fix compiler warning on linux<commit_after>namespace factor
{

/* The compiled code heap is structured into blocks. */
struct code_block
{
	cell header;
	cell owner; /* tagged pointer to word, quotation or f */
	cell parameters; /* tagged pointer to array or f */
	cell relocation; /* tagged pointer to byte-array or f */

	bool free_p() const
	{
		return (header & 1) == 1;
	}

	code_block_type type() const
	{
		return (code_block_type)((header >> 1) & 0x3);
	}

	void set_type(code_block_type type)
	{
		header = ((header & ~0x7) | (type << 1));
	}

	bool pic_p() const
	{
		return type() == code_block_pic;
	}

	bool optimized_p() const
	{
		return type() == code_block_optimized;
	}

	cell size() const
	{
		return header & ~7;
	}

	void *xt() const
	{
		return (void *)(this + 1);
	}

	void flush_icache()
	{
		factor::flush_icache((cell)this,size());
	}

	template<typename Iterator> void each_instruction_operand(Iterator &iter)
	{
		if(to_boolean(relocation))
		{
			byte_array *rels = (byte_array *)UNTAG(relocation);

			cell index = 0;
			cell length = (rels->capacity >> TAG_BITS) / sizeof(relocation_entry);

			for(cell i = 0; i < length; i++)
			{
				relocation_entry rel = rels->data<relocation_entry>()[i];
				iter(instruction_operand(rel,this,index));
				index += rel.number_of_parameters();
			}
		}
	}
};

}
<|endoftext|>


<commit_before>/*******************************************************************************
Copyright (c) 2012-2016 Alex Zhondin <lexxmark.dev@gmail.com>
Copyright (c) 2015-2019 Alexandra Cherdantseva <neluhus.vagus@gmail.com>

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*******************************************************************************/

#include "PropertyDelegate.h"
#include "Utils/PropertyEditorHandler.h"
#include "PropertyView.h"

QtnPropertyDelegate::QtnPropertyDelegate(QtnPropertyBase &ownerProperty)
	: m_ownerProperty(&ownerProperty)
	, m_stateProperty(nullptr)
{
}

QtnPropertyDelegate::~QtnPropertyDelegate()
{
	if (m_editorHandler)
		m_editorHandler->cleanup();
}

void QtnPropertyDelegate::init()
{
	// do nothing
}

QtnPropertyChangeReason QtnPropertyDelegate::editReason() const
{
	QtnPropertyChangeReason result = QtnPropertyChangeReasonEditValue;
	if (stateProperty()->isMultiValue())
		result |= QtnPropertyChangeReasonEditMultiValue;
	return result;
}

void QtnPropertyDelegate::applySubPropertyInfo(
	const QtnPropertyDelegateInfo &info, const QtnSubPropertyInfo &subInfo)
{
	auto p = subProperty(subInfo.id);
	p->setName(subInfo.key);
	info.storeAttributeValue(subInfo.displayNameAttr, p,
		&QtnPropertyBase::displayName, &QtnPropertyBase::setDisplayName);
	info.storeAttributeValue(subInfo.descriptionAttr, p,
		&QtnPropertyBase::description, &QtnPropertyBase::setDescription);
}

int QtnPropertyDelegate::subPropertyCountImpl() const
{
	return 0;
}

QtnPropertyBase *QtnPropertyDelegate::subPropertyImpl(int index)
{
	Q_UNUSED(index);
	return nullptr;
}

void QtnPropertyDelegate::applyAttributesImpl(
	const QtnPropertyDelegateInfo &info)
{
	Q_UNUSED(info);
}

QStyle::State QtnPropertyDelegate::state(
	bool isActive, const QtnSubItem &subItem) const
{
	auto subState = subItem.state();
	QStyle::State state = QStyle::State_Active;
	if (stateProperty()->isEditableByUser())
		state |= QStyle::State_Enabled;
	if (isActive)
	{
		state |= QStyle::State_Selected;
		state |= QStyle::State_HasFocus;
	}

	if (subState == QtnSubItemStateUnderCursor)
		state |= QStyle::State_MouseOver;
	else if (subState == QtnSubItemStatePushed)
		state |= QStyle::State_Sunken;

	return state;
}

void QtnPropertyDelegate::addSubItemLock(
	QtnDrawContext &context, QList<QtnSubItem> &subItems)
{
	if (!stateProperty()->isUnlockable())
	{
		return;
	}

	QtnSubItem item(context.rect.marginsRemoved(context.margins));
	item.rect.setWidth(item.rect.height());
	context.margins.setLeft(context.margins.left() + item.rect.height() +
		context.widget->valueLeftMargin());

	item.setTextAsTooltip(stateProperty()->isWritable()
			? QtnPropertyView::tr("Lock")
			: QtnPropertyView::tr("Unlock"));

	item.trackState();

	if (item.rect.isValid())
	{
		item.drawHandler = [this](QtnDrawContext &context,
							   const QtnSubItem &item) {
			drawButton(context, item, QIcon(),
				stateProperty()->isWritable()
					? QString::fromUtf8("\xF0\x9F\x94\x93")
					: QString::fromUtf8("\xF0\x9F\x94\x92"));
		};

		item.eventHandler = [this](QtnEventContext &context, const QtnSubItem &,
								QtnPropertyToEdit *) -> bool {
			if ((context.eventType() == QEvent::MouseButtonPress) ||
				(context.eventType() == QEvent::MouseButtonDblClick))
			{
				property()->toggleState(QtnPropertyStateImmutable);
				return true;
			}

			return false;
		};

		subItems.append(item);
	}
}

QColor QtnPropertyDelegate::disabledTextColor(const QStylePainter &painter)
{
	return painter.style()->standardPalette().color(
		QPalette::Disabled, QPalette::Text);
}

void QtnPropertyDelegate::addSubItemBranchNode(
	QtnDrawContext &context, QList<QtnSubItem> &subItems)
{
	if (!context.hasChildren)
		return;

	QtnSubItem brItem(context.rect.marginsRemoved(context.margins));
	brItem.rect.setWidth(brItem.rect.height());
	context.margins.setLeft(context.margins.left() + brItem.rect.height());
	brItem.trackState();

	if (!brItem.rect.isValid())
		return;

	brItem.drawHandler = [this](
							 QtnDrawContext &context, const QtnSubItem &item) {
		auto &painter = *context.painter;
		QRectF branchRect = item.rect;
		qreal side = branchRect.height() / qreal(3.5);
		QColor fillClr = context.palette().color(QPalette::Text);
		QColor outlineClr = (item.state() != QtnSubItemStateNone)
			? Qt::blue
			: context.palette().color(QPalette::Text);

		painter.save();
		painter.setPen(outlineClr);

		QPainterPath branchPath;
		if (stateProperty()->isCollapsed())
		{
			branchPath.moveTo(
				branchRect.left() + side, branchRect.top() + side);
			branchPath.lineTo(branchRect.right() - side - 1,
				branchRect.top() + branchRect.height() / qreal(2.0));
			branchPath.lineTo(
				branchRect.left() + side, branchRect.bottom() - side);
			branchPath.closeSubpath();
		} else
		{
			branchPath.moveTo(
				branchRect.left() + side, branchRect.top() + side);
			branchPath.lineTo(
				branchRect.right() - side, branchRect.top() + side);
			branchPath.lineTo(
				branchRect.left() + branchRect.width() / qreal(2.0),
				branchRect.bottom() - side - 1);
			branchPath.closeSubpath();
		}

		if (painter.testRenderHint(QPainter::Antialiasing))
		{
			painter.fillPath(branchPath, fillClr);
			painter.drawPath(branchPath);
		} else
		{
			painter.setRenderHint(QPainter::Antialiasing, true);
			painter.fillPath(branchPath, fillClr);
			painter.drawPath(branchPath);
			painter.setRenderHint(QPainter::Antialiasing, false);
		}

		painter.restore();
	};

	brItem.eventHandler = [this](QtnEventContext &context, const QtnSubItem &,
							  QtnPropertyToEdit *) -> bool {
		if ((context.eventType() == QEvent::MouseButtonPress) ||
			(context.eventType() == QEvent::MouseButtonDblClick))
		{
			stateProperty()->toggleState(QtnPropertyStateCollapsed);
			return true;
		}

		return false;
	};

	brItem.tooltipHandler = [this](QtnEventContext &,
								const QtnSubItem &) -> QString {
		return (stateProperty()->isCollapsed())
			? QtnPropertyView::tr("Click to expand")
			: QtnPropertyView::tr("Click to collapse");
	};

	subItems.append(brItem);
}
<commit_msg>fix lock/unlock<commit_after>/*******************************************************************************
Copyright (c) 2012-2016 Alex Zhondin <lexxmark.dev@gmail.com>
Copyright (c) 2015-2019 Alexandra Cherdantseva <neluhus.vagus@gmail.com>

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*******************************************************************************/

#include "PropertyDelegate.h"
#include "Utils/PropertyEditorHandler.h"
#include "PropertyView.h"

QtnPropertyDelegate::QtnPropertyDelegate(QtnPropertyBase &ownerProperty)
	: m_ownerProperty(&ownerProperty)
	, m_stateProperty(nullptr)
{
}

QtnPropertyDelegate::~QtnPropertyDelegate()
{
	if (m_editorHandler)
		m_editorHandler->cleanup();
}

void QtnPropertyDelegate::init()
{
	// do nothing
}

QtnPropertyChangeReason QtnPropertyDelegate::editReason() const
{
	QtnPropertyChangeReason result = QtnPropertyChangeReasonEditValue;
	if (stateProperty()->isMultiValue())
		result |= QtnPropertyChangeReasonEditMultiValue;
	return result;
}

void QtnPropertyDelegate::applySubPropertyInfo(
	const QtnPropertyDelegateInfo &info, const QtnSubPropertyInfo &subInfo)
{
	auto p = subProperty(subInfo.id);
	p->setName(subInfo.key);
	info.storeAttributeValue(subInfo.displayNameAttr, p,
		&QtnPropertyBase::displayName, &QtnPropertyBase::setDisplayName);
	info.storeAttributeValue(subInfo.descriptionAttr, p,
		&QtnPropertyBase::description, &QtnPropertyBase::setDescription);
}

int QtnPropertyDelegate::subPropertyCountImpl() const
{
	return 0;
}

QtnPropertyBase *QtnPropertyDelegate::subPropertyImpl(int index)
{
	Q_UNUSED(index);
	return nullptr;
}

void QtnPropertyDelegate::applyAttributesImpl(
	const QtnPropertyDelegateInfo &info)
{
	Q_UNUSED(info);
}

QStyle::State QtnPropertyDelegate::state(
	bool isActive, const QtnSubItem &subItem) const
{
	auto subState = subItem.state();
	QStyle::State state = QStyle::State_Active;
	if (stateProperty()->isEditableByUser())
		state |= QStyle::State_Enabled;
	if (isActive)
	{
		state |= QStyle::State_Selected;
		state |= QStyle::State_HasFocus;
	}

	if (subState == QtnSubItemStateUnderCursor)
		state |= QStyle::State_MouseOver;
	else if (subState == QtnSubItemStatePushed)
		state |= QStyle::State_Sunken;

	return state;
}

void QtnPropertyDelegate::addSubItemLock(
	QtnDrawContext &context, QList<QtnSubItem> &subItems)
{
	if (!stateProperty()->isUnlockable())
	{
		return;
	}

	QtnSubItem item(context.rect.marginsRemoved(context.margins));
	item.rect.setWidth(item.rect.height());
	context.margins.setLeft(context.margins.left() + item.rect.height() +
		context.widget->valueLeftMargin());

	item.setTextAsTooltip(stateProperty()->isWritable()
			? QtnPropertyView::tr("Lock")
			: QtnPropertyView::tr("Unlock"));

	item.trackState();

	if (item.rect.isValid())
	{
		item.drawHandler = [this](QtnDrawContext &context,
							   const QtnSubItem &item) {
			drawButton(context, item, QIcon(),
				stateProperty()->isWritable()
					? QString::fromUtf8("\xF0\x9F\x94\x93")
					: QString::fromUtf8("\xF0\x9F\x94\x92"));
		};

		item.eventHandler = [this](QtnEventContext &context, const QtnSubItem &,
								QtnPropertyToEdit *) -> bool {
			if ((context.eventType() == QEvent::MouseButtonPress) ||
				(context.eventType() == QEvent::MouseButtonDblClick))
			{
				stateProperty()->toggleState(QtnPropertyStateImmutable);
				property()->switchState(
					QtnPropertyStateImmutable, !stateProperty()->isWritable());
				return true;
			}

			return false;
		};

		subItems.append(item);
	}
}

QColor QtnPropertyDelegate::disabledTextColor(const QStylePainter &painter)
{
	return painter.style()->standardPalette().color(
		QPalette::Disabled, QPalette::Text);
}

void QtnPropertyDelegate::addSubItemBranchNode(
	QtnDrawContext &context, QList<QtnSubItem> &subItems)
{
	if (!context.hasChildren)
		return;

	QtnSubItem brItem(context.rect.marginsRemoved(context.margins));
	brItem.rect.setWidth(brItem.rect.height());
	context.margins.setLeft(context.margins.left() + brItem.rect.height());
	brItem.trackState();

	if (!brItem.rect.isValid())
		return;

	brItem.drawHandler = [this](
							 QtnDrawContext &context, const QtnSubItem &item) {
		auto &painter = *context.painter;
		QRectF branchRect = item.rect;
		qreal side = branchRect.height() / qreal(3.5);
		QColor fillClr = context.palette().color(QPalette::Text);
		QColor outlineClr = (item.state() != QtnSubItemStateNone)
			? Qt::blue
			: context.palette().color(QPalette::Text);

		painter.save();
		painter.setPen(outlineClr);

		QPainterPath branchPath;
		if (stateProperty()->isCollapsed())
		{
			branchPath.moveTo(
				branchRect.left() + side, branchRect.top() + side);
			branchPath.lineTo(branchRect.right() - side - 1,
				branchRect.top() + branchRect.height() / qreal(2.0));
			branchPath.lineTo(
				branchRect.left() + side, branchRect.bottom() - side);
			branchPath.closeSubpath();
		} else
		{
			branchPath.moveTo(
				branchRect.left() + side, branchRect.top() + side);
			branchPath.lineTo(
				branchRect.right() - side, branchRect.top() + side);
			branchPath.lineTo(
				branchRect.left() + branchRect.width() / qreal(2.0),
				branchRect.bottom() - side - 1);
			branchPath.closeSubpath();
		}

		if (painter.testRenderHint(QPainter::Antialiasing))
		{
			painter.fillPath(branchPath, fillClr);
			painter.drawPath(branchPath);
		} else
		{
			painter.setRenderHint(QPainter::Antialiasing, true);
			painter.fillPath(branchPath, fillClr);
			painter.drawPath(branchPath);
			painter.setRenderHint(QPainter::Antialiasing, false);
		}

		painter.restore();
	};

	brItem.eventHandler = [this](QtnEventContext &context, const QtnSubItem &,
							  QtnPropertyToEdit *) -> bool {
		if ((context.eventType() == QEvent::MouseButtonPress) ||
			(context.eventType() == QEvent::MouseButtonDblClick))
		{
			stateProperty()->toggleState(QtnPropertyStateCollapsed);
			return true;
		}

		return false;
	};

	brItem.tooltipHandler = [this](QtnEventContext &,
								const QtnSubItem &) -> QString {
		return (stateProperty()->isCollapsed())
			? QtnPropertyView::tr("Click to expand")
			: QtnPropertyView::tr("Click to collapse");
	};

	subItems.append(brItem);
}
<|endoftext|>