<commit_before>
//
// This source file is part of appleseed.
// Visit http://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2016-2017 Esteban Tovagliari, The appleseedhq Organization
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

// Interface header.
#include "shadingnode.h"

// appleseed-maya headers.
#include "appleseedmaya/attributeutils.h"
#include "appleseedmaya/logger.h"
#include "appleseedmaya/shadingnodemetadata.h"
#include "appleseedmaya/shadingnoderegistry.h"

// appleseed.foundation headers.
#include "foundation/math/vector.h"
#include "foundation/utility/string.h"

// Maya headers.
#include "appleseedmaya/_beginmayaheaders.h"
#include <maya/MFnDependencyNode.h>
#include <maya/MFnEnumAttribute.h>
#include <maya/MFnMatrixAttribute.h>
#include <maya/MFnNumericAttribute.h>
#include <maya/MFnStringData.h>
#include <maya/MFnTypedAttribute.h>
#include "appleseedmaya/_endmayaheaders.h"

namespace asf = foundation;

namespace
{
    MObject createPointAttribute(
        MFnNumericAttribute&    numAttrFn,
        const OSLParamInfo&     p,
        MStatus&                status)
    {
        MObject attr = numAttrFn.createPoint(
            p.mayaAttributeName,
            p.mayaAttributeShortName,
            &status);

        if (p.hasDefault)
        {
            numAttrFn.setDefault(
                static_cast<float>(p.defaultValue[0]),
                static_cast<float>(p.defaultValue[1]),
                static_cast<float>(p.defaultValue[2]));
        }

        return attr;
    }

    template <typename T>
    MObject createNumericAttribute(
        MFnNumericAttribute&    numAttrFn,
        const OSLParamInfo&     p,
        MFnNumericData::Type    type,
        MStatus&                status)
    {
        MObject attr = numAttrFn.create(
            p.mayaAttributeName,
            p.mayaAttributeShortName,
            type,
            T(0),
            &status);

        if (p.hasDefault)
            numAttrFn.setDefault(static_cast<T>(p.defaultValue[0]));

        return attr;
    }

    template <>
    MObject createNumericAttribute<bool>(
        MFnNumericAttribute&    numAttrFn,
        const OSLParamInfo&     p,
        MFnNumericData::Type    type,
        MStatus&                status)
    {
        MObject attr = numAttrFn.create(
            p.mayaAttributeName,
            p.mayaAttributeShortName,
            type,
            0.0,
            &status);

        if (p.hasDefault)
            numAttrFn.setDefault(p.defaultValue[0] == 0.0 ? false : true);

        return attr;
    }

    MStatus initializeAttribute(MFnAttribute& attr, const OSLParamInfo& p)
    {
        if (p.label.length() != 0)
            attr.setNiceNameOverride(p.label);

        if (p.mayaAttributeConnectable == false)
            attr.setConnectable(false);

        if (p.mayaAttributeHidden == true)
            attr.setHidden(true);

        if (p.mayaAttributeKeyable == false)
            attr.setKeyable(false);

        if (p.isOutput)
            return AttributeUtils::makeOutput(attr);
        else
            return AttributeUtils::makeInput(attr);
    }

    MStatus initializeNumericAttribute(MFnNumericAttribute& attr, const OSLParamInfo& p)
    {
        if (p.hasMin)
            attr.setMin(p.minValue);

        if (p.hasMax)
            attr.setMax(p.maxValue);

        if (p.hasSoftMin)
            attr.setSoftMin(p.softMinValue);

        if (p.hasSoftMax)
            attr.setSoftMax(p.softMaxValue);

        return initializeAttribute(attr, p);
    }

    // Stores the current shader info of the shader being registered.
    const OSLShaderInfo* g_currentShaderInfo = nullptr;
}

void ShadingNode::setCurrentShaderInfo(const OSLShaderInfo* shaderInfo)
{
    g_currentShaderInfo = shaderInfo;
}

void* ShadingNode::creator()
{
    return new ShadingNode();
}

MStatus ShadingNode::initialize()
{
    #define CHECK_STATUS_AND_HANDLE_ERROR           \
        if (!status)                                \
        {                                           \
            report_error(*shaderInfo, p, status);   \
            return status;                          \
        }

    assert(g_currentShaderInfo);
    const OSLShaderInfo* shaderInfo = g_currentShaderInfo;
    g_currentShaderInfo = nullptr;

    // todo: lots of refactoring possibilities here...
    for (size_t i = 0, e = shaderInfo->paramInfo.size(); i < e; ++i)
    {
        const OSLParamInfo& p = shaderInfo->paramInfo[i];

        MStatus status;
        MObject attr;

        if (p.paramType == "color")
        {
            MFnNumericAttribute numAttrFn;
            attr = numAttrFn.createColor(
                p.mayaAttributeName,
                p.mayaAttributeShortName,
                &status);
            CHECK_STATUS_AND_HANDLE_ERROR

            numAttrFn.setUsedAsColor(true);

            if (p.hasDefault)
            {
                numAttrFn.setDefault(
                    static_cast<float>(p.defaultValue[0]),
                    static_cast<float>(p.defaultValue[1]),
                    static_cast<float>(p.defaultValue[2]));
            }

            status = initializeAttribute(numAttrFn, p);
            CHECK_STATUS_AND_HANDLE_ERROR
        }
        else if (p.paramType == "float")
        {
            MFnNumericAttribute numAttrFn;
            attr = createNumericAttribute<float>(numAttrFn, p, MFnNumericData::kFloat, status);
            CHECK_STATUS_AND_HANDLE_ERROR

            status = initializeNumericAttribute(numAttrFn, p);
            CHECK_STATUS_AND_HANDLE_ERROR
        }
        else if (p.paramType == "int")
        {
            // Check to see if we need to create an int, bool or enum
            if (p.widget == "mapper")
            {
                if (p.options.length() != 0)
                {
                    std::vector<std::string> fields;
                    asf::tokenize(p.options.asChar(), "|", fields);

                    int defaultValue = 0;
                    if (p.hasDefault)
                        defaultValue = static_cast<int>(p.defaultValue[0]);

                    MFnEnumAttribute enumAttrFn;
                    attr = enumAttrFn.create(
                        p.mayaAttributeName,
                        p.mayaAttributeShortName,
                        defaultValue,
                        &status);
                    CHECK_STATUS_AND_HANDLE_ERROR

                    std::vector<std::string> subfields;
                    for (size_t i = 0, e = fields.size(); i < e; ++i)
                    {
                        subfields.clear();
                        asf::tokenize(fields[i].c_str(), ":", subfields);
                        status = enumAttrFn.addField(
                            subfields[0].c_str(),
                            asf::from_string<int>(subfields[1]));
                        CHECK_STATUS_AND_HANDLE_ERROR
                    }

                    status = initializeAttribute(enumAttrFn, p);
                    CHECK_STATUS_AND_HANDLE_ERROR
                }
                else
                {
                    // todo: what here...?
                }
            }
            else if (p.widget == "checkBox")
            {
                MFnNumericAttribute numAttrFn;
                attr = createNumericAttribute<bool>(numAttrFn, p, MFnNumericData::kBoolean, status);
                CHECK_STATUS_AND_HANDLE_ERROR

                status = initializeAttribute(numAttrFn, p);
                CHECK_STATUS_AND_HANDLE_ERROR
            }
            else // normal int attribute.
            {
                MFnNumericAttribute numAttrFn;
                attr = createNumericAttribute<int>(numAttrFn, p, MFnNumericData::kInt, status);
                CHECK_STATUS_AND_HANDLE_ERROR

                status = initializeNumericAttribute(numAttrFn, p);
                CHECK_STATUS_AND_HANDLE_ERROR
            }
        }
        else if (p.paramType == "matrix")
        {
            MFnMatrixAttribute matrixAttrFn;
            attr = matrixAttrFn.create(
                p.mayaAttributeName,
                p.mayaAttributeShortName,
                MFnMatrixAttribute::kFloat,
                &status);
            CHECK_STATUS_AND_HANDLE_ERROR

            initializeAttribute(matrixAttrFn, p);
            CHECK_STATUS_AND_HANDLE_ERROR
        }
        else if (p.paramType == "normal")
        {
            MFnNumericAttribute numAttrFn;
            attr = createPointAttribute(numAttrFn, p, status);
            CHECK_STATUS_AND_HANDLE_ERROR

            status = initializeAttribute(numAttrFn, p);
            CHECK_STATUS_AND_HANDLE_ERROR
        }
        else if (p.paramType == "point")
        {
            MFnNumericAttribute numAttrFn;
            attr = createPointAttribute(numAttrFn, p, status);
            CHECK_STATUS_AND_HANDLE_ERROR

            status = initializeAttribute(numAttrFn, p);
            CHECK_STATUS_AND_HANDLE_ERROR
        }
        else if (p.paramType == "pointer") // closure color
        {
            MFnNumericAttribute numAttrFn;
            attr = numAttrFn.createColor(
                p.mayaAttributeName,
                p.mayaAttributeShortName,
                &status);
            CHECK_STATUS_AND_HANDLE_ERROR

            status = initializeAttribute(numAttrFn, p);
            CHECK_STATUS_AND_HANDLE_ERROR
        }
        else if (p.paramType == "string")
        {
            // Check to see if we need to create a string or an enum
            if (p.widget == "popup")
            {
                if (p.options.length() != 0)
                {
                    std::vector<std::string> fields;
                    asf::tokenize(p.options.asChar(), "|", fields);

                    size_t defaultValue = 0;
                    if (p.hasDefault)
                    {
                        for (size_t i = 0, e = fields.size(); i < e; ++i)
                        {
                            if (p.defaultStringValue == fields[i].c_str())
                            {
                                defaultValue = i;
                                break;
                            }
                        }
                    }

                    MFnEnumAttribute enumAttrFn;
                    attr = enumAttrFn.create(
                        p.mayaAttributeName,
                        p.mayaAttributeShortName,
                        static_cast<short>(defaultValue),
                        &status);
                    CHECK_STATUS_AND_HANDLE_ERROR

                    for (size_t i = 0, e = fields.size(); i < e; ++i)
                        enumAttrFn.addField(fields[i].c_str(), static_cast<short>(i));

                    status = initializeAttribute(enumAttrFn, p);
                    CHECK_STATUS_AND_HANDLE_ERROR
                }
                else
                {
                    // todo: what here...?
                }
            }
            else
            {
                MFnTypedAttribute typedAttrFn;
                attr = typedAttrFn.create(
                    p.mayaAttributeName,
                    p.mayaAttributeShortName,
                    MFnData::kString,
                    &status);
                CHECK_STATUS_AND_HANDLE_ERROR

                if (p.widget == "filename")
                    typedAttrFn.setUsedAsFilename(true);

                if (p.hasDefault)
                {
                    MFnStringData stringDataFn;
                    MObject defaultData = stringDataFn.create(p.defaultStringValue);
                    typedAttrFn.setDefault(defaultData);
                }

                status = initializeAttribute(typedAttrFn, p);
                CHECK_STATUS_AND_HANDLE_ERROR
            }
        }
        else if (p.paramType == "vector")
        {
            MFnNumericAttribute numAttrFn;
            attr = createPointAttribute(numAttrFn, p, status);
            CHECK_STATUS_AND_HANDLE_ERROR

            status = initializeAttribute(numAttrFn, p);
            CHECK_STATUS_AND_HANDLE_ERROR
        }
        else if (p.paramType == "float[2]")
        {
            MFnNumericAttribute numAttrFn;

            if (p.mayaAttributeName == "uvCoord")
            {
                MObject child1 = numAttrFn.create("uCoord", "u", MFnNumericData::kFloat);
                MObject child2 = numAttrFn.create("vCoord", "v", MFnNumericData::kFloat);
                attr = numAttrFn.create("uvCoord", "uv", child1, child2);
            }
            else if (p.mayaAttributeName == "uvFilterSize")
            {
                MObject child1 = numAttrFn.create("uvFilterSizeX", "fsx", MFnNumericData::kFloat);
                MObject child2 = numAttrFn.create("uvFilterSizeY", "fsy", MFnNumericData::kFloat);
                attr = numAttrFn.create("uvFilterSize", "fs", child1, child2);
            }
            else
            {
                MObject child1 = numAttrFn.create(p.mayaAttributeName + "X", p.mayaAttributeShortName + "x", MFnNumericData::kFloat);
                MObject child2 = numAttrFn.create(p.mayaAttributeName + "Y", p.mayaAttributeShortName + "y", MFnNumericData::kFloat);

                attr = numAttrFn.create(p.mayaAttributeName, p.mayaAttributeShortName, child1, child2);
            }
            if (!attr.isNull())
            {
                status = AttributeUtils::makeInput(numAttrFn);
                CHECK_STATUS_AND_HANDLE_ERROR
            }
        }
        else
        {
            RENDERER_LOG_WARNING(
                "Ignoring param %s of shader %s",
                p.paramName.asChar(),
                shaderInfo->shaderName.asChar());
            continue;
        }

        if (!attr.isNull())
        {
            status = addAttribute(attr);
            CHECK_STATUS_AND_HANDLE_ERROR
        }
    }

    #undef CHECK_STATUS_AND_HANDLE_ERROR

    // TODO: set dependencies between in and out attributes.
    // it's needed for swatches to update correctly.

    return MS::kSuccess;
}

ShadingNode::ShadingNode()
{
}

void ShadingNode::postConstructor()
{
    MPxNode::postConstructor();
    setMPSafe(true);
}

void ShadingNode::report_error(
    const OSLShaderInfo&    shaderInfo,
    const OSLParamInfo&     paramInfo,
    MStatus&                status)
{
    RENDERER_LOG_WARNING(
        "Error while initializing node %s, param %s, error = %s",
        shaderInfo.shaderName.asChar(),
        paramInfo.paramName.asChar(),
        status.errorString().asChar());
}
<commit_msg>fixed swatch rendering update bugs<commit_after>
//
// This source file is part of appleseed.
// Visit http://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2016-2017 Esteban Tovagliari, The appleseedhq Organization
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

// Interface header.
#include "shadingnode.h"

// appleseed-maya headers.
#include "appleseedmaya/attributeutils.h"
#include "appleseedmaya/logger.h"
#include "appleseedmaya/shadingnodemetadata.h"
#include "appleseedmaya/shadingnoderegistry.h"

// appleseed.foundation headers.
#include "foundation/math/vector.h"
#include "foundation/utility/string.h"

// Maya headers.
#include "appleseedmaya/_beginmayaheaders.h"
#include <maya/MFnDependencyNode.h>
#include <maya/MFnEnumAttribute.h>
#include <maya/MFnMatrixAttribute.h>
#include <maya/MFnNumericAttribute.h>
#include <maya/MFnStringData.h>
#include <maya/MFnTypedAttribute.h>
#include <maya/MObjectArray.h>
#include "appleseedmaya/_endmayaheaders.h"

namespace asf = foundation;

namespace
{
    MObject createPointAttribute(
        MFnNumericAttribute&    numAttrFn,
        const OSLParamInfo&     p,
        MStatus&                status)
    {
        MObject attr = numAttrFn.createPoint(
            p.mayaAttributeName,
            p.mayaAttributeShortName,
            &status);

        if (p.hasDefault)
        {
            numAttrFn.setDefault(
                static_cast<float>(p.defaultValue[0]),
                static_cast<float>(p.defaultValue[1]),
                static_cast<float>(p.defaultValue[2]));
        }

        return attr;
    }

    template <typename T>
    MObject createNumericAttribute(
        MFnNumericAttribute&    numAttrFn,
        const OSLParamInfo&     p,
        MFnNumericData::Type    type,
        MStatus&                status)
    {
        MObject attr = numAttrFn.create(
            p.mayaAttributeName,
            p.mayaAttributeShortName,
            type,
            T(0),
            &status);

        if (p.hasDefault)
            numAttrFn.setDefault(static_cast<T>(p.defaultValue[0]));

        return attr;
    }

    template <>
    MObject createNumericAttribute<bool>(
        MFnNumericAttribute&    numAttrFn,
        const OSLParamInfo&     p,
        MFnNumericData::Type    type,
        MStatus&                status)
    {
        MObject attr = numAttrFn.create(
            p.mayaAttributeName,
            p.mayaAttributeShortName,
            type,
            0.0,
            &status);

        if (p.hasDefault)
            numAttrFn.setDefault(p.defaultValue[0] == 0.0 ? false : true);

        return attr;
    }

    MStatus initializeAttribute(MFnAttribute& attr, const OSLParamInfo& p)
    {
        if (p.label.length() != 0)
            attr.setNiceNameOverride(p.label);

        if (p.mayaAttributeConnectable == false)
            attr.setConnectable(false);

        if (p.mayaAttributeHidden == true)
            attr.setHidden(true);

        if (p.mayaAttributeKeyable == false)
            attr.setKeyable(false);

        if (p.isOutput)
            return AttributeUtils::makeOutput(attr);
        else
            return AttributeUtils::makeInput(attr);
    }

    MStatus initializeNumericAttribute(MFnNumericAttribute& attr, const OSLParamInfo& p)
    {
        if (p.hasMin)
            attr.setMin(p.minValue);

        if (p.hasMax)
            attr.setMax(p.maxValue);

        if (p.hasSoftMin)
            attr.setSoftMin(p.softMinValue);

        if (p.hasSoftMax)
            attr.setSoftMax(p.softMaxValue);

        return initializeAttribute(attr, p);
    }

    // Stores the current shader info of the shader being registered.
    const OSLShaderInfo* g_currentShaderInfo = nullptr;
}

void ShadingNode::setCurrentShaderInfo(const OSLShaderInfo* shaderInfo)
{
    g_currentShaderInfo = shaderInfo;
}

void* ShadingNode::creator()
{
    return new ShadingNode();
}

MStatus ShadingNode::initialize()
{
    #define CHECK_STATUS_AND_HANDLE_ERROR           \
        if (!status)                                \
        {                                           \
            report_error(*shaderInfo, p, status);   \
            return status;                          \
        }

    assert(g_currentShaderInfo);
    const OSLShaderInfo* shaderInfo = g_currentShaderInfo;
    g_currentShaderInfo = nullptr;

    MObjectArray inputAttributes;
    MObjectArray outputAttributes;

    // todo: lots of refactoring possibilities here...
    for (size_t i = 0, e = shaderInfo->paramInfo.size(); i < e; ++i)
    {
        const OSLParamInfo& p = shaderInfo->paramInfo[i];

        MStatus status;
        MObject attr;

        if (p.paramType == "color")
        {
            MFnNumericAttribute numAttrFn;
            attr = numAttrFn.createColor(
                p.mayaAttributeName,
                p.mayaAttributeShortName,
                &status);
            CHECK_STATUS_AND_HANDLE_ERROR

            numAttrFn.setUsedAsColor(true);

            if (p.hasDefault)
            {
                numAttrFn.setDefault(
                    static_cast<float>(p.defaultValue[0]),
                    static_cast<float>(p.defaultValue[1]),
                    static_cast<float>(p.defaultValue[2]));
            }

            status = initializeAttribute(numAttrFn, p);
            CHECK_STATUS_AND_HANDLE_ERROR
        }
        else if (p.paramType == "float")
        {
            MFnNumericAttribute numAttrFn;
            attr = createNumericAttribute<float>(numAttrFn, p, MFnNumericData::kFloat, status);
            CHECK_STATUS_AND_HANDLE_ERROR

            status = initializeNumericAttribute(numAttrFn, p);
            CHECK_STATUS_AND_HANDLE_ERROR
        }
        else if (p.paramType == "int")
        {
            // Check to see if we need to create an int, bool or enum
            if (p.widget == "mapper")
            {
                if (p.options.length() != 0)
                {
                    std::vector<std::string> fields;
                    asf::tokenize(p.options.asChar(), "|", fields);

                    int defaultValue = 0;
                    if (p.hasDefault)
                        defaultValue = static_cast<int>(p.defaultValue[0]);

                    MFnEnumAttribute enumAttrFn;
                    attr = enumAttrFn.create(
                        p.mayaAttributeName,
                        p.mayaAttributeShortName,
                        defaultValue,
                        &status);
                    CHECK_STATUS_AND_HANDLE_ERROR

                    std::vector<std::string> subfields;
                    for (size_t i = 0, e = fields.size(); i < e; ++i)
                    {
                        subfields.clear();
                        asf::tokenize(fields[i].c_str(), ":", subfields);
                        status = enumAttrFn.addField(
                            subfields[0].c_str(),
                            asf::from_string<int>(subfields[1]));
                        CHECK_STATUS_AND_HANDLE_ERROR
                    }

                    status = initializeAttribute(enumAttrFn, p);
                    CHECK_STATUS_AND_HANDLE_ERROR
                }
                else
                {
                    // todo: what here...?
                }
            }
            else if (p.widget == "checkBox")
            {
                MFnNumericAttribute numAttrFn;
                attr = createNumericAttribute<bool>(numAttrFn, p, MFnNumericData::kBoolean, status);
                CHECK_STATUS_AND_HANDLE_ERROR

                status = initializeAttribute(numAttrFn, p);
                CHECK_STATUS_AND_HANDLE_ERROR
            }
            else // normal int attribute.
            {
                MFnNumericAttribute numAttrFn;
                attr = createNumericAttribute<int>(numAttrFn, p, MFnNumericData::kInt, status);
                CHECK_STATUS_AND_HANDLE_ERROR

                status = initializeNumericAttribute(numAttrFn, p);
                CHECK_STATUS_AND_HANDLE_ERROR
            }
        }
        else if (p.paramType == "matrix")
        {
            MFnMatrixAttribute matrixAttrFn;
            attr = matrixAttrFn.create(
                p.mayaAttributeName,
                p.mayaAttributeShortName,
                MFnMatrixAttribute::kFloat,
                &status);
            CHECK_STATUS_AND_HANDLE_ERROR

            initializeAttribute(matrixAttrFn, p);
            CHECK_STATUS_AND_HANDLE_ERROR
        }
        else if (p.paramType == "normal")
        {
            MFnNumericAttribute numAttrFn;
            attr = createPointAttribute(numAttrFn, p, status);
            CHECK_STATUS_AND_HANDLE_ERROR

            status = initializeAttribute(numAttrFn, p);
            CHECK_STATUS_AND_HANDLE_ERROR
        }
        else if (p.paramType == "point")
        {
            MFnNumericAttribute numAttrFn;
            attr = createPointAttribute(numAttrFn, p, status);
            CHECK_STATUS_AND_HANDLE_ERROR

            status = initializeAttribute(numAttrFn, p);
            CHECK_STATUS_AND_HANDLE_ERROR
        }
        else if (p.paramType == "pointer") // closure color
        {
            MFnNumericAttribute numAttrFn;
            attr = numAttrFn.createColor(
                p.mayaAttributeName,
                p.mayaAttributeShortName,
                &status);
            CHECK_STATUS_AND_HANDLE_ERROR

            status = initializeAttribute(numAttrFn, p);
            CHECK_STATUS_AND_HANDLE_ERROR
        }
        else if (p.paramType == "string")
        {
            // Check to see if we need to create a string or an enum
            if (p.widget == "popup")
            {
                if (p.options.length() != 0)
                {
                    std::vector<std::string> fields;
                    asf::tokenize(p.options.asChar(), "|", fields);

                    size_t defaultValue = 0;
                    if (p.hasDefault)
                    {
                        for (size_t i = 0, e = fields.size(); i < e; ++i)
                        {
                            if (p.defaultStringValue == fields[i].c_str())
                            {
                                defaultValue = i;
                                break;
                            }
                        }
                    }

                    MFnEnumAttribute enumAttrFn;
                    attr = enumAttrFn.create(
                        p.mayaAttributeName,
                        p.mayaAttributeShortName,
                        static_cast<short>(defaultValue),
                        &status);
                    CHECK_STATUS_AND_HANDLE_ERROR

                    for (size_t i = 0, e = fields.size(); i < e; ++i)
                        enumAttrFn.addField(fields[i].c_str(), static_cast<short>(i));

                    status = initializeAttribute(enumAttrFn, p);
                    CHECK_STATUS_AND_HANDLE_ERROR
                }
                else
                {
                    // todo: what here...?
                }
            }
            else
            {
                MFnTypedAttribute typedAttrFn;
                attr = typedAttrFn.create(
                    p.mayaAttributeName,
                    p.mayaAttributeShortName,
                    MFnData::kString,
                    &status);
                CHECK_STATUS_AND_HANDLE_ERROR

                if (p.widget == "filename")
                    typedAttrFn.setUsedAsFilename(true);

                if (p.hasDefault)
                {
                    MFnStringData stringDataFn;
                    MObject defaultData = stringDataFn.create(p.defaultStringValue);
                    typedAttrFn.setDefault(defaultData);
                }

                status = initializeAttribute(typedAttrFn, p);
                CHECK_STATUS_AND_HANDLE_ERROR
            }
        }
        else if (p.paramType == "vector")
        {
            MFnNumericAttribute numAttrFn;
            attr = createPointAttribute(numAttrFn, p, status);
            CHECK_STATUS_AND_HANDLE_ERROR

            status = initializeAttribute(numAttrFn, p);
            CHECK_STATUS_AND_HANDLE_ERROR
        }
        else if (p.paramType == "float[2]")
        {
            MFnNumericAttribute numAttrFn;

            if (p.mayaAttributeName == "uvCoord")
            {
                MObject child1 = numAttrFn.create("uCoord", "u", MFnNumericData::kFloat);
                MObject child2 = numAttrFn.create("vCoord", "v", MFnNumericData::kFloat);
                attr = numAttrFn.create("uvCoord", "uv", child1, child2);
            }
            else if (p.mayaAttributeName == "uvFilterSize")
            {
                MObject child1 = numAttrFn.create("uvFilterSizeX", "fsx", MFnNumericData::kFloat);
                MObject child2 = numAttrFn.create("uvFilterSizeY", "fsy", MFnNumericData::kFloat);
                attr = numAttrFn.create("uvFilterSize", "fs", child1, child2);
            }
            else
            {
                MObject child1 = numAttrFn.create(p.mayaAttributeName + "X", p.mayaAttributeShortName + "x", MFnNumericData::kFloat);
                MObject child2 = numAttrFn.create(p.mayaAttributeName + "Y", p.mayaAttributeShortName + "y", MFnNumericData::kFloat);

                attr = numAttrFn.create(p.mayaAttributeName, p.mayaAttributeShortName, child1, child2);
            }
            if (!attr.isNull())
            {
                status = AttributeUtils::makeInput(numAttrFn);
                CHECK_STATUS_AND_HANDLE_ERROR
            }
        }
        else
        {
            RENDERER_LOG_WARNING(
                "Ignoring param %s of shader %s",
                p.paramName.asChar(),
                shaderInfo->shaderName.asChar());
            continue;
        }

        if (!attr.isNull())
        {
            if (p.isOutput)
                outputAttributes.append(attr);
            else
                inputAttributes.append(attr);

            status = addAttribute(attr);
            CHECK_STATUS_AND_HANDLE_ERROR
        }
    }

    #undef CHECK_STATUS_AND_HANDLE_ERROR

    // Set dependencies between input and output attributes.
    // This is needed for swatches to update correctly.
    for (unsigned int i = 0 , ie = inputAttributes.length() ; i < ie ; ++i)
    {
        for (unsigned int j = 0 , je = outputAttributes.length() ; j < je ; ++j)
            attributeAffects(inputAttributes[i], outputAttributes[j]);
    }

    return MS::kSuccess;
}

ShadingNode::ShadingNode()
{
}

void ShadingNode::postConstructor()
{
    MPxNode::postConstructor();
    setMPSafe(true);
}

void ShadingNode::report_error(
    const OSLShaderInfo&    shaderInfo,
    const OSLParamInfo&     paramInfo,
    MStatus&                status)
{
    RENDERER_LOG_WARNING(
        "Error while initializing node %s, param %s, error = %s",
        shaderInfo.shaderName.asChar(),
        paramInfo.paramName.asChar(),
        status.errorString().asChar());
}
<|endoftext|>


<commit_before>
#include "catch.hpp"

#include <mapnik/projection.hpp>
#include <mapnik/proj_transform.hpp>
#include <mapnik/box2d.hpp>

#ifdef MAPNIK_USE_PROJ4
// proj4
#include <proj_api.h>
#endif

TEST_CASE("projection transform")
{

SECTION("Test bounding box transforms - 4326 to 3857")
{
    mapnik::projection proj_4326("+init=epsg:4326");
    mapnik::projection proj_3857("+init=epsg:3857");
    mapnik::proj_transform prj_trans(proj_4326, proj_3857);

    double minx = -45.0;
    double miny = 55.0;
    double maxx = -40.0;
    double maxy = 75.0;

    mapnik::box2d<double> bbox(minx, miny, maxx, maxy);

    prj_trans.forward(bbox);
    INFO(bbox.to_string());
    CHECK(bbox.minx() == Approx(-5009377.085697311));
    CHECK(bbox.miny() == Approx(7361866.1130511891));
    CHECK(bbox.maxx() == Approx(-4452779.631730943));
    CHECK(bbox.maxy() == Approx(12932243.1119920239));

    prj_trans.backward(bbox);
    CHECK(bbox.minx() == Approx(minx));
    CHECK(bbox.miny() == Approx(miny));
    CHECK(bbox.maxx() == Approx(maxx));
    CHECK(bbox.maxy() == Approx(maxy));

}

#if defined(MAPNIK_USE_PROJ4) && PJ_VERSION >= 480
SECTION("test pj_transform failure behavior")
{
    mapnik::projection proj_4269("+init=epsg:4269");
    mapnik::projection proj_3857("+init=epsg:3857");
    mapnik::proj_transform prj_trans(proj_4269, proj_3857);
    mapnik::proj_transform prj_trans2(proj_3857, proj_4269);

    auto proj_ctx0 = pj_ctx_alloc();
    REQUIRE( proj_ctx0 != nullptr );
    auto proj0 = pj_init_plus_ctx(proj_ctx0, proj_4269.params().c_str());
    REQUIRE( proj0 != nullptr );

    auto proj_ctx1 = pj_ctx_alloc();
    REQUIRE( proj_ctx1 != nullptr );
    auto proj1 = pj_init_plus_ctx(proj_ctx1, proj_3857.params().c_str());
    REQUIRE( proj1 != nullptr );

    // first test valid values directly against proj
    double x = -180.0;
    double y = -60.0;
    x *= DEG_TO_RAD;
    y *= DEG_TO_RAD;
    CHECK( x == Approx(-3.1415926536) );
    CHECK( y == Approx(-1.0471975512) );
    CHECK( 0 == pj_transform(proj0, proj1, 1, 0, &x, &y, nullptr) );
    CHECK( x == Approx(-20037508.3427892439) );
    CHECK( y == Approx(-8399737.8896366451) );

    // now test mapnik class
    double x0 = -180.0;
    double y0 = -60.0;
    CHECK( prj_trans.forward(&x0,&y0,nullptr,1,1) );
    CHECK( x0 == Approx(-20037508.3427892439) );
    CHECK( y0 == Approx(-8399737.8896366451) );
    double x1 = -180.0;
    double y1 = -60.0;
    CHECK( prj_trans2.backward(&x1,&y1,nullptr,1,1) );
    CHECK( x1 == Approx(-20037508.3427892439) );
    CHECK( y1 == Approx(-8399737.8896366451) );

    // longitude value outside the value range for mercator
    x = -181.0;
    y = -91.0;
    x *= DEG_TO_RAD;
    y *= DEG_TO_RAD;
    CHECK( x == Approx(-3.1590459461) );
    CHECK( y == Approx(-1.5882496193) );
    CHECK( 0 == pj_transform(proj0, proj1, 1, 0, &x, &y, nullptr) );
    CHECK( std::isinf(x) );
    CHECK( std::isinf(y) );

    // now test mapnik class
    double x2 = -181.0;
    double y2 = -91.0;
    CHECK( false == prj_trans.forward(&x2,&y2,nullptr,1,1) );
    CHECK( std::isinf(x2) );
    CHECK( std::isinf(y2) );
    double x3 = -181.0;
    double y3 = -91.0;
    CHECK( false == prj_trans2.backward(&x3,&y3,nullptr,1,1) );
    CHECK( std::isinf(x3) );
    CHECK( std::isinf(y3) );

    // cleanup
    pj_ctx_free(proj_ctx0);
    proj_ctx0 = nullptr;
    pj_free(proj0);
    proj0 = nullptr;
    pj_ctx_free(proj_ctx1);
    proj_ctx1 = nullptr;
    pj_free(proj1);
    proj1 = nullptr;
}

#endif

// Github Issue https://github.com/mapnik/mapnik/issues/2648
SECTION("Test proj antimeridian bbox")
{
    mapnik::projection prj_geog("+init=epsg:4326");
    mapnik::projection prj_proj("+init=epsg:2193");

    mapnik::proj_transform prj_trans_fwd(prj_proj, prj_geog);
    mapnik::proj_transform prj_trans_rev(prj_geog, prj_proj);

    // bad = mapnik.Box2d(-177.31453250437079, -62.33374815225163, 178.02778363316355, -24.584597490955804)
    const mapnik::box2d<double> better(-180.0, -62.33374815225163,
                                        180.0, -24.584597490955804);

    {
        mapnik::box2d<double> ext(274000, 3087000, 3327000, 7173000);
        prj_trans_fwd.forward(ext, PROJ_ENVELOPE_POINTS);
        CHECK(ext.minx() == Approx(better.minx()));
        CHECK(ext.miny() == Approx(better.miny()));
        CHECK(ext.maxx() == Approx(better.maxx()));
        CHECK(ext.maxy() == Approx(better.maxy()));
    }

    {
        // check the same logic works for .backward()
        mapnik::box2d<double> ext(274000, 3087000, 3327000, 7173000);
        prj_trans_rev.backward(ext, PROJ_ENVELOPE_POINTS);
        CHECK(ext.minx() == Approx(better.minx()));
        CHECK(ext.miny() == Approx(better.miny()));
        CHECK(ext.maxx() == Approx(better.maxx()));
        CHECK(ext.maxy() == Approx(better.maxy()));
    }

    {
        // checks for not being snapped (ie. not antimeridian)
        mapnik::box2d<double> ext(274000, 3087000, 3327000, 7173000);
        prj_trans_rev.backward(ext, PROJ_ENVELOPE_POINTS);
        CHECK(ext.minx() == Approx(better.minx()));
        CHECK(ext.miny() == Approx(better.miny()));
        CHECK(ext.maxx() == Approx(better.maxx()));
        CHECK(ext.maxy() == Approx(better.maxy()));
    }

    const mapnik::box2d<double> normal(148.766759749, -60.1222810238,
                                       159.95484893, -24.9774643167);

    {
        // checks for not being snapped (ie. not antimeridian)
        mapnik::box2d<double> ext(274000, 3087000, 276000, 7173000);
        prj_trans_fwd.forward(ext, PROJ_ENVELOPE_POINTS);
        CHECK(ext.minx() == Approx(normal.minx()));
        CHECK(ext.miny() == Approx(normal.miny()));
        CHECK(ext.maxx() == Approx(normal.maxx()));
        CHECK(ext.maxy() == Approx(normal.maxy()));
    }

    {
        // check the same logic works for .backward()
        mapnik::box2d<double> ext(274000, 3087000, 276000, 7173000);
        prj_trans_rev.backward(ext, PROJ_ENVELOPE_POINTS);
        CHECK(ext.minx() == Approx(normal.minx()));
        CHECK(ext.miny() == Approx(normal.miny()));
        CHECK(ext.maxx() == Approx(normal.maxx()));
        CHECK(ext.maxy() == Approx(normal.maxy()));
    }
}

}
<commit_msg>proj_transform test: use reference values from cs2cs tool<commit_after>
#include "catch.hpp"

#include <mapnik/projection.hpp>
#include <mapnik/proj_transform.hpp>
#include <mapnik/box2d.hpp>

#ifdef MAPNIK_USE_PROJ4
// proj4
#include <proj_api.h>
#endif

TEST_CASE("projection transform")
{

SECTION("Test bounding box transforms - 4326 to 3857")
{
    mapnik::projection proj_4326("+init=epsg:4326");
    mapnik::projection proj_3857("+init=epsg:3857");
    mapnik::proj_transform prj_trans(proj_4326, proj_3857);

    double minx = -45.0;
    double miny = 55.0;
    double maxx = -40.0;
    double maxy = 75.0;

    mapnik::box2d<double> bbox(minx, miny, maxx, maxy);

    prj_trans.forward(bbox);
    INFO(bbox.to_string());
    CHECK(bbox.minx() == Approx(-5009377.085697311));
    CHECK(bbox.miny() == Approx(7361866.1130511891));
    CHECK(bbox.maxx() == Approx(-4452779.631730943));
    CHECK(bbox.maxy() == Approx(12932243.1119920239));

    prj_trans.backward(bbox);
    CHECK(bbox.minx() == Approx(minx));
    CHECK(bbox.miny() == Approx(miny));
    CHECK(bbox.maxx() == Approx(maxx));
    CHECK(bbox.maxy() == Approx(maxy));

}

#if defined(MAPNIK_USE_PROJ4) && PJ_VERSION >= 480
SECTION("test pj_transform failure behavior")
{
    mapnik::projection proj_4269("+init=epsg:4269");
    mapnik::projection proj_3857("+init=epsg:3857");
    mapnik::proj_transform prj_trans(proj_4269, proj_3857);
    mapnik::proj_transform prj_trans2(proj_3857, proj_4269);

    auto proj_ctx0 = pj_ctx_alloc();
    REQUIRE( proj_ctx0 != nullptr );
    auto proj0 = pj_init_plus_ctx(proj_ctx0, proj_4269.params().c_str());
    REQUIRE( proj0 != nullptr );

    auto proj_ctx1 = pj_ctx_alloc();
    REQUIRE( proj_ctx1 != nullptr );
    auto proj1 = pj_init_plus_ctx(proj_ctx1, proj_3857.params().c_str());
    REQUIRE( proj1 != nullptr );

    // first test valid values directly against proj
    double x = -180.0;
    double y = -60.0;
    x *= DEG_TO_RAD;
    y *= DEG_TO_RAD;
    CHECK( x == Approx(-3.1415926536) );
    CHECK( y == Approx(-1.0471975512) );
    CHECK( 0 == pj_transform(proj0, proj1, 1, 0, &x, &y, nullptr) );
    CHECK( x == Approx(-20037508.3427892439) );
    CHECK( y == Approx(-8399737.8896366451) );

    // now test mapnik class
    double x0 = -180.0;
    double y0 = -60.0;
    CHECK( prj_trans.forward(&x0,&y0,nullptr,1,1) );
    CHECK( x0 == Approx(-20037508.3427892439) );
    CHECK( y0 == Approx(-8399737.8896366451) );
    double x1 = -180.0;
    double y1 = -60.0;
    CHECK( prj_trans2.backward(&x1,&y1,nullptr,1,1) );
    CHECK( x1 == Approx(-20037508.3427892439) );
    CHECK( y1 == Approx(-8399737.8896366451) );

    // longitude value outside the value range for mercator
    x = -181.0;
    y = -91.0;
    x *= DEG_TO_RAD;
    y *= DEG_TO_RAD;
    CHECK( x == Approx(-3.1590459461) );
    CHECK( y == Approx(-1.5882496193) );
    CHECK( 0 == pj_transform(proj0, proj1, 1, 0, &x, &y, nullptr) );
    CHECK( std::isinf(x) );
    CHECK( std::isinf(y) );

    // now test mapnik class
    double x2 = -181.0;
    double y2 = -91.0;
    CHECK( false == prj_trans.forward(&x2,&y2,nullptr,1,1) );
    CHECK( std::isinf(x2) );
    CHECK( std::isinf(y2) );
    double x3 = -181.0;
    double y3 = -91.0;
    CHECK( false == prj_trans2.backward(&x3,&y3,nullptr,1,1) );
    CHECK( std::isinf(x3) );
    CHECK( std::isinf(y3) );

    // cleanup
    pj_ctx_free(proj_ctx0);
    proj_ctx0 = nullptr;
    pj_free(proj0);
    proj0 = nullptr;
    pj_ctx_free(proj_ctx1);
    proj_ctx1 = nullptr;
    pj_free(proj1);
    proj1 = nullptr;
}

#endif

// Github Issue https://github.com/mapnik/mapnik/issues/2648
SECTION("Test proj antimeridian bbox")
{
    mapnik::projection prj_geog("+init=epsg:4326");
    mapnik::projection prj_proj("+init=epsg:2193");

    mapnik::proj_transform prj_trans_fwd(prj_proj, prj_geog);
    mapnik::proj_transform prj_trans_rev(prj_geog, prj_proj);

    // reference values taken from proj4 command line tool:
    // (non-corner points assume PROJ_ENVELOPE_POINTS == 20)
    //
    //  cs2cs -Ef %.10f +init=epsg:2193 +to +init=epsg:4326 <<END
    //        2105800 3087000 # left-most
    //        1495200 3087000 # bottom-most
    //        2105800 7173000 # right-most
    //        3327000 7173000 # top-most
    //  END
    //
    // wrong = mapnik.Box2d(-177.3145325044, -62.3337481525,
    //                       178.0277836332, -24.5845974912)
    const mapnik::box2d<double> better(-180.0, -62.3337481525,
                                        180.0, -24.5845974912);

    {
        mapnik::box2d<double> ext(274000, 3087000, 3327000, 7173000);
        prj_trans_fwd.forward(ext, PROJ_ENVELOPE_POINTS);
        CHECK(ext.minx() == Approx(better.minx()));
        CHECK(ext.miny() == Approx(better.miny()));
        CHECK(ext.maxx() == Approx(better.maxx()));
        CHECK(ext.maxy() == Approx(better.maxy()));
    }

    {
        // check the same logic works for .backward()
        mapnik::box2d<double> ext(274000, 3087000, 3327000, 7173000);
        prj_trans_rev.backward(ext, PROJ_ENVELOPE_POINTS);
        CHECK(ext.minx() == Approx(better.minx()));
        CHECK(ext.miny() == Approx(better.miny()));
        CHECK(ext.maxx() == Approx(better.maxx()));
        CHECK(ext.maxy() == Approx(better.maxy()));
    }

    // reference values taken from proj4 command line tool:
    //
    //  cs2cs -Ef %.10f +init=epsg:2193 +to +init=epsg:4326 <<END
    //        274000 3087000 # left-most
    //        276000 3087000 # bottom-most
    //        276000 7173000 # right-most
    //        274000 7173000 # top-most
    //  END
    //
    const mapnik::box2d<double> normal(148.7667597489, -60.1222810241,
                                       159.9548489296, -24.9771195155);

    {
        // checks for not being snapped (ie. not antimeridian)
        mapnik::box2d<double> ext(274000, 3087000, 276000, 7173000);
        prj_trans_fwd.forward(ext, PROJ_ENVELOPE_POINTS);
        CHECK(ext.minx() == Approx(normal.minx()));
        CHECK(ext.miny() == Approx(normal.miny()));
        CHECK(ext.maxx() == Approx(normal.maxx()));
        CHECK(ext.maxy() == Approx(normal.maxy()));
    }

    {
        // check the same logic works for .backward()
        mapnik::box2d<double> ext(274000, 3087000, 276000, 7173000);
        prj_trans_rev.backward(ext, PROJ_ENVELOPE_POINTS);
        CHECK(ext.minx() == Approx(normal.minx()));
        CHECK(ext.miny() == Approx(normal.miny()));
        CHECK(ext.maxx() == Approx(normal.maxx()));
        CHECK(ext.maxy() == Approx(normal.maxy()));
    }
}

}
<|endoftext|>


<commit_before>// Copyright 2016 Google Inc. All rights reserved.
//
// 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 "mocks/fplbase_mocks.h"

#include "fplbase/asset_manager.h"
#include "fplbase/async_loader.h"
#include "fplbase/input.h"
#include "fplbase/mesh.h"
#include "fplbase/renderer.h"
#include "fplbase/shader.h"
#include "fplbase/utilities.h"

#define STB_IMAGE_IMPLEMENTATION
#define STBI_ONLY_PNG
#include "stb_image.h"

// STB_image to resize PNG glyph.
#define STB_IMAGE_RESIZE_IMPLEMENTATION
// Disable warnings in STB_image_resize.
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4100)  // Disable 'unused reference' warning.
#else
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-variable"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif /* _MSC_VER */
#include "stb_image_resize.h"
// Pop warning status.
#ifdef _MSC_VER
#pragma warning(pop)
#else
#pragma GCC diagnostic pop
#endif

namespace fplbase {

void LogError(const char* fmt, va_list args) {
  FplbaseMocks::get_mocks().LogError(fmt, args);
}

Texture* AssetManager::FindTexture(const char* filename) {
  return FplbaseMocks::get_mocks().FindTexture(filename);
}

// Unused functions to satisfy the linker.

// asset_manager.h
AssetManager::AssetManager(Renderer& renderer) : renderer_(renderer) {}
void AssetManager::ClearAllAssets() {}
Shader* AssetManager::LoadShader(const char*, bool) { return nullptr; }

// async_loader.h
AsyncLoader::AsyncLoader() {}
AsyncLoader::~AsyncLoader() {}
void AsyncLoader::Stop() {}

// input.h
Button fake_button_to_return;
Button& InputSystem::GetButton(int) { return fake_button_to_return; }
void InputSystem::StartTextInput() {}
void InputSystem::StopTextInput() {}
double InputSystem::Time() const { return 0.0; }
void InputSystem::SetTextInputRect(const mathfu::vec4&) {}
const std::vector<TextInputEvent>* InputSystem::GetTextInputEvents() {
  return nullptr;
}

// mesh.h
void Mesh::RenderArray(Primitive, int, const Attribute*, int, const void*,
                       const unsigned short*) {}
void Mesh::RenderAAQuadAlongX(const mathfu::vec3&, const mathfu::vec3&,
                              const mathfu::vec2&, const mathfu::vec2&) {}
void Mesh::RenderAAQuadAlongXNinePatch(const mathfu::vec3&, const mathfu::vec3&,
                                       const mathfu::vec2i&,
                                       const mathfu::vec4&) {}

// renderer.h
Renderer::Renderer() {}
Renderer::~Renderer() {}

// shader.h
void Shader::Set(const Renderer&) const {}
void Shader::SetUniform(UniformHandle, const float*, size_t) {}
UniformHandle Shader::FindUniform(const char*) { return 0; }

// texture.h
Texture::Texture(const char*, TextureFormat, TextureFlags) {}
void Texture::Delete() {}
void Texture::Load() {}
void Texture::Finalize() {}
void Texture::LoadFromMemory(const uint8_t*, const mathfu::vec2i&,
                             TextureFormat) {}
void Texture::Set(size_t) {}
void Texture::Set(size_t) const {}
void Texture::UpdateTexture(TextureFormat, int, int, int, int, const void*) {}

// utilities.h
void LogInfo(const char*, va_list) {}
void LogInfo(LogCategory, const char*, va_list) {}
void LogError(LogCategory, const char*, va_list) {}
void LogError(const char*, ...) {}
void LogInfo(const char*, ...) {}
void LogInfo(LogCategory, const char*, ...) {}
void LogError(LogCategory, const char*, ...) {}
bool LoadFile(const char*, std::string*) { return false; }
bool LoeadWithIncludes(const char*, std::string*, std::string*) {
  return false;
}
#ifdef __ANDROID__
JNIEnv* AndroidGetJNIEnv() { return nullptr; }
int32_t AndroidGetApiLevel() { return 0; }
#endif  // __ANDROID__

}  // fplbase
<commit_msg>Fix unit test break caused by outdated mocks.<commit_after>// Copyright 2016 Google Inc. All rights reserved.
//
// 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 "mocks/fplbase_mocks.h"

#include "fplbase/asset_manager.h"
#include "fplbase/async_loader.h"
#include "fplbase/input.h"
#include "fplbase/mesh.h"
#include "fplbase/renderer.h"
#include "fplbase/shader.h"
#include "fplbase/utilities.h"

#define STB_IMAGE_IMPLEMENTATION
#define STBI_ONLY_PNG
#include "stb_image.h"

// STB_image to resize PNG glyph.
#define STB_IMAGE_RESIZE_IMPLEMENTATION
// Disable warnings in STB_image_resize.
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4100)  // Disable 'unused reference' warning.
#else
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-variable"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif /* _MSC_VER */
#include "stb_image_resize.h"
// Pop warning status.
#ifdef _MSC_VER
#pragma warning(pop)
#else
#pragma GCC diagnostic pop
#endif

namespace fplbase {

void LogError(const char* fmt, va_list args) {
  FplbaseMocks::get_mocks().LogError(fmt, args);
}

Texture* AssetManager::FindTexture(const char* filename) {
  return FplbaseMocks::get_mocks().FindTexture(filename);
}

// Unused functions to satisfy the linker.

// asset_manager.h
AssetManager::AssetManager(Renderer& renderer) : renderer_(renderer) {}
void AssetManager::ClearAllAssets() {}
Shader* AssetManager::LoadShader(const char*, bool, const char*) {
  return nullptr;
}

// async_loader.h
AsyncLoader::AsyncLoader() {}
AsyncLoader::~AsyncLoader() {}
void AsyncLoader::Stop() {}

// input.h
Button fake_button_to_return;
Button& InputSystem::GetButton(int) { return fake_button_to_return; }
void InputSystem::StartTextInput() {}
void InputSystem::StopTextInput() {}
double InputSystem::Time() const { return 0.0; }
void InputSystem::SetTextInputRect(const mathfu::vec4&) {}
const std::vector<TextInputEvent>* InputSystem::GetTextInputEvents() {
  return nullptr;
}

// mesh.h
void Mesh::RenderArray(Primitive, int, const Attribute*, int, const void*,
                       const unsigned short*) {}
void Mesh::RenderAAQuadAlongX(const mathfu::vec3&, const mathfu::vec3&,
                              const mathfu::vec2&, const mathfu::vec2&) {}
void Mesh::RenderAAQuadAlongXNinePatch(const mathfu::vec3&, const mathfu::vec3&,
                                       const mathfu::vec2i&,
                                       const mathfu::vec4&) {}

// renderer.h
Renderer::Renderer() {}
Renderer::~Renderer() {}

// shader.h
void Shader::Set(const Renderer&) const {}
void Shader::SetUniform(UniformHandle, const float*, size_t) {}
UniformHandle Shader::FindUniform(const char*) { return 0; }

// texture.h
Texture::Texture(const char*, TextureFormat, TextureFlags) {}
void Texture::Delete() {}
void Texture::Load() {}
bool Texture::Finalize() { return false; }
void Texture::LoadFromMemory(const uint8_t*, const mathfu::vec2i&,
                             TextureFormat) {}
void Texture::Set(size_t) {}
void Texture::Set(size_t) const {}
void Texture::UpdateTexture(TextureFormat, int, int, int, int, const void*) {}

// utilities.h
void LogInfo(const char*, va_list) {}
void LogInfo(LogCategory, const char*, va_list) {}
void LogError(LogCategory, const char*, va_list) {}
void LogError(const char*, ...) {}
void LogInfo(const char*, ...) {}
void LogInfo(LogCategory, const char*, ...) {}
void LogError(LogCategory, const char*, ...) {}
bool LoadFile(const char*, std::string*) { return false; }
bool LoeadWithIncludes(const char*, std::string*, std::string*) {
  return false;
}
#ifdef __ANDROID__
JNIEnv* AndroidGetJNIEnv() { return nullptr; }
int32_t AndroidGetApiLevel() { return 0; }
#endif  // __ANDROID__

}  // fplbase
<|endoftext|>


<commit_before>/*

Copyright (c) 2007, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * 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.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

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.

*/

#include <asio/ip/host_name.hpp>
#include <asio/ip/multicast.hpp>
#include <boost/bind.hpp>

#include "libtorrent/socket.hpp"
#include "libtorrent/enum_net.hpp"
#include "libtorrent/broadcast_socket.hpp"
#include "libtorrent/assert.hpp"

namespace libtorrent
{
	bool is_local(address const& a)
	{
		if (a.is_v6()) return a.to_v6().is_link_local();
		address_v4 a4 = a.to_v4();
		unsigned long ip = a4.to_ulong();
		return ((ip & 0xff000000) == 0x0a000000
			|| (ip & 0xfff00000) == 0xac100000
			|| (ip & 0xffff0000) == 0xc0a80000);
	}

	bool is_loopback(address const& addr)
	{
		if (addr.is_v4())
		  return addr.to_v4() == address_v4::loopback();
		else
			return addr.to_v6() == address_v6::loopback();
	}

	bool is_multicast(address const& addr)
	{
		if (addr.is_v4())
			return addr.to_v4().is_multicast();
		else
			return addr.to_v6().is_multicast();
	}

	bool is_any(address const& addr)
	{
		if (addr.is_v4())
			return addr.to_v4() == address_v4::any();
		else
			return addr.to_v6() == address_v6::any();
	}

	address guess_local_address(asio::io_service& ios)
	{
		// make a best guess of the interface we're using and its IP
		asio::error_code ec;
		std::vector<address> const& interfaces = enum_net_interfaces(ios, ec);
		address ret = address_v4::any();
		for (std::vector<address>::const_iterator i = interfaces.begin()
			, end(interfaces.end()); i != end; ++i)
		{
			address const& a = *i;
			if (is_loopback(a)
				|| is_multicast(a)
				|| is_any(a)) continue;

			// prefer a v4 address, but return a v6 if
			// there are no v4
			if (a.is_v4()) return a;

			if (ret != address_v4::any())
				ret = a;
		}
		return ret;
	}

	broadcast_socket::broadcast_socket(asio::io_service& ios
		, udp::endpoint const& multicast_endpoint
		, receive_handler_t const& handler
		, bool loopback)
		: m_multicast_endpoint(multicast_endpoint)
		, m_on_receive(handler)
	{
		TORRENT_ASSERT(is_multicast(m_multicast_endpoint.address()));

		using namespace asio::ip::multicast;

		asio::error_code ec;
		std::vector<address> interfaces = enum_net_interfaces(ios, ec);

		for (std::vector<address>::const_iterator i = interfaces.begin()
			, end(interfaces.end()); i != end; ++i)
		{
			// only broadcast to IPv4 addresses that are not local
			if (!is_local(*i)) continue;
			// only multicast on compatible networks
			if (i->is_v4() != multicast_endpoint.address().is_v4()) continue;
			// ignore any loopback interface
			if (is_loopback(*i)) continue;

			boost::shared_ptr<datagram_socket> s(new datagram_socket(ios));
			if (i->is_v4())
			{
				s->open(udp::v4(), ec);
				if (ec) continue;
				s->set_option(datagram_socket::reuse_address(true), ec);
				if (ec) continue;
				s->bind(udp::endpoint(address_v4::any(), multicast_endpoint.port()), ec);
				if (ec) continue;
				s->set_option(join_group(multicast_endpoint.address()), ec);
				if (ec) continue;
				s->set_option(outbound_interface(i->to_v4()), ec);
				if (ec) continue;
			}
			else
			{
				s->open(udp::v6(), ec);
				if (ec) continue;
				s->set_option(datagram_socket::reuse_address(true), ec);
				if (ec) continue;
				s->bind(udp::endpoint(address_v6::any(), multicast_endpoint.port()), ec);
				if (ec) continue;
				s->set_option(join_group(multicast_endpoint.address()), ec);
				if (ec) continue;
//				s->set_option(outbound_interface(i->to_v6()), ec);
//				if (ec) continue;
			}
			s->set_option(hops(255), ec);
			if (ec) continue;
			s->set_option(enable_loopback(loopback), ec);
			if (ec) continue;
			m_sockets.push_back(socket_entry(s));
			socket_entry& se = m_sockets.back();
			s->async_receive_from(asio::buffer(se.buffer, sizeof(se.buffer))
				, se.remote, bind(&broadcast_socket::on_receive, this, &se, _1, _2));
#ifndef NDEBUG
//			std::cerr << "broadcast socket [ if: " << i->to_v4().to_string()
//				<< " group: " << multicast_endpoint.address() << " ]" << std::endl;
#endif
		}
		open_unicast_socket(ios, address_v4::any());
		open_unicast_socket(ios, address_v6::any());
	}

	void broadcast_socket::open_unicast_socket(io_service& ios, address const& addr)
	{
		asio::error_code ec;
		boost::shared_ptr<datagram_socket> s(new datagram_socket(ios));
		s->open(addr.is_v4() ? udp::v4() : udp::v6(), ec);
		if (ec) return;
		s->bind(udp::endpoint(addr, 0), ec);
		if (ec) return;
		m_unicast_sockets.push_back(socket_entry(s));
		socket_entry& se = m_unicast_sockets.back();
		s->async_receive_from(asio::buffer(se.buffer, sizeof(se.buffer))
			, se.remote, bind(&broadcast_socket::on_receive, this, &se, _1, _2));
	}

	void broadcast_socket::send(char const* buffer, int size, asio::error_code& ec)
	{
		for (std::list<socket_entry>::iterator i = m_unicast_sockets.begin()
			, end(m_unicast_sockets.end()); i != end; ++i)
		{
			if (!i->socket) continue;
			asio::error_code e;
			i->socket->send_to(asio::buffer(buffer, size), m_multicast_endpoint, 0, e);
#ifndef NDEBUG
//			std::cerr << " sending on " << i->socket->local_endpoint().address().to_string() << " to: " << m_multicast_endpoint << std::endl;
#endif
			if (e)
			{
				i->socket->close(e);
				i->socket.reset();
			}
		}

		for (std::list<socket_entry>::iterator i = m_sockets.begin()
			, end(m_sockets.end()); i != end; ++i)
		{
			if (!i->socket) continue;
			asio::error_code e;
			i->socket->send_to(asio::buffer(buffer, size), m_multicast_endpoint, 0, e);
#ifndef NDEBUG
//			std::cerr << " sending on " << i->socket->local_endpoint().address().to_string() << " to: " << m_multicast_endpoint << std::endl;
#endif
			if (e)
			{
				i->socket->close(e);
				i->socket.reset();
			}
		}
	}

	void broadcast_socket::on_receive(socket_entry* s, asio::error_code const& ec
		, std::size_t bytes_transferred)
	{
		if (ec || bytes_transferred == 0 || !m_on_receive) return;
		m_on_receive(s->remote, s->buffer, bytes_transferred);
		if (!s->socket) return;
		s->socket->async_receive_from(asio::buffer(s->buffer, sizeof(s->buffer))
			, s->remote, bind(&broadcast_socket::on_receive, this, s, _1, _2));
	}

	void broadcast_socket::close()
	{
		std::for_each(m_sockets.begin(), m_sockets.end(), bind(&socket_entry::close, _1));
		std::for_each(m_unicast_sockets.begin(), m_unicast_sockets.end(), bind(&socket_entry::close, _1));

		m_on_receive.clear();
	}
}

<commit_msg>broadcast socket updates (better upnp support)<commit_after>/*

Copyright (c) 2007, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * 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.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

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.

*/

#include <asio/ip/host_name.hpp>
#include <asio/ip/multicast.hpp>
#include <boost/bind.hpp>

#include "libtorrent/socket.hpp"
#include "libtorrent/enum_net.hpp"
#include "libtorrent/broadcast_socket.hpp"
#include "libtorrent/assert.hpp"

namespace libtorrent
{
	bool is_local(address const& a)
	{
		if (a.is_v6()) return a.to_v6().is_link_local();
		address_v4 a4 = a.to_v4();
		unsigned long ip = a4.to_ulong();
		return ((ip & 0xff000000) == 0x0a000000
			|| (ip & 0xfff00000) == 0xac100000
			|| (ip & 0xffff0000) == 0xc0a80000);
	}

	bool is_loopback(address const& addr)
	{
		if (addr.is_v4())
		  return addr.to_v4() == address_v4::loopback();
		else
			return addr.to_v6() == address_v6::loopback();
	}

	bool is_multicast(address const& addr)
	{
		if (addr.is_v4())
			return addr.to_v4().is_multicast();
		else
			return addr.to_v6().is_multicast();
	}

	bool is_any(address const& addr)
	{
		if (addr.is_v4())
			return addr.to_v4() == address_v4::any();
		else
			return addr.to_v6() == address_v6::any();
	}

	address guess_local_address(asio::io_service& ios)
	{
		// make a best guess of the interface we're using and its IP
		asio::error_code ec;
		std::vector<address> const& interfaces = enum_net_interfaces(ios, ec);
		address ret = address_v4::any();
		for (std::vector<address>::const_iterator i = interfaces.begin()
			, end(interfaces.end()); i != end; ++i)
		{
			address const& a = *i;
			if (is_loopback(a)
				|| is_multicast(a)
				|| is_any(a)) continue;

			// prefer a v4 address, but return a v6 if
			// there are no v4
			if (a.is_v4()) return a;

			if (ret != address_v4::any())
				ret = a;
		}
		return ret;
	}

	broadcast_socket::broadcast_socket(asio::io_service& ios
		, udp::endpoint const& multicast_endpoint
		, receive_handler_t const& handler
		, bool loopback)
		: m_multicast_endpoint(multicast_endpoint)
		, m_on_receive(handler)
	{
		TORRENT_ASSERT(is_multicast(m_multicast_endpoint.address()));

		using namespace asio::ip::multicast;

		asio::error_code ec;
		std::vector<address> interfaces = enum_net_interfaces(ios, ec);

		if (multicast_endpoint.address().is_v4())
			open_multicast_socket(ios, address_v4::any(), loopback);
		else
			open_multicast_socket(ios, address_v6::any(), loopback);

		for (std::vector<address>::const_iterator i = interfaces.begin()
			, end(interfaces.end()); i != end; ++i)
		{
			// only broadcast to IPv4 addresses that are not local
			if (!is_local(*i)) continue;
			// only multicast on compatible networks
			if (i->is_v4() != multicast_endpoint.address().is_v4()) continue;
			// ignore any loopback interface
			if (is_loopback(*i)) continue;

#ifndef NDEBUG
//			std::cerr << "broadcast socket [ if: " << i->to_v4().to_string()
//				<< " group: " << multicast_endpoint.address() << " ]" << std::endl;
#endif
			open_unicast_socket(ios, *i);
		}
	}

	void broadcast_socket::open_multicast_socket(io_service& ios
		, address const& addr, bool loopback)
	{
		using namespace asio::ip::multicast;

		asio::error_code ec;
		boost::shared_ptr<datagram_socket> s(new datagram_socket(ios));
		if (addr.is_v4())
			s->open(udp::v4(), ec);
		else
			s->open(udp::v6(), ec);
		if (ec) return;
		s->set_option(datagram_socket::reuse_address(true), ec);
		if (ec) return;
		s->bind(udp::endpoint(addr, m_multicast_endpoint.port()), ec);
		if (ec) return;
		s->set_option(join_group(m_multicast_endpoint.address()), ec);
		if (ec) return;
		s->set_option(hops(255), ec);
		if (ec) return;
		s->set_option(enable_loopback(loopback), ec);
		if (ec) return;
		m_sockets.push_back(socket_entry(s));
		socket_entry& se = m_sockets.back();
		s->async_receive_from(asio::buffer(se.buffer, sizeof(se.buffer))
			, se.remote, bind(&broadcast_socket::on_receive, this, &se, _1, _2));
	}

	void broadcast_socket::open_unicast_socket(io_service& ios, address const& addr)
	{
		using namespace asio::ip::multicast;
		asio::error_code ec;
		boost::shared_ptr<datagram_socket> s(new datagram_socket(ios));
		s->open(addr.is_v4() ? udp::v4() : udp::v6(), ec);
		if (ec) return;
		s->bind(udp::endpoint(addr, 0), ec);
		if (ec) return;
		if (addr.is_v4())
			s->set_option(outbound_interface(addr.to_v4()), ec);
		if (ec) return;
		m_unicast_sockets.push_back(socket_entry(s));
		socket_entry& se = m_unicast_sockets.back();
		s->async_receive_from(asio::buffer(se.buffer, sizeof(se.buffer))
			, se.remote, bind(&broadcast_socket::on_receive, this, &se, _1, _2));
	}

	void broadcast_socket::send(char const* buffer, int size, asio::error_code& ec)
	{
		for (std::list<socket_entry>::iterator i = m_unicast_sockets.begin()
			, end(m_unicast_sockets.end()); i != end; ++i)
		{
			if (!i->socket) continue;
			asio::error_code e;
			i->socket->send_to(asio::buffer(buffer, size), m_multicast_endpoint, 0, e);
#ifndef NDEBUG
//			std::cerr << " sending on " << i->socket->local_endpoint().address().to_string() << " to: " << m_multicast_endpoint << std::endl;
#endif
			if (e)
			{
				i->socket->close(e);
				i->socket.reset();
			}
		}
	}

	void broadcast_socket::on_receive(socket_entry* s, asio::error_code const& ec
		, std::size_t bytes_transferred)
	{
		if (ec || bytes_transferred == 0 || !m_on_receive) return;
		m_on_receive(s->remote, s->buffer, bytes_transferred);
		if (!s->socket) return;
		s->socket->async_receive_from(asio::buffer(s->buffer, sizeof(s->buffer))
			, s->remote, bind(&broadcast_socket::on_receive, this, s, _1, _2));
	}

	void broadcast_socket::close()
	{
		std::for_each(m_sockets.begin(), m_sockets.end(), bind(&socket_entry::close, _1));
		std::for_each(m_unicast_sockets.begin(), m_unicast_sockets.end(), bind(&socket_entry::close, _1));

		m_on_receive.clear();
	}
}

<|endoftext|>


<commit_before>/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * 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 "common_throws.h"
#include "debugger.h"
#include "jni_internal.h"
#include "monitor.h"
#include "mirror/object.h"
#include "scoped_fast_native_object_access.h"
#include "scoped_thread_state_change.h"
#include "ScopedUtfChars.h"
#include "thread.h"
#include "thread_list.h"
#include "verify_object-inl.h"

namespace art {

static jobject Thread_currentThread(JNIEnv* env, jclass) {
  ScopedFastNativeObjectAccess soa(env);
  return soa.AddLocalReference<jobject>(soa.Self()->GetPeer());
}

static jboolean Thread_interrupted(JNIEnv* env, jclass) {
  return static_cast<JNIEnvExt*>(env)->self->Interrupted() ? JNI_TRUE : JNI_FALSE;
}

static jboolean Thread_isInterrupted(JNIEnv* env, jobject java_thread) {
  ScopedFastNativeObjectAccess soa(env);
  MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
  Thread* thread = Thread::FromManagedThread(soa, java_thread);
  return (thread != NULL) ? thread->IsInterrupted() : JNI_FALSE;
}

static void Thread_nativeCreate(JNIEnv* env, jclass, jobject java_thread, jlong stack_size,
                                jboolean daemon) {
  Thread::CreateNativeThread(env, java_thread, stack_size, daemon == JNI_TRUE);
}

static jint Thread_nativeGetStatus(JNIEnv* env, jobject java_thread, jboolean has_been_started) {
  // Ordinals from Java's Thread.State.
  const jint kJavaNew = 0;
  const jint kJavaRunnable = 1;
  const jint kJavaBlocked = 2;
  const jint kJavaWaiting = 3;
  const jint kJavaTimedWaiting = 4;
  const jint kJavaTerminated = 5;

  ScopedObjectAccess soa(env);
  ThreadState internal_thread_state = (has_been_started ? kTerminated : kStarting);
  MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
  Thread* thread = Thread::FromManagedThread(soa, java_thread);
  if (thread != NULL) {
    internal_thread_state = thread->GetState();
  }
  switch (internal_thread_state) {
    case kTerminated:                     return kJavaTerminated;
    case kRunnable:                       return kJavaRunnable;
    case kTimedWaiting:                   return kJavaTimedWaiting;
    case kSleeping:                       return kJavaTimedWaiting;
    case kBlocked:                        return kJavaBlocked;
    case kWaiting:                        return kJavaWaiting;
    case kStarting:                       return kJavaNew;
    case kNative:                         return kJavaRunnable;
    case kWaitingForGcToComplete:         return kJavaWaiting;
    case kWaitingPerformingGc:            return kJavaWaiting;
    case kWaitingForCheckPointsToRun:     return kJavaWaiting;
    case kWaitingForDebuggerSend:         return kJavaWaiting;
    case kWaitingForDebuggerToAttach:     return kJavaWaiting;
    case kWaitingInMainDebuggerLoop:      return kJavaWaiting;
    case kWaitingForDebuggerSuspension:   return kJavaWaiting;
    case kWaitingForDeoptimization:       return kJavaWaiting;
    case kWaitingForJniOnLoad:            return kJavaWaiting;
    case kWaitingForSignalCatcherOutput:  return kJavaWaiting;
    case kWaitingInMainSignalCatcherLoop: return kJavaWaiting;
    case kSuspended:                      return kJavaRunnable;
    // Don't add a 'default' here so the compiler can spot incompatible enum changes.
  }
  return -1;  // Unreachable.
}

static jboolean Thread_nativeHoldsLock(JNIEnv* env, jobject java_thread, jobject java_object) {
  ScopedObjectAccess soa(env);
  mirror::Object* object = soa.Decode<mirror::Object*>(java_object);
  if (object == NULL) {
    ThrowNullPointerException(NULL, "object == null");
    return JNI_FALSE;
  }
  MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
  Thread* thread = Thread::FromManagedThread(soa, java_thread);
  return thread->HoldsLock(object);
}

static void Thread_nativeInterrupt(JNIEnv* env, jobject java_thread) {
  ScopedFastNativeObjectAccess soa(env);
  MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
  Thread* thread = Thread::FromManagedThread(soa, java_thread);
  if (thread != NULL) {
    thread->Interrupt(soa.Self());
  }
}

static void Thread_nativeSetName(JNIEnv* env, jobject peer, jstring java_name) {
  ScopedUtfChars name(env, java_name);
  {
    ScopedObjectAccess soa(env);
    if (soa.Decode<mirror::Object*>(peer) == soa.Self()->GetPeer()) {
      soa.Self()->SetThreadName(name.c_str());
      return;
    }
  }
  // Suspend thread to avoid it from killing itself while we set its name. We don't just hold the
  // thread list lock to avoid this, as setting the thread name causes mutator to lock/unlock
  // in the DDMS send code.
  bool timed_out;
  Thread* thread = ThreadList::SuspendThreadByPeer(peer, true, false, &timed_out);
  if (thread != NULL) {
    {
      ScopedObjectAccess soa(env);
      thread->SetThreadName(name.c_str());
    }
    Runtime::Current()->GetThreadList()->Resume(thread, false);
  } else if (timed_out) {
    LOG(ERROR) << "Trying to set thread name to '" << name.c_str() << "' failed as the thread "
        "failed to suspend within a generous timeout.";
  }
}

/*
 * Alter the priority of the specified thread.  "new_priority" will range
 * from Thread.MIN_PRIORITY to Thread.MAX_PRIORITY (1-10), with "normal"
 * threads at Thread.NORM_PRIORITY (5).
 */
static void Thread_nativeSetPriority(JNIEnv* env, jobject java_thread, jint new_priority) {
  ScopedObjectAccess soa(env);
  MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
  Thread* thread = Thread::FromManagedThread(soa, java_thread);
  if (thread != NULL) {
    thread->SetNativePriority(new_priority);
  }
}

static void Thread_sleep(JNIEnv* env, jclass, jobject java_lock, jlong ms, jint ns) {
  ScopedFastNativeObjectAccess soa(env);
  mirror::Object* lock = soa.Decode<mirror::Object*>(java_lock);
  Monitor::Wait(Thread::Current(), lock, ms, ns, true, kSleeping);
}

/*
 * Causes the thread to temporarily pause and allow other threads to execute.
 *
 * The exact behavior is poorly defined.  Some discussion here:
 *   http://www.cs.umd.edu/~pugh/java/memoryModel/archive/0944.html
 */
static void Thread_yield(JNIEnv*, jobject) {
  sched_yield();
}

static JNINativeMethod gMethods[] = {
  NATIVE_METHOD(Thread, currentThread, "!()Ljava/lang/Thread;"),
  NATIVE_METHOD(Thread, interrupted, "!()Z"),
  NATIVE_METHOD(Thread, isInterrupted, "!()Z"),
  NATIVE_METHOD(Thread, nativeCreate, "(Ljava/lang/Thread;JZ)V"),
  NATIVE_METHOD(Thread, nativeGetStatus, "(Z)I"),
  NATIVE_METHOD(Thread, nativeHoldsLock, "(Ljava/lang/Object;)Z"),
  NATIVE_METHOD(Thread, nativeInterrupt, "!()V"),
  NATIVE_METHOD(Thread, nativeSetName, "(Ljava/lang/String;)V"),
  NATIVE_METHOD(Thread, nativeSetPriority, "(I)V"),
  NATIVE_METHOD(Thread, sleep, "!(Ljava/lang/Object;JI)V"),
  NATIVE_METHOD(Thread, yield, "()V"),
};

void register_java_lang_Thread(JNIEnv* env) {
  REGISTER_NATIVE_METHODS("java/lang/Thread");
}

}  // namespace art
<commit_msg>Warn when thread state lookup fails.<commit_after>/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * 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 "common_throws.h"
#include "debugger.h"
#include "jni_internal.h"
#include "monitor.h"
#include "mirror/object.h"
#include "scoped_fast_native_object_access.h"
#include "scoped_thread_state_change.h"
#include "ScopedUtfChars.h"
#include "thread.h"
#include "thread_list.h"
#include "verify_object-inl.h"

namespace art {

static jobject Thread_currentThread(JNIEnv* env, jclass) {
  ScopedFastNativeObjectAccess soa(env);
  return soa.AddLocalReference<jobject>(soa.Self()->GetPeer());
}

static jboolean Thread_interrupted(JNIEnv* env, jclass) {
  return static_cast<JNIEnvExt*>(env)->self->Interrupted() ? JNI_TRUE : JNI_FALSE;
}

static jboolean Thread_isInterrupted(JNIEnv* env, jobject java_thread) {
  ScopedFastNativeObjectAccess soa(env);
  MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
  Thread* thread = Thread::FromManagedThread(soa, java_thread);
  return (thread != NULL) ? thread->IsInterrupted() : JNI_FALSE;
}

static void Thread_nativeCreate(JNIEnv* env, jclass, jobject java_thread, jlong stack_size,
                                jboolean daemon) {
  Thread::CreateNativeThread(env, java_thread, stack_size, daemon == JNI_TRUE);
}

static jint Thread_nativeGetStatus(JNIEnv* env, jobject java_thread, jboolean has_been_started) {
  // Ordinals from Java's Thread.State.
  const jint kJavaNew = 0;
  const jint kJavaRunnable = 1;
  const jint kJavaBlocked = 2;
  const jint kJavaWaiting = 3;
  const jint kJavaTimedWaiting = 4;
  const jint kJavaTerminated = 5;

  ScopedObjectAccess soa(env);
  ThreadState internal_thread_state = (has_been_started ? kTerminated : kStarting);
  MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
  Thread* thread = Thread::FromManagedThread(soa, java_thread);
  if (thread != NULL) {
    internal_thread_state = thread->GetState();
  }
  switch (internal_thread_state) {
    case kTerminated:                     return kJavaTerminated;
    case kRunnable:                       return kJavaRunnable;
    case kTimedWaiting:                   return kJavaTimedWaiting;
    case kSleeping:                       return kJavaTimedWaiting;
    case kBlocked:                        return kJavaBlocked;
    case kWaiting:                        return kJavaWaiting;
    case kStarting:                       return kJavaNew;
    case kNative:                         return kJavaRunnable;
    case kWaitingForGcToComplete:         return kJavaWaiting;
    case kWaitingPerformingGc:            return kJavaWaiting;
    case kWaitingForCheckPointsToRun:     return kJavaWaiting;
    case kWaitingForDebuggerSend:         return kJavaWaiting;
    case kWaitingForDebuggerToAttach:     return kJavaWaiting;
    case kWaitingInMainDebuggerLoop:      return kJavaWaiting;
    case kWaitingForDebuggerSuspension:   return kJavaWaiting;
    case kWaitingForDeoptimization:       return kJavaWaiting;
    case kWaitingForJniOnLoad:            return kJavaWaiting;
    case kWaitingForSignalCatcherOutput:  return kJavaWaiting;
    case kWaitingInMainSignalCatcherLoop: return kJavaWaiting;
    case kSuspended:                      return kJavaRunnable;
    // Don't add a 'default' here so the compiler can spot incompatible enum changes.
  }
  LOG(ERROR) << "Unexpected thread state: " << internal_thread_state;
  return -1;  // Unreachable.
}

static jboolean Thread_nativeHoldsLock(JNIEnv* env, jobject java_thread, jobject java_object) {
  ScopedObjectAccess soa(env);
  mirror::Object* object = soa.Decode<mirror::Object*>(java_object);
  if (object == NULL) {
    ThrowNullPointerException(NULL, "object == null");
    return JNI_FALSE;
  }
  MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
  Thread* thread = Thread::FromManagedThread(soa, java_thread);
  return thread->HoldsLock(object);
}

static void Thread_nativeInterrupt(JNIEnv* env, jobject java_thread) {
  ScopedFastNativeObjectAccess soa(env);
  MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
  Thread* thread = Thread::FromManagedThread(soa, java_thread);
  if (thread != NULL) {
    thread->Interrupt(soa.Self());
  }
}

static void Thread_nativeSetName(JNIEnv* env, jobject peer, jstring java_name) {
  ScopedUtfChars name(env, java_name);
  {
    ScopedObjectAccess soa(env);
    if (soa.Decode<mirror::Object*>(peer) == soa.Self()->GetPeer()) {
      soa.Self()->SetThreadName(name.c_str());
      return;
    }
  }
  // Suspend thread to avoid it from killing itself while we set its name. We don't just hold the
  // thread list lock to avoid this, as setting the thread name causes mutator to lock/unlock
  // in the DDMS send code.
  bool timed_out;
  Thread* thread = ThreadList::SuspendThreadByPeer(peer, true, false, &timed_out);
  if (thread != NULL) {
    {
      ScopedObjectAccess soa(env);
      thread->SetThreadName(name.c_str());
    }
    Runtime::Current()->GetThreadList()->Resume(thread, false);
  } else if (timed_out) {
    LOG(ERROR) << "Trying to set thread name to '" << name.c_str() << "' failed as the thread "
        "failed to suspend within a generous timeout.";
  }
}

/*
 * Alter the priority of the specified thread.  "new_priority" will range
 * from Thread.MIN_PRIORITY to Thread.MAX_PRIORITY (1-10), with "normal"
 * threads at Thread.NORM_PRIORITY (5).
 */
static void Thread_nativeSetPriority(JNIEnv* env, jobject java_thread, jint new_priority) {
  ScopedObjectAccess soa(env);
  MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
  Thread* thread = Thread::FromManagedThread(soa, java_thread);
  if (thread != NULL) {
    thread->SetNativePriority(new_priority);
  }
}

static void Thread_sleep(JNIEnv* env, jclass, jobject java_lock, jlong ms, jint ns) {
  ScopedFastNativeObjectAccess soa(env);
  mirror::Object* lock = soa.Decode<mirror::Object*>(java_lock);
  Monitor::Wait(Thread::Current(), lock, ms, ns, true, kSleeping);
}

/*
 * Causes the thread to temporarily pause and allow other threads to execute.
 *
 * The exact behavior is poorly defined.  Some discussion here:
 *   http://www.cs.umd.edu/~pugh/java/memoryModel/archive/0944.html
 */
static void Thread_yield(JNIEnv*, jobject) {
  sched_yield();
}

static JNINativeMethod gMethods[] = {
  NATIVE_METHOD(Thread, currentThread, "!()Ljava/lang/Thread;"),
  NATIVE_METHOD(Thread, interrupted, "!()Z"),
  NATIVE_METHOD(Thread, isInterrupted, "!()Z"),
  NATIVE_METHOD(Thread, nativeCreate, "(Ljava/lang/Thread;JZ)V"),
  NATIVE_METHOD(Thread, nativeGetStatus, "(Z)I"),
  NATIVE_METHOD(Thread, nativeHoldsLock, "(Ljava/lang/Object;)Z"),
  NATIVE_METHOD(Thread, nativeInterrupt, "!()V"),
  NATIVE_METHOD(Thread, nativeSetName, "(Ljava/lang/String;)V"),
  NATIVE_METHOD(Thread, nativeSetPriority, "(I)V"),
  NATIVE_METHOD(Thread, sleep, "!(Ljava/lang/Object;JI)V"),
  NATIVE_METHOD(Thread, yield, "()V"),
};

void register_java_lang_Thread(JNIEnv* env) {
  REGISTER_NATIVE_METHODS("java/lang/Thread");
}

}  // namespace art
<|endoftext|>