<commit_before>/*
-----------------------------------------------------------------------------
This source file is part of OGRE
    (Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2014 Torus Knot Software Ltd

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.
-----------------------------------------------------------------------------
*/
#include "OgreGLES2Prerequisites.h"
#include "OgreGpuProgram.h"
#include "OgreHighLevelGpuProgramManager.h"
#include "OgreLogManager.h"
#include "OgreRoot.h"
#include "OgreStringConverter.h"
#include "OgreGLUtil.h"
#include "OgreGLES2RenderSystem.h"
#include "OgreGLES2Support.h"

#include "OgreGLSLESProgram.h"
#include "OgreGLSLESProgramManager.h"
#include "OgreGLSLPreprocessor.h"

namespace Ogre {
    //-----------------------------------------------------------------------
#if !OGRE_NO_GLES2_GLSL_OPTIMISER
    GLSLESProgram::CmdOptimisation GLSLESProgram::msCmdOptimisation;
#endif
    //-----------------------------------------------------------------------
    //-----------------------------------------------------------------------
    GLSLESProgram::GLSLESProgram(ResourceManager* creator,
        const String& name, ResourceHandle handle,
        const String& group, bool isManual, ManualResourceLoader* loader)
        : GLSLShaderCommon(creator, name, handle, group, isManual, loader)
        , mGLShaderHandle(0)
        , mGLProgramHandle(0)
#if !OGRE_NO_GLES2_GLSL_OPTIMISER
        , mIsOptimised(false)
        , mOptimiserEnabled(false)
#endif
    {
        if (createParamDictionary("GLSLESProgram"))
        {
            setupBaseParamDictionary();
            ParamDictionary* dict = getParamDictionary();

            dict->addParameter(ParameterDef("preprocessor_defines",
                                            "Preprocessor defines use to compile the program.",
                                            PT_STRING),&msCmdPreprocessorDefines);
#if !OGRE_NO_GLES2_GLSL_OPTIMISER
            dict->addParameter(ParameterDef("use_optimiser",
                                            "Should the GLSL optimiser be used. Default is false.",
                                            PT_BOOL),&msCmdOptimisation);
#endif
        }
        // Manually assign language now since we use it immediately
        mSyntaxCode = "glsles";

        // There is nothing to load
        mLoadFromFile = false;
    }
    //---------------------------------------------------------------------------
    GLSLESProgram::~GLSLESProgram()
    {
        // Have to call this here reather than in Resource destructor
        // since calling virtual methods in base destructors causes crash
        if (isLoaded())
        {
            unload();
        }
        else
        {
            unloadHighLevel();
        }
    }
    //---------------------------------------------------------------------------
#if OGRE_PLATFORM == OGRE_PLATFORM_ANDROID || OGRE_PLATFORM == OGRE_PLATFORM_EMSCRIPTEN
    void GLSLESProgram::notifyOnContextLost()
    {
        unloadHighLevelImpl();
    }

    void GLSLESProgram::notifyOnContextReset()
    {
        try {
            compile(true);
        }
        catch(Exception& e)
        {
            // we already compiled this once, this should not happen
            LogManager::getSingleton().stream(LML_WARNING) << e.what();
        }
    }
#endif
    GLuint GLSLESProgram::createGLProgramHandle() {
        if(!Root::getSingleton().getRenderSystem()->getCapabilities()->hasCapability(RSC_SEPARATE_SHADER_OBJECTS))
            return 0;

        if (mGLProgramHandle)
            return mGLProgramHandle;

        OGRE_CHECK_GL_ERROR(mGLProgramHandle = glCreateProgram());
        if(Root::getSingleton().getRenderSystem()->getCapabilities()->hasCapability(RSC_DEBUG))
        {
            glLabelObjectEXT(GL_PROGRAM_OBJECT_EXT, mGLProgramHandle, 0, mName.c_str());
        }

        return mGLProgramHandle;
    }

    bool GLSLESProgram::compile(bool checkErrors)
    {
        // Only create a shader object if glsl es is supported
        if (isSupported())
        {
            // Create shader object
            GLenum shaderType = 0x0000;
            if (mType == GPT_VERTEX_PROGRAM)
            {
                shaderType = GL_VERTEX_SHADER;
            }
            else if (mType == GPT_FRAGMENT_PROGRAM)
            {
                shaderType = GL_FRAGMENT_SHADER;
            }
            OGRE_CHECK_GL_ERROR(mGLShaderHandle = glCreateShader(shaderType));

            if(Root::getSingleton().getRenderSystem()->getCapabilities()->hasCapability(RSC_DEBUG))
            {
                glLabelObjectEXT(GL_SHADER_OBJECT_EXT, mGLShaderHandle, 0, mName.c_str());
            }

            createGLProgramHandle();
        }

        // Add preprocessor extras and main source
        if (!mSource.empty())
        {
            const RenderSystemCapabilities* caps = Root::getSingleton().getRenderSystem()->getCapabilities();
            // Fix up the source in case someone forgot to redeclare gl_Position
            if (caps->hasCapability(RSC_GLSL_SSO_REDECLARE) && mType == GPT_VERTEX_PROGRAM)
            {
                size_t versionPos = mSource.find("#version");
                int shaderVersion = StringConverter::parseInt(mSource.substr(versionPos+9, 3));
                size_t belowVersionPos = mSource.find('\n', versionPos) + 1;

                if(shaderVersion >= 300) {
                    // Check that it's missing and that this shader has a main function, ie. not a child shader.
                    if(mSource.find("out highp vec4 gl_Position") == String::npos)
                    {
                        mSource.insert(belowVersionPos, "out highp vec4 gl_Position;\nout highp float gl_PointSize;\n");
                    }
                    if(mSource.find("#extension GL_EXT_separate_shader_objects : require") == String::npos)
                    {
                        mSource.insert(belowVersionPos, "#extension GL_EXT_separate_shader_objects : require\n");
                    }
                }
            }

#if !OGRE_NO_GLES2_GLSL_OPTIMISER
            const char *source = (getOptimiserEnabled() && getIsOptimised()) ? mOptimisedSource.c_str() : mSource.c_str();
#else
            const char *source = mSource.c_str();
#endif

            OGRE_CHECK_GL_ERROR(glShaderSource(mGLShaderHandle, 1, &source, NULL));
        }

        OGRE_CHECK_GL_ERROR(glCompileShader(mGLShaderHandle));

        // Check for compile errors
        int compiled;
        OGRE_CHECK_GL_ERROR(glGetShaderiv(mGLShaderHandle, GL_COMPILE_STATUS, &compiled));

        if(!checkErrors)
            return compiled == 1;

        if(!compiled)
        {
            String message = GLSLES::getObjectInfo(mGLShaderHandle);
            checkAndFixInvalidDefaultPrecisionError(message);
            OGRE_CHECK_GL_ERROR(glGetShaderiv(mGLShaderHandle, GL_COMPILE_STATUS, &compiled));
        }

        String compileInfo = GLSLES::getObjectInfo(mGLShaderHandle);

        if (!compiled)
            OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, getResourceLogName() + " " + compileInfo, "compile");

        // probably we have warnings
        if (!compileInfo.empty())
            LogManager::getSingleton().stream(LML_WARNING) << getResourceLogName() << " " << compileInfo;

        return compiled == 1;
    }

#if !OGRE_NO_GLES2_GLSL_OPTIMISER
    //-----------------------------------------------------------------------
    void GLSLESProgram::setOptimiserEnabled(bool enabled)
    {
        if(mOptimiserEnabled != enabled && mOptimiserEnabled && mCompiled == 1)
        {
            OGRE_CHECK_GL_ERROR(glDeleteShader(mGLShaderHandle));

            if(Root::getSingleton().getRenderSystem()->getCapabilities()->hasCapability(RSC_SEPARATE_SHADER_OBJECTS))
            {
                OGRE_CHECK_GL_ERROR(glDeleteProgram(mGLProgramHandle));
            }

            mGLShaderHandle = 0;
            mGLProgramHandle = 0;
            mCompiled = 0;
        }
        mOptimiserEnabled = enabled;
    }
#endif
    //-----------------------------------------------------------------------
    void GLSLESProgram::createLowLevelImpl(void)
    {
    }
    //-----------------------------------------------------------------------
    void GLSLESProgram::unloadHighLevelImpl(void)
    {
        if (isSupported())
        {
//            LogManager::getSingleton().logMessage("Deleting shader " + StringConverter::toString(mGLShaderHandle) +
//                                                  " and program " + StringConverter::toString(mGLProgramHandle));
            OGRE_CHECK_GL_ERROR(glDeleteShader(mGLShaderHandle));

            if(Root::getSingleton().getRenderSystem()->getCapabilities()->hasCapability(RSC_SEPARATE_SHADER_OBJECTS))
            {
                OGRE_CHECK_GL_ERROR(glDeleteProgram(mGLProgramHandle));
            }
            // destroy all programs using this shader
            GLSLESProgramManager::getSingletonPtr()->destroyAllByShader(this);

            mGLShaderHandle = 0;
            mGLProgramHandle = 0;
            mLinked = 0;
        }
    }
    //-----------------------------------------------------------------------
    void GLSLESProgram::buildConstantDefinitions() const
    {
        // We need an accurate list of all the uniforms in the shader, but we
        // can't get at them until we link all the shaders into a program object.

        // Therefore instead, parse the source code manually and extract the uniforms
        createParameterMappingStructures(true);
        GLSLESProgramManager::getSingleton().extractUniformsFromGLSL(mSource, *mConstantDefs, mName);
    }

    //-----------------------------------------------------------------------
#if !OGRE_NO_GLES2_GLSL_OPTIMISER
    String GLSLESProgram::CmdOptimisation::doGet(const void *target) const
    {
        return StringConverter::toString(static_cast<const GLSLESProgram*>(target)->getOptimiserEnabled());
    }
    void GLSLESProgram::CmdOptimisation::doSet(void *target, const String& val)
    {
        static_cast<GLSLESProgram*>(target)->setOptimiserEnabled(StringConverter::parseBool(val));
    }
#endif
    //-----------------------------------------------------------------------
    void GLSLESProgram::attachToProgramObject( const GLuint programObject )
    {
//        LogManager::getSingleton().logMessage("Attaching shader " + StringConverter::toString(mGLShaderHandle) +
//                                              " to program " + StringConverter::toString(programObject));
        OGRE_CHECK_GL_ERROR(glAttachShader(programObject, mGLShaderHandle));
    }
    //-----------------------------------------------------------------------
    void GLSLESProgram::detachFromProgramObject( const GLuint programObject )
    {
//        LogManager::getSingleton().logMessage("Detaching shader " + StringConverter::toString(mGLShaderHandle) +
//                                              " to program " + StringConverter::toString(programObject));
        OGRE_CHECK_GL_ERROR(glDetachShader(programObject, mGLShaderHandle));
    }

    //-----------------------------------------------------------------------
    const String& GLSLESProgram::getLanguage(void) const
    {
        static const String language = "glsles";

        return language;
    }
    //-----------------------------------------------------------------------
    Ogre::GpuProgramParametersSharedPtr GLSLESProgram::createParameters( void )
    {
        GpuProgramParametersSharedPtr params = HighLevelGpuProgram::createParameters();
        params->setTransposeMatrices(true);
        return params;
    }
    //-----------------------------------------------------------------------
    void GLSLESProgram::checkAndFixInvalidDefaultPrecisionError( String &message )
    {
        String precisionQualifierErrorString = ": 'Default Precision Qualifier' : invalid type Type for default precision qualifier can be only float or int";
        std::vector< String > linesOfSource = StringUtil::split(mSource, "\n");
        if( message.find(precisionQualifierErrorString) != String::npos )
        {
            LogManager::getSingleton().logMessage("Fixing invalid type Type for default precision qualifier by deleting bad lines the re-compiling");

            // remove relevant lines from source
            std::vector< String > errors = StringUtil::split(message, "\n");

            // going from the end so when we delete a line the numbers of the lines before will not change
            for(int i = static_cast<int>(errors.size()) - 1 ; i != -1 ; i--)
            {
                String & curError = errors[i];
                size_t foundPos = curError.find(precisionQualifierErrorString);
                if(foundPos != String::npos)
                {
                    String lineNumber = curError.substr(0, foundPos);
                    size_t posOfStartOfNumber = lineNumber.find_last_of(':');
                    if (posOfStartOfNumber != String::npos)
                    {
                        lineNumber = lineNumber.substr(posOfStartOfNumber + 1, lineNumber.size() - (posOfStartOfNumber + 1));
                        if (StringConverter::isNumber(lineNumber))
                        {
                            int iLineNumber = StringConverter::parseInt(lineNumber);
                            linesOfSource.erase(linesOfSource.begin() + iLineNumber - 1);
                        }
                    }
                }
            }
            // rebuild source
            StringStream newSource;
            for(size_t i = 0; i < linesOfSource.size()  ; i++)
            {
                newSource << linesOfSource[i] << "\n";
            }
            mSource = newSource.str();

            const char *source = mSource.c_str();
            OGRE_CHECK_GL_ERROR(glShaderSource(mGLShaderHandle, 1, &source, NULL));

            if (compile())
            {
                LogManager::getSingleton().logMessage("The removing of the lines fixed the invalid type Type for default precision qualifier error.");
            }
            else
            {
                LogManager::getSingleton().logMessage("The removing of the lines didn't help.");
            }
        }
    }

    //-----------------------------------------------------------------------------
    void GLSLESProgram::bindProgram(void)
    {
        // Tell the Link Program Manager what shader is to become active
        switch (mType)
        {
            case GPT_VERTEX_PROGRAM:
                GLSLESProgramManager::getSingleton().setActiveVertexShader( this );
                break;
            case GPT_FRAGMENT_PROGRAM:
                GLSLESProgramManager::getSingleton().setActiveFragmentShader( this );
                break;
            case GPT_GEOMETRY_PROGRAM:
            default:
                break;
        }
    }

    //-----------------------------------------------------------------------------
    void GLSLESProgram::unbindProgram(void)
    {
        // Tell the Link Program Manager what shader is to become inactive
        if (mType == GPT_VERTEX_PROGRAM)
        {
            GLSLESProgramManager::getSingleton().setActiveVertexShader( NULL );
        }
        else if (mType == GPT_FRAGMENT_PROGRAM)
        {
            GLSLESProgramManager::getSingleton().setActiveFragmentShader( NULL );
        }
    }

    //-----------------------------------------------------------------------------
    void GLSLESProgram::bindProgramParameters(GpuProgramParametersSharedPtr params, uint16 mask)
    {
        // Link can throw exceptions, ignore them at this point
        try
        {
            // Activate the link program object
            GLSLESProgramCommon* linkProgram = GLSLESProgramManager::getSingleton().getActiveProgram();
            // Pass on parameters from params to program object uniforms
            linkProgram->updateUniforms(params, mask, mType);

        }
        catch (Exception& e) {}
    }

    //-----------------------------------------------------------------------------
    void GLSLESProgram::bindProgramSharedParameters(GpuProgramParametersSharedPtr params, uint16 mask)
    {
        // Link can throw exceptions, ignore them at this point
        try
        {
            // Activate the link program object
            GLSLESProgramCommon* linkProgram = GLSLESProgramManager::getSingleton().getActiveProgram();
            // Pass on parameters from params to program object uniforms
            linkProgram->updateUniformBlocks(params, mask, mType);
        }
        catch (Exception& e) {}
    }

    //-----------------------------------------------------------------------------
    void GLSLESProgram::bindProgramPassIterationParameters(GpuProgramParametersSharedPtr params)
    {
        // Activate the link program object
        GLSLESProgramCommon* linkProgram = GLSLESProgramManager::getSingleton().getActiveProgram();
        // Pass on parameters from params to program object uniforms
        linkProgram->updatePassIterationUniforms( params );
    }

    //-----------------------------------------------------------------------------
    size_t GLSLESProgram::calculateSize(void) const
    {
        size_t memSize = 0;

        // Delegate Names
        memSize += sizeof(GLuint);
        memSize += sizeof(GLenum);
        memSize += GpuProgram::calculateSize();

        return memSize;
    }
}
<commit_msg>GLES2: make Qualcomm specific workaround actually Qualcomm specific<commit_after>/*
-----------------------------------------------------------------------------
This source file is part of OGRE
    (Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2014 Torus Knot Software Ltd

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.
-----------------------------------------------------------------------------
*/
#include "OgreGLES2Prerequisites.h"
#include "OgreGpuProgram.h"
#include "OgreHighLevelGpuProgramManager.h"
#include "OgreLogManager.h"
#include "OgreRoot.h"
#include "OgreStringConverter.h"
#include "OgreGLUtil.h"
#include "OgreGLES2RenderSystem.h"
#include "OgreGLES2Support.h"

#include "OgreGLSLESProgram.h"
#include "OgreGLSLESProgramManager.h"
#include "OgreGLSLPreprocessor.h"

namespace Ogre {
    //-----------------------------------------------------------------------
#if !OGRE_NO_GLES2_GLSL_OPTIMISER
    GLSLESProgram::CmdOptimisation GLSLESProgram::msCmdOptimisation;
#endif
    //-----------------------------------------------------------------------
    //-----------------------------------------------------------------------
    GLSLESProgram::GLSLESProgram(ResourceManager* creator,
        const String& name, ResourceHandle handle,
        const String& group, bool isManual, ManualResourceLoader* loader)
        : GLSLShaderCommon(creator, name, handle, group, isManual, loader)
        , mGLShaderHandle(0)
        , mGLProgramHandle(0)
#if !OGRE_NO_GLES2_GLSL_OPTIMISER
        , mIsOptimised(false)
        , mOptimiserEnabled(false)
#endif
    {
        if (createParamDictionary("GLSLESProgram"))
        {
            setupBaseParamDictionary();
            ParamDictionary* dict = getParamDictionary();

            dict->addParameter(ParameterDef("preprocessor_defines",
                                            "Preprocessor defines use to compile the program.",
                                            PT_STRING),&msCmdPreprocessorDefines);
#if !OGRE_NO_GLES2_GLSL_OPTIMISER
            dict->addParameter(ParameterDef("use_optimiser",
                                            "Should the GLSL optimiser be used. Default is false.",
                                            PT_BOOL),&msCmdOptimisation);
#endif
        }
        // Manually assign language now since we use it immediately
        mSyntaxCode = "glsles";

        // There is nothing to load
        mLoadFromFile = false;
    }
    //---------------------------------------------------------------------------
    GLSLESProgram::~GLSLESProgram()
    {
        // Have to call this here reather than in Resource destructor
        // since calling virtual methods in base destructors causes crash
        if (isLoaded())
        {
            unload();
        }
        else
        {
            unloadHighLevel();
        }
    }
    //---------------------------------------------------------------------------
#if OGRE_PLATFORM == OGRE_PLATFORM_ANDROID || OGRE_PLATFORM == OGRE_PLATFORM_EMSCRIPTEN
    void GLSLESProgram::notifyOnContextLost()
    {
        unloadHighLevelImpl();
    }

    void GLSLESProgram::notifyOnContextReset()
    {
        try {
            compile(true);
        }
        catch(Exception& e)
        {
            // we already compiled this once, this should not happen
            LogManager::getSingleton().stream(LML_WARNING) << e.what();
        }
    }
#endif
    GLuint GLSLESProgram::createGLProgramHandle() {
        if(!Root::getSingleton().getRenderSystem()->getCapabilities()->hasCapability(RSC_SEPARATE_SHADER_OBJECTS))
            return 0;

        if (mGLProgramHandle)
            return mGLProgramHandle;

        OGRE_CHECK_GL_ERROR(mGLProgramHandle = glCreateProgram());
        if(Root::getSingleton().getRenderSystem()->getCapabilities()->hasCapability(RSC_DEBUG))
        {
            glLabelObjectEXT(GL_PROGRAM_OBJECT_EXT, mGLProgramHandle, 0, mName.c_str());
        }

        return mGLProgramHandle;
    }

    bool GLSLESProgram::compile(bool checkErrors)
    {
        // Only create a shader object if glsl es is supported
        if (isSupported())
        {
            // Create shader object
            GLenum shaderType = 0x0000;
            if (mType == GPT_VERTEX_PROGRAM)
            {
                shaderType = GL_VERTEX_SHADER;
            }
            else if (mType == GPT_FRAGMENT_PROGRAM)
            {
                shaderType = GL_FRAGMENT_SHADER;
            }
            OGRE_CHECK_GL_ERROR(mGLShaderHandle = glCreateShader(shaderType));

            if(Root::getSingleton().getRenderSystem()->getCapabilities()->hasCapability(RSC_DEBUG))
            {
                glLabelObjectEXT(GL_SHADER_OBJECT_EXT, mGLShaderHandle, 0, mName.c_str());
            }

            createGLProgramHandle();
        }

        const RenderSystemCapabilities* caps = Root::getSingleton().getRenderSystem()->getCapabilities();

        // Add preprocessor extras and main source
        if (!mSource.empty())
        {
            // Fix up the source in case someone forgot to redeclare gl_Position
            if (caps->hasCapability(RSC_GLSL_SSO_REDECLARE) && mType == GPT_VERTEX_PROGRAM)
            {
                size_t versionPos = mSource.find("#version");
                int shaderVersion = StringConverter::parseInt(mSource.substr(versionPos+9, 3));
                size_t belowVersionPos = mSource.find('\n', versionPos) + 1;

                if(shaderVersion >= 300) {
                    // Check that it's missing and that this shader has a main function, ie. not a child shader.
                    if(mSource.find("out highp vec4 gl_Position") == String::npos)
                    {
                        mSource.insert(belowVersionPos, "out highp vec4 gl_Position;\nout highp float gl_PointSize;\n");
                    }
                    if(mSource.find("#extension GL_EXT_separate_shader_objects : require") == String::npos)
                    {
                        mSource.insert(belowVersionPos, "#extension GL_EXT_separate_shader_objects : require\n");
                    }
                }
            }

#if !OGRE_NO_GLES2_GLSL_OPTIMISER
            const char *source = (getOptimiserEnabled() && getIsOptimised()) ? mOptimisedSource.c_str() : mSource.c_str();
#else
            const char *source = mSource.c_str();
#endif

            OGRE_CHECK_GL_ERROR(glShaderSource(mGLShaderHandle, 1, &source, NULL));
        }

        OGRE_CHECK_GL_ERROR(glCompileShader(mGLShaderHandle));

        // Check for compile errors
        int compiled;
        OGRE_CHECK_GL_ERROR(glGetShaderiv(mGLShaderHandle, GL_COMPILE_STATUS, &compiled));

        if(!checkErrors)
            return compiled == 1;

        if(!compiled && caps->getVendor() == GPU_QUALCOMM)
        {
            String message = GLSLES::getObjectInfo(mGLShaderHandle);
            checkAndFixInvalidDefaultPrecisionError(message);
            OGRE_CHECK_GL_ERROR(glGetShaderiv(mGLShaderHandle, GL_COMPILE_STATUS, &compiled));
        }

        String compileInfo = GLSLES::getObjectInfo(mGLShaderHandle);

        if (!compiled)
            OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, getResourceLogName() + " " + compileInfo, "compile");

        // probably we have warnings
        if (!compileInfo.empty())
            LogManager::getSingleton().stream(LML_WARNING) << getResourceLogName() << " " << compileInfo;

        return compiled == 1;
    }

#if !OGRE_NO_GLES2_GLSL_OPTIMISER
    //-----------------------------------------------------------------------
    void GLSLESProgram::setOptimiserEnabled(bool enabled)
    {
        if(mOptimiserEnabled != enabled && mOptimiserEnabled && mCompiled == 1)
        {
            OGRE_CHECK_GL_ERROR(glDeleteShader(mGLShaderHandle));

            if(Root::getSingleton().getRenderSystem()->getCapabilities()->hasCapability(RSC_SEPARATE_SHADER_OBJECTS))
            {
                OGRE_CHECK_GL_ERROR(glDeleteProgram(mGLProgramHandle));
            }

            mGLShaderHandle = 0;
            mGLProgramHandle = 0;
            mCompiled = 0;
        }
        mOptimiserEnabled = enabled;
    }
#endif
    //-----------------------------------------------------------------------
    void GLSLESProgram::createLowLevelImpl(void)
    {
    }
    //-----------------------------------------------------------------------
    void GLSLESProgram::unloadHighLevelImpl(void)
    {
        if (isSupported())
        {
//            LogManager::getSingleton().logMessage("Deleting shader " + StringConverter::toString(mGLShaderHandle) +
//                                                  " and program " + StringConverter::toString(mGLProgramHandle));
            OGRE_CHECK_GL_ERROR(glDeleteShader(mGLShaderHandle));

            if(Root::getSingleton().getRenderSystem()->getCapabilities()->hasCapability(RSC_SEPARATE_SHADER_OBJECTS))
            {
                OGRE_CHECK_GL_ERROR(glDeleteProgram(mGLProgramHandle));
            }
            // destroy all programs using this shader
            GLSLESProgramManager::getSingletonPtr()->destroyAllByShader(this);

            mGLShaderHandle = 0;
            mGLProgramHandle = 0;
            mLinked = 0;
        }
    }
    //-----------------------------------------------------------------------
    void GLSLESProgram::buildConstantDefinitions() const
    {
        // We need an accurate list of all the uniforms in the shader, but we
        // can't get at them until we link all the shaders into a program object.

        // Therefore instead, parse the source code manually and extract the uniforms
        createParameterMappingStructures(true);
        GLSLESProgramManager::getSingleton().extractUniformsFromGLSL(mSource, *mConstantDefs, mName);
    }

    //-----------------------------------------------------------------------
#if !OGRE_NO_GLES2_GLSL_OPTIMISER
    String GLSLESProgram::CmdOptimisation::doGet(const void *target) const
    {
        return StringConverter::toString(static_cast<const GLSLESProgram*>(target)->getOptimiserEnabled());
    }
    void GLSLESProgram::CmdOptimisation::doSet(void *target, const String& val)
    {
        static_cast<GLSLESProgram*>(target)->setOptimiserEnabled(StringConverter::parseBool(val));
    }
#endif
    //-----------------------------------------------------------------------
    void GLSLESProgram::attachToProgramObject( const GLuint programObject )
    {
//        LogManager::getSingleton().logMessage("Attaching shader " + StringConverter::toString(mGLShaderHandle) +
//                                              " to program " + StringConverter::toString(programObject));
        OGRE_CHECK_GL_ERROR(glAttachShader(programObject, mGLShaderHandle));
    }
    //-----------------------------------------------------------------------
    void GLSLESProgram::detachFromProgramObject( const GLuint programObject )
    {
//        LogManager::getSingleton().logMessage("Detaching shader " + StringConverter::toString(mGLShaderHandle) +
//                                              " to program " + StringConverter::toString(programObject));
        OGRE_CHECK_GL_ERROR(glDetachShader(programObject, mGLShaderHandle));
    }

    //-----------------------------------------------------------------------
    const String& GLSLESProgram::getLanguage(void) const
    {
        static const String language = "glsles";

        return language;
    }
    //-----------------------------------------------------------------------
    Ogre::GpuProgramParametersSharedPtr GLSLESProgram::createParameters( void )
    {
        GpuProgramParametersSharedPtr params = HighLevelGpuProgram::createParameters();
        params->setTransposeMatrices(true);
        return params;
    }
    //-----------------------------------------------------------------------
    void GLSLESProgram::checkAndFixInvalidDefaultPrecisionError( String &message )
    {
        String precisionQualifierErrorString = ": 'Default Precision Qualifier' : invalid type Type for default precision qualifier can be only float or int";
        std::vector< String > linesOfSource = StringUtil::split(mSource, "\n");
        if( message.find(precisionQualifierErrorString) != String::npos )
        {
            LogManager::getSingleton().logMessage("Fixing invalid type Type for default precision qualifier by deleting bad lines the re-compiling");

            // remove relevant lines from source
            std::vector< String > errors = StringUtil::split(message, "\n");

            // going from the end so when we delete a line the numbers of the lines before will not change
            for(int i = static_cast<int>(errors.size()) - 1 ; i != -1 ; i--)
            {
                String & curError = errors[i];
                size_t foundPos = curError.find(precisionQualifierErrorString);
                if(foundPos != String::npos)
                {
                    String lineNumber = curError.substr(0, foundPos);
                    size_t posOfStartOfNumber = lineNumber.find_last_of(':');
                    if (posOfStartOfNumber != String::npos)
                    {
                        lineNumber = lineNumber.substr(posOfStartOfNumber + 1, lineNumber.size() - (posOfStartOfNumber + 1));
                        if (StringConverter::isNumber(lineNumber))
                        {
                            int iLineNumber = StringConverter::parseInt(lineNumber);
                            linesOfSource.erase(linesOfSource.begin() + iLineNumber - 1);
                        }
                    }
                }
            }
            // rebuild source
            StringStream newSource;
            for(size_t i = 0; i < linesOfSource.size()  ; i++)
            {
                newSource << linesOfSource[i] << "\n";
            }
            mSource = newSource.str();

            const char *source = mSource.c_str();
            OGRE_CHECK_GL_ERROR(glShaderSource(mGLShaderHandle, 1, &source, NULL));

            if (compile())
            {
                LogManager::getSingleton().logMessage("The removing of the lines fixed the invalid type Type for default precision qualifier error.");
            }
            else
            {
                LogManager::getSingleton().logMessage("The removing of the lines didn't help.");
            }
        }
    }

    //-----------------------------------------------------------------------------
    void GLSLESProgram::bindProgram(void)
    {
        // Tell the Link Program Manager what shader is to become active
        switch (mType)
        {
            case GPT_VERTEX_PROGRAM:
                GLSLESProgramManager::getSingleton().setActiveVertexShader( this );
                break;
            case GPT_FRAGMENT_PROGRAM:
                GLSLESProgramManager::getSingleton().setActiveFragmentShader( this );
                break;
            case GPT_GEOMETRY_PROGRAM:
            default:
                break;
        }
    }

    //-----------------------------------------------------------------------------
    void GLSLESProgram::unbindProgram(void)
    {
        // Tell the Link Program Manager what shader is to become inactive
        if (mType == GPT_VERTEX_PROGRAM)
        {
            GLSLESProgramManager::getSingleton().setActiveVertexShader( NULL );
        }
        else if (mType == GPT_FRAGMENT_PROGRAM)
        {
            GLSLESProgramManager::getSingleton().setActiveFragmentShader( NULL );
        }
    }

    //-----------------------------------------------------------------------------
    void GLSLESProgram::bindProgramParameters(GpuProgramParametersSharedPtr params, uint16 mask)
    {
        // Link can throw exceptions, ignore them at this point
        try
        {
            // Activate the link program object
            GLSLESProgramCommon* linkProgram = GLSLESProgramManager::getSingleton().getActiveProgram();
            // Pass on parameters from params to program object uniforms
            linkProgram->updateUniforms(params, mask, mType);

        }
        catch (Exception& e) {}
    }

    //-----------------------------------------------------------------------------
    void GLSLESProgram::bindProgramSharedParameters(GpuProgramParametersSharedPtr params, uint16 mask)
    {
        // Link can throw exceptions, ignore them at this point
        try
        {
            // Activate the link program object
            GLSLESProgramCommon* linkProgram = GLSLESProgramManager::getSingleton().getActiveProgram();
            // Pass on parameters from params to program object uniforms
            linkProgram->updateUniformBlocks(params, mask, mType);
        }
        catch (Exception& e) {}
    }

    //-----------------------------------------------------------------------------
    void GLSLESProgram::bindProgramPassIterationParameters(GpuProgramParametersSharedPtr params)
    {
        // Activate the link program object
        GLSLESProgramCommon* linkProgram = GLSLESProgramManager::getSingleton().getActiveProgram();
        // Pass on parameters from params to program object uniforms
        linkProgram->updatePassIterationUniforms( params );
    }

    //-----------------------------------------------------------------------------
    size_t GLSLESProgram::calculateSize(void) const
    {
        size_t memSize = 0;

        // Delegate Names
        memSize += sizeof(GLuint);
        memSize += sizeof(GLenum);
        memSize += GpuProgram::calculateSize();

        return memSize;
    }
}
<|endoftext|>


<commit_before>// Copyright Microsoft Corporation
// Copyright GHI Electronics, LLC
//
// 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 "LPC24.h"

#define AD0CR (*(volatile unsigned *)0xE0034000)

#define LPC24xx_ADC_DataRegisterShiftBits	4
#define LPC24xx_ADC_BitRegisterMask			0xFFF

#define ADC_DATA_BASE_ADDRESS	0xE0034010
#define ADC_GLOBAR_DATA_ADDRESS 0xE0034004

static TinyCLR_Adc_Provider adcProvider;
static TinyCLR_Api_Info adcApi;

uint32_t g_lpc24_adc_isOpened;

const TinyCLR_Api_Info* LPC24_Adc_GetApi() {
    adcProvider.Parent = &adcApi;
    adcProvider.Index = 0;
    adcProvider.Acquire = &LPC24_Adc_Acquire;
    adcProvider.Release = &LPC24_Adc_Release;
    adcProvider.AcquireChannel = &LPC24_Adc_AcquireChannel;
    adcProvider.ReleaseChannel = &LPC24_Adc_ReleaseChannel;
    adcProvider.IsChannelModeSupported = &LPC24_Adc_IsChannelModeSupported;
    adcProvider.ReadValue = &LPC24_Adc_ReadValue;
    adcProvider.GetMinValue = &LPC24_Adc_GetMinValue;
    adcProvider.GetMaxValue = &LPC24_Adc_GetMaxValue;
    adcProvider.GetResolutionInBits = &LPC24_Adc_GetResolutionInBits;
    adcProvider.GetChannelCount = &LPC24_Adc_GetChannelCount;
    adcProvider.GetChannelMode = &LPC24_Adc_GetChannelMode;
    adcProvider.SetChannelMode = &LPC24_Adc_SetChannelMode;

    adcApi.Author = "GHI Electronics, LLC";
    adcApi.Name = "GHIElectronics.TinyCLR.NativeApis.LPC24.AdcProvider";
    adcApi.Type = TinyCLR_Api_Type::AdcProvider;
    adcApi.Version = 0;
    adcApi.Count = 1;
    adcApi.Implementation = &adcProvider;

    return &adcApi;
}

TinyCLR_Result LPC24_Adc_Acquire(const TinyCLR_Adc_Provider* self) {
    if (self == nullptr)
        return TinyCLR_Result::ArgumentNull;

    return TinyCLR_Result::Success;
}

TinyCLR_Result LPC24_Adc_Release(const TinyCLR_Adc_Provider* self) {
    if (self == nullptr)
        return TinyCLR_Result::ArgumentNull;

    return TinyCLR_Result::Success;
}

int32_t LPC24_Adc_GetPinForChannel(int32_t channel) {
    if ((uint32_t)channel >= LPC24_Adc_GetControllerCount())
        return PIN_NONE;

    return LPC24_Adc_GetPin(channel);
}

TinyCLR_Result LPC24_Adc_AcquireChannel(const TinyCLR_Adc_Provider* self, int32_t channel) {
    if (channel >= LPC24_Adc_GetControllerCount())
        return TinyCLR_Result::ArgumentOutOfRange;

    if (LPC24_Adc_GetPin(channel) == PIN_NONE)
        return TinyCLR_Result::ArgumentInvalid;

    LPC24XX::SYSCON().PCONP |= PCONP_PCAD;

    LPC24_Gpio_ConfigurePin(LPC24_Adc_GetPin(channel), LPC24_Gpio_Direction::Input, LPC24_Adc_GetPinFunction(channel), LPC24_Gpio_PinMode::Inactive);

    AD0CR |= (1 << channel) |// sample one of the pins
        ((3 - 1) << 8) |//devide the clock by 14 60/14= 4.3 (must be <4.5)
        (1 << 16) |//burst mode
        (0 << 17) |//10 bits
        (1 << 21);//operational

    g_lpc24_adc_isOpened |= (1 << channel);

    return TinyCLR_Result::Success;
}

TinyCLR_Result LPC24_Adc_ReleaseChannel(const TinyCLR_Adc_Provider* self, int32_t channel) {
    if (g_lpc24_adc_isOpened & (1 << channel))
        LPC24_Gpio_ConfigurePin(LPC24_Adc_GetPin(channel), LPC24_Gpio_Direction::Input, LPC24_Gpio_PinFunction::PinFunction0, LPC24_Gpio_PinMode::Inactive);

    g_lpc24_adc_isOpened &= ~(1 << channel);

    return TinyCLR_Result::Success;
}

TinyCLR_Result LPC24_Adc_ReadValue(const TinyCLR_Adc_Provider* self, int32_t channel, int32_t& value) {
    uint32_t result = 0;

    if (channel >= LPC24_Adc_GetControllerCount())
        return TinyCLR_Result::ArgumentOutOfRange;

    value = 0;

    // get the values
    for (auto i = 0; i < 5; i++) {
        LPC24_Time_Delay(nullptr, 5);

        result = ((*((uint32_t*)(ADC_DATA_BASE_ADDRESS)+channel)) >> 6) & 0x3FF;

        value += result;
    }

    value /= 5;

    // channel not available
    return TinyCLR_Result::Success;
}

int32_t LPC24_Adc_GetChannelCount(const TinyCLR_Adc_Provider* self) {
    return LPC24_Adc_GetControllerCount();
}

int32_t LPC24_Adc_GetResolutionInBits(const TinyCLR_Adc_Provider* self) {
    return 10;
}

int32_t LPC24_Adc_GetMinValue(const TinyCLR_Adc_Provider* self) {
    return 0;
}

int32_t LPC24_Adc_GetMaxValue(const TinyCLR_Adc_Provider* self) {
    return (1 << LPC24_Adc_GetResolutionInBits(self)) - 1;
}

TinyCLR_Adc_ChannelMode LPC24_Adc_GetChannelMode(const TinyCLR_Adc_Provider* self) {
    return TinyCLR_Adc_ChannelMode::SingleEnded;
}

TinyCLR_Result LPC24_Adc_SetChannelMode(const TinyCLR_Adc_Provider* self, TinyCLR_Adc_ChannelMode mode) {
    return mode == TinyCLR_Adc_ChannelMode::SingleEnded ? TinyCLR_Result::Success : TinyCLR_Result::NotSupported;
}

bool LPC24_Adc_IsChannelModeSupported(const TinyCLR_Adc_Provider* self, TinyCLR_Adc_ChannelMode mode) {
    return mode == TinyCLR_Adc_ChannelMode::SingleEnded;
}

void LPC24_Adc_Reset() {
    for (auto ch = 0; ch < LPC24_Adc_GetControllerCount(); ch++) {
        LPC24_Adc_ReleaseChannel(&adcProvider, ch);
    }

    g_lpc24_adc_isOpened = 0;

    LPC24XX::SYSCON().PCONP &= ~(PCONP_PCAD);
}
<commit_msg>Make sure Open and Close peripheral pins are in Acquired and Release<commit_after>// Copyright Microsoft Corporation
// Copyright GHI Electronics, LLC
//
// 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 "LPC24.h"

#define AD0CR (*(volatile unsigned *)0xE0034000)

#define LPC24xx_ADC_DataRegisterShiftBits	4
#define LPC24xx_ADC_BitRegisterMask			0xFFF

#define ADC_DATA_BASE_ADDRESS	0xE0034010
#define ADC_GLOBAR_DATA_ADDRESS 0xE0034004

static TinyCLR_Adc_Provider adcProvider;
static TinyCLR_Api_Info adcApi;

uint32_t g_lpc24_adc_isOpened;

const TinyCLR_Api_Info* LPC24_Adc_GetApi() {
    adcProvider.Parent = &adcApi;
    adcProvider.Index = 0;
    adcProvider.Acquire = &LPC24_Adc_Acquire;
    adcProvider.Release = &LPC24_Adc_Release;
    adcProvider.AcquireChannel = &LPC24_Adc_AcquireChannel;
    adcProvider.ReleaseChannel = &LPC24_Adc_ReleaseChannel;
    adcProvider.IsChannelModeSupported = &LPC24_Adc_IsChannelModeSupported;
    adcProvider.ReadValue = &LPC24_Adc_ReadValue;
    adcProvider.GetMinValue = &LPC24_Adc_GetMinValue;
    adcProvider.GetMaxValue = &LPC24_Adc_GetMaxValue;
    adcProvider.GetResolutionInBits = &LPC24_Adc_GetResolutionInBits;
    adcProvider.GetChannelCount = &LPC24_Adc_GetChannelCount;
    adcProvider.GetChannelMode = &LPC24_Adc_GetChannelMode;
    adcProvider.SetChannelMode = &LPC24_Adc_SetChannelMode;

    adcApi.Author = "GHI Electronics, LLC";
    adcApi.Name = "GHIElectronics.TinyCLR.NativeApis.LPC24.AdcProvider";
    adcApi.Type = TinyCLR_Api_Type::AdcProvider;
    adcApi.Version = 0;
    adcApi.Count = 1;
    adcApi.Implementation = &adcProvider;

    return &adcApi;
}

TinyCLR_Result LPC24_Adc_Acquire(const TinyCLR_Adc_Provider* self) {
    if (self == nullptr)
        return TinyCLR_Result::ArgumentNull;

    return TinyCLR_Result::Success;
}

TinyCLR_Result LPC24_Adc_Release(const TinyCLR_Adc_Provider* self) {
    if (self == nullptr)
        return TinyCLR_Result::ArgumentNull;

    return TinyCLR_Result::Success;
}

int32_t LPC24_Adc_GetPinForChannel(int32_t channel) {
    if ((uint32_t)channel >= LPC24_Adc_GetControllerCount())
        return PIN_NONE;

    return LPC24_Adc_GetPin(channel);
}

TinyCLR_Result LPC24_Adc_AcquireChannel(const TinyCLR_Adc_Provider* self, int32_t channel) {
    if (channel >= LPC24_Adc_GetControllerCount())
        return TinyCLR_Result::ArgumentOutOfRange;

    if (LPC24_Adc_GetPin(channel) == PIN_NONE)
        return TinyCLR_Result::ArgumentInvalid;

    if (!LPC24_Gpio_OpenPin(LPC24_Adc_GetPin(channel)))
        return  TinyCLR_Result::SharingViolation;

    LPC24XX::SYSCON().PCONP |= PCONP_PCAD;

    LPC24_Gpio_ConfigurePin(LPC24_Adc_GetPin(channel), LPC24_Gpio_Direction::Input, LPC24_Adc_GetPinFunction(channel), LPC24_Gpio_PinMode::Inactive);

    AD0CR |= (1 << channel) |// sample one of the pins
        ((3 - 1) << 8) |//devide the clock by 14 60/14= 4.3 (must be <4.5)
        (1 << 16) |//burst mode
        (0 << 17) |//10 bits
        (1 << 21);//operational

    g_lpc24_adc_isOpened |= (1 << channel);

    return TinyCLR_Result::Success;
}

TinyCLR_Result LPC24_Adc_ReleaseChannel(const TinyCLR_Adc_Provider* self, int32_t channel) {
    if (g_lpc24_adc_isOpened & (1 << channel)) {
        LPC24_Gpio_ClosePin(LPC24_Adc_GetPin(channel));

    }

    g_lpc24_adc_isOpened &= ~(1 << channel);

    return TinyCLR_Result::Success;
}

TinyCLR_Result LPC24_Adc_ReadValue(const TinyCLR_Adc_Provider* self, int32_t channel, int32_t& value) {
    uint32_t result = 0;

    if (channel >= LPC24_Adc_GetControllerCount())
        return TinyCLR_Result::ArgumentOutOfRange;

    value = 0;

    // get the values
    for (auto i = 0; i < 5; i++) {
        LPC24_Time_Delay(nullptr, 5);

        result = ((*((uint32_t*)(ADC_DATA_BASE_ADDRESS)+channel)) >> 6) & 0x3FF;

        value += result;
    }

    value /= 5;

    // channel not available
    return TinyCLR_Result::Success;
}

int32_t LPC24_Adc_GetChannelCount(const TinyCLR_Adc_Provider* self) {
    return LPC24_Adc_GetControllerCount();
}

int32_t LPC24_Adc_GetResolutionInBits(const TinyCLR_Adc_Provider* self) {
    return 10;
}

int32_t LPC24_Adc_GetMinValue(const TinyCLR_Adc_Provider* self) {
    return 0;
}

int32_t LPC24_Adc_GetMaxValue(const TinyCLR_Adc_Provider* self) {
    return (1 << LPC24_Adc_GetResolutionInBits(self)) - 1;
}

TinyCLR_Adc_ChannelMode LPC24_Adc_GetChannelMode(const TinyCLR_Adc_Provider* self) {
    return TinyCLR_Adc_ChannelMode::SingleEnded;
}

TinyCLR_Result LPC24_Adc_SetChannelMode(const TinyCLR_Adc_Provider* self, TinyCLR_Adc_ChannelMode mode) {
    return mode == TinyCLR_Adc_ChannelMode::SingleEnded ? TinyCLR_Result::Success : TinyCLR_Result::NotSupported;
}

bool LPC24_Adc_IsChannelModeSupported(const TinyCLR_Adc_Provider* self, TinyCLR_Adc_ChannelMode mode) {
    return mode == TinyCLR_Adc_ChannelMode::SingleEnded;
}

void LPC24_Adc_Reset() {
    for (auto ch = 0; ch < LPC24_Adc_GetControllerCount(); ch++) {
        LPC24_Adc_ReleaseChannel(&adcProvider, ch);
    }

    g_lpc24_adc_isOpened = 0;

    LPC24XX::SYSCON().PCONP &= ~(PCONP_PCAD);
}
<|endoftext|>


<commit_before>/******************************************************************************\

          This file is part of the C! library.  A.K.A the cbang library.

              Copyright (c) 2003-2015, Cauldron Development LLC
                 Copyright (c) 2003-2015, Stanford University
                             All rights reserved.

        The C! library is free software: you can redistribute it and/or
        modify it under the terms of the GNU Lesser General Public License
        as published by the Free Software Foundation, either version 2.1 of
        the License, or (at your option) any later version.

        The C! library is distributed in the hope that it will be useful,
        but WITHOUT ANY WARRANTY; without even the implied warranty of
        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
        Lesser General Public License for more details.

        You should have received a copy of the GNU Lesser General Public
        License along with the C! library.  If not, see
        <http://www.gnu.org/licenses/>.

        In addition, BSD licensing may be granted on a case by case basis
        by written permission from at least one of the copyright holders.
        You may request written permission by emailing the authors.

                For information regarding this software email:
                               Joseph Coffland
                        joseph@cauldrondevelopment.com

\******************************************************************************/

#include "Script.h"
#include "ContextScope.h"

#include <cbang/log/Logger.h>

using namespace std;
using namespace cb;
using namespace cb::js;

Script::Script(Context &context, const string &s, const string &filename) :
  context(context) {
  load(s, filename);
}

Script::Script(Context &context, const InputSource &source) : context(context) {
  load(source.toString(), source.getName());
}

Script::~Script() {}

Value Script::eval() {
  v8::HandleScope handleScope;

  ContextScope contextScope(context);

  v8::TryCatch tryCatch;
  v8::Handle<v8::Value> ret = script->Run();
  if (tryCatch.HasCaught()) translateException(tryCatch);

  return handleScope.Close(ret);
}

void Script::translateException(const v8::TryCatch &tryCatch) {
  v8::HandleScope handleScope;

  string msg;

  if (Exception::enableStackTraces && !tryCatch.StackTrace().IsEmpty())
    msg = Value(tryCatch.StackTrace()).toString();
  else msg = Value(tryCatch.Exception()).toString();

  v8::Handle<v8::Message> message = tryCatch.Message();
  if (message.IsEmpty()) THROW(msg);

  string filename = Value(message->GetScriptResourceName()).toString();
  int line = message->GetLineNumber();
  int col = message->GetStartColumn();

  throw Exception(msg, FileLocation(filename, line, col));
}

void Script::load(const string &s, const string &filename) {
  v8::HandleScope handleScope;
  v8::Local<v8::String> source = v8::String::New(s.c_str(), s.length());
  v8::Local<v8::String> origin;

  if (!filename.empty())
    origin = v8::String::New(filename.c_str(), filename.length());

  ContextScope contextScope(context);

  v8::TryCatch tryCatch;
  script = v8::Script::Compile(source, origin);
  if (tryCatch.HasCaught()) translateException(tryCatch);

  script = handleScope.Close(script);
}
<commit_msg>Cleaner stack traces<commit_after>/******************************************************************************\

          This file is part of the C! library.  A.K.A the cbang library.

              Copyright (c) 2003-2015, Cauldron Development LLC
                 Copyright (c) 2003-2015, Stanford University
                             All rights reserved.

        The C! library is free software: you can redistribute it and/or
        modify it under the terms of the GNU Lesser General Public License
        as published by the Free Software Foundation, either version 2.1 of
        the License, or (at your option) any later version.

        The C! library is distributed in the hope that it will be useful,
        but WITHOUT ANY WARRANTY; without even the implied warranty of
        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
        Lesser General Public License for more details.

        You should have received a copy of the GNU Lesser General Public
        License along with the C! library.  If not, see
        <http://www.gnu.org/licenses/>.

        In addition, BSD licensing may be granted on a case by case basis
        by written permission from at least one of the copyright holders.
        You may request written permission by emailing the authors.

                For information regarding this software email:
                               Joseph Coffland
                        joseph@cauldrondevelopment.com

\******************************************************************************/

#include "Script.h"
#include "ContextScope.h"

#include <cbang/log/Logger.h>

using namespace std;
using namespace cb;
using namespace cb::js;

Script::Script(Context &context, const string &s, const string &filename) :
  context(context) {
  load(s, filename);
}

Script::Script(Context &context, const InputSource &source) : context(context) {
  load(source.toString(), source.getName());
}

Script::~Script() {}

Value Script::eval() {
  v8::HandleScope handleScope;

  ContextScope contextScope(context);

  v8::TryCatch tryCatch;
  v8::Handle<v8::Value> ret = script->Run();
  if (tryCatch.HasCaught()) translateException(tryCatch);

  return handleScope.Close(ret);
}

void Script::translateException(const v8::TryCatch &tryCatch) {
  v8::HandleScope handleScope;

  if (!tryCatch.StackTrace().IsEmpty())
    throw Exception(Value(tryCatch.StackTrace()).toString());

  string msg = Value(tryCatch.Exception()).toString();

  v8::Handle<v8::Message> message = tryCatch.Message();
  if (message.IsEmpty()) throw Exception(msg);

  string filename = Value(message->GetScriptResourceName()).toString();
  int line = message->GetLineNumber();
  int col = message->GetStartColumn();

  throw Exception(msg, FileLocation(filename, line, col));
}

void Script::load(const string &s, const string &filename) {
  v8::HandleScope handleScope;
  v8::Local<v8::String> source = v8::String::New(s.c_str(), s.length());
  v8::Local<v8::String> origin;

  if (!filename.empty())
    origin = v8::String::New(filename.c_str(), filename.length());

  ContextScope contextScope(context);

  v8::TryCatch tryCatch;
  script = v8::Script::Compile(source, origin);
  if (tryCatch.HasCaught()) translateException(tryCatch);

  script = handleScope.Close(script);
}
<|endoftext|>


<commit_before>//---------------------------------------------------------- -*- Mode: C++ -*-
// $Id$
//
// Created 2013/06/08
// Author: Mike Ovsiannikov
//
// Copyright 2013 Quantcast Corp.
//
// This file is part of Kosmos File System (KFS).
//
// 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.
//
// Kerberos 5 client side authentication implementation.
//
//----------------------------------------------------------------------------

#include "KrbClient.h"

#include <krb5/krb5.h>

#include <string.h>
#include <string>
#include <algorithm>

namespace KFS
{

using std::string;
using std::max;

class KrbClient::Impl
{
public:
    Impl()
        : mServiceHost(),
          mCtx(),
          mAuthCtx(),
          mErrCode(0),
          mOutBuf(),
          mCreds(),
          mServerPtr(0),
          mKeyBlockPtr(0),
          mInitedFlag(false),
          mServiceName(),
          mErrorMsg()
    {
        mOutBuf.data   = 0;
        mOutBuf.length = 0;
    }
    ~Impl()
        {}
    const char* Init(
        const char* inServiceHostNamePtr,
        const char* inServeiceNamePtr)
    {
        CleanupSelf();
        mServiceHost = inServiceHostNamePtr ? inServiceHostNamePtr : "";
        mServiceName = inServeiceNamePtr    ? inServeiceNamePtr    : "";
        mErrCode     = 0;
        mErrorMsg.clear();
        InitSelf();
        if (mErrCode) {
            mErrorMsg = ErrToStr(mErrCode);
            return mErrorMsg.c_str();
        }
        return 0;
    }
    const char* Cleanup()
    {
        mErrorMsg.clear();
        mErrCode = CleanupSelf();
        if (mErrCode) {
            return ErrStr();
        }
        return 0;
    }
    const char* Request(
        const char*& outDataPtr,
        int&         outDataLen)
    {
        if (! mInitedFlag) {
            mErrCode  = KRB5_CONFIG_BADFORMAT;
            mErrorMsg = "not initialized yet, invoke KrbClient::Init() first";
            return mErrorMsg.c_str();
        }
        CleanupAuth();
        krb5_free_data_contents(mCtx, &mOutBuf);
        krb5_ccache theCache = 0;
	if ((mErrCode = krb5_cc_default(mCtx, &theCache)) != 0) {
            return ErrStr();
        }
        mCreds.server = mServerPtr;
	if ((mErrCode = krb5_cc_get_principal(
                mCtx, theCache, &mCreds.client)) != 0) {
            return ErrStr();
        }
        krb5_creds* theCreds = 0;
	if ((mErrCode = krb5_get_credentials(
                mCtx, 0, theCache, &mCreds, &theCreds)) != 0) {
            return ErrStr();
        }
        krb5_data theAppData = { 0 };
        theAppData.data   = 0;
        theAppData.length = 0;
        if ((mErrCode = krb5_mk_req_extended(
                    mCtx,
                    &mAuthCtx,
                    AP_OPTS_MUTUAL_REQUIRED,
                    &theAppData,
                    theCreds,
                    &mOutBuf)) != 0) {
            return ErrStr();
        }
        outDataPtr = (const char*)mOutBuf.data;
        outDataLen = (int)mOutBuf.length;
        return 0;
    }
    const char* Reply(
        const char*  inReplyPtr,
        int          inReplyLen,
        const char*& outSessionKeyPtr,
        int&         outSessionKeyLen)
    {
        if (! mInitedFlag) {
            mErrCode  = KRB5_CONFIG_BADFORMAT;
            mErrorMsg = "not initialized yet, invoke KrbClient::Init";
            return mErrorMsg.c_str();
        }
        if (mOutBuf.length <= 0 || ! mOutBuf.data) {
            mErrCode  = KRB5_CONFIG_BADFORMAT;
            mErrorMsg = "not ready to process reply, invoke KrbClient::Request";
            return mErrorMsg.c_str();
        }
        if (mKeyBlockPtr) {
            mErrCode  = KRB5_CONFIG_BADFORMAT;
            mErrorMsg = "possible extraneous invocation of KrbClient::Reply";
            return mErrorMsg.c_str();
        }
        krb5_data theData = { 0 };
        theData.length = max(0, inReplyLen);
        theData.data   = const_cast<char*>(inReplyPtr);
        krb5_ap_rep_enc_part* theReplPtr = 0;
        if ((mErrCode = krb5_rd_rep(
                mCtx,
                mAuthCtx,
                &theData,
                &theReplPtr))) {
            return ErrStr();
        }
        if ((mErrCode = krb5_auth_con_getkey(mCtx, mAuthCtx, &mKeyBlockPtr))) {
            return ErrStr();
        }
        krb5_free_ap_rep_enc_part(mCtx, theReplPtr);
        krb5_free_data_contents(mCtx, &mOutBuf);
        outSessionKeyPtr = (const char*)mKeyBlockPtr->contents;
        outSessionKeyLen = (int)mKeyBlockPtr->length;
        return 0;
    }
    int GetErrorCode() const
    {
        return mErrCode;
    }
private:
    string            mServiceHost;
    krb5_context      mCtx;
    krb5_auth_context mAuthCtx;
    krb5_error_code   mErrCode;
    krb5_data         mOutBuf;
    krb5_creds        mCreds;
    krb5_principal    mServerPtr;
    krb5_keyblock*    mKeyBlockPtr;
    bool              mInitedFlag;
    string            mServiceName;
    string            mErrorMsg;

    void InitSelf()
    {
        mErrCode = krb5_init_context(&mCtx);
        if (mErrCode) {
            return;
        }
        mInitedFlag = true;
        memset(&mCreds, 0, sizeof(mCreds));
	if ((mErrCode = krb5_sname_to_principal(
                mCtx,
                mServiceHost.c_str(),
                mServiceName.c_str(),
                KRB5_NT_UNKNOWN, // KRB5_NT_SRV_HST,
                &mServerPtr
            ))) {
            return;
        }
    }
    krb5_error_code CleanupSelf()
    {
        if (! mInitedFlag) {
            return 0;
        }
        krb5_error_code theErr = CleanupAuth();
        mInitedFlag = false;
        memset(&mCreds, 0, sizeof(mCreds));
	if (mServerPtr) {
            krb5_free_principal(mCtx, mServerPtr);
            mServerPtr = 0;
        }
        krb5_free_context(mCtx);
        return theErr;
    }
    krb5_error_code CleanupAuth()
    {
        if (! mInitedFlag) {
            return 0;
        }
	if (mCreds.client) {
            krb5_free_principal(mCtx, mCreds.client);
            mCreds.client = 0;
        }
        if (mKeyBlockPtr) {
            krb5_free_keyblock(mCtx, mKeyBlockPtr);
            mKeyBlockPtr = 0;
        }
        krb5_free_data_contents(mCtx, &mOutBuf);
        if (! mAuthCtx) {
            return 0;
        }
        const krb5_error_code theErr = krb5_auth_con_free(mCtx, mAuthCtx);
        memset(&mCreds, 0, sizeof(mCreds));
        mAuthCtx = 0;
        return theErr;
    }
    const char* ErrStr()
    {
        mErrorMsg = ErrToStr(mErrCode);
        return mErrorMsg.c_str();
    }
    string ErrToStr(
        krb5_error_code inErrCode) const
    {
        if (! inErrCode) {
            return string();
        }
        if ( ! mCtx) {
            return string("no kerberos context");
        }
        const char* const theMsgPtr = krb5_get_error_message(mCtx, inErrCode);
        return string(theMsgPtr);
    }

private:
    Impl(
        const Impl& inImpl);
    Impl& operator=(
        const Impl& inImpl);
};

KrbClient::KrbClient()
    : mImpl(*(new Impl()))
{
}

KrbClient::~KrbClient()
{
    delete &mImpl;
}

    const char*
KrbClient::Init(
    const char* inServiceHostNamePtr,
    const char* inServeiceNamePtr)
{
    return mImpl.Init(inServiceHostNamePtr, inServeiceNamePtr);
}

    const char*
KrbClient::Cleanup()
{
    return mImpl.Cleanup();
}

    const char*
KrbClient::Request(
    const char*& outDataPtr,
    int&         outDataLen)
{
    return mImpl.Request(outDataPtr, outDataLen);
}

    const char*
KrbClient::Reply(
    const char*  inReplyPtr,
    int          inReplyLen,
    const char*& outSessionKeyPtr,
    int&         outSessionKeyLen)
{
    return mImpl.Reply(
        inReplyPtr, inReplyLen, outSessionKeyPtr, outSessionKeyLen);
}

    int
KrbClient::GetErrorCode() const
{
    return mImpl.GetErrorCode();
}

}
<commit_msg>Qfs kerberos lib: fix error message.<commit_after>//---------------------------------------------------------- -*- Mode: C++ -*-
// $Id$
//
// Created 2013/06/08
// Author: Mike Ovsiannikov
//
// Copyright 2013 Quantcast Corp.
//
// This file is part of Kosmos File System (KFS).
//
// 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.
//
// Kerberos 5 client side authentication implementation.
//
//----------------------------------------------------------------------------

#include "KrbClient.h"

#include <krb5/krb5.h>

#include <string.h>
#include <string>
#include <algorithm>

namespace KFS
{

using std::string;
using std::max;

class KrbClient::Impl
{
public:
    Impl()
        : mServiceHost(),
          mCtx(),
          mAuthCtx(),
          mErrCode(0),
          mOutBuf(),
          mCreds(),
          mServerPtr(0),
          mKeyBlockPtr(0),
          mInitedFlag(false),
          mServiceName(),
          mErrorMsg()
    {
        mOutBuf.data   = 0;
        mOutBuf.length = 0;
    }
    ~Impl()
        {}
    const char* Init(
        const char* inServiceHostNamePtr,
        const char* inServeiceNamePtr)
    {
        CleanupSelf();
        mServiceHost = inServiceHostNamePtr ? inServiceHostNamePtr : "";
        mServiceName = inServeiceNamePtr    ? inServeiceNamePtr    : "";
        mErrCode     = 0;
        mErrorMsg.clear();
        InitSelf();
        if (mErrCode) {
            mErrorMsg = ErrToStr(mErrCode);
            return mErrorMsg.c_str();
        }
        return 0;
    }
    const char* Cleanup()
    {
        mErrorMsg.clear();
        mErrCode = CleanupSelf();
        if (mErrCode) {
            return ErrStr();
        }
        return 0;
    }
    const char* Request(
        const char*& outDataPtr,
        int&         outDataLen)
    {
        if (! mInitedFlag) {
            mErrCode  = KRB5_CONFIG_BADFORMAT;
            mErrorMsg = "not initialized yet, invoke KrbClient::Init";
            return mErrorMsg.c_str();
        }
        CleanupAuth();
        krb5_free_data_contents(mCtx, &mOutBuf);
        krb5_ccache theCache = 0;
	if ((mErrCode = krb5_cc_default(mCtx, &theCache)) != 0) {
            return ErrStr();
        }
        mCreds.server = mServerPtr;
	if ((mErrCode = krb5_cc_get_principal(
                mCtx, theCache, &mCreds.client)) != 0) {
            return ErrStr();
        }
        krb5_creds* theCreds = 0;
	if ((mErrCode = krb5_get_credentials(
                mCtx, 0, theCache, &mCreds, &theCreds)) != 0) {
            return ErrStr();
        }
        krb5_data theAppData = { 0 };
        theAppData.data   = 0;
        theAppData.length = 0;
        if ((mErrCode = krb5_mk_req_extended(
                    mCtx,
                    &mAuthCtx,
                    AP_OPTS_MUTUAL_REQUIRED,
                    &theAppData,
                    theCreds,
                    &mOutBuf)) != 0) {
            return ErrStr();
        }
        outDataPtr = (const char*)mOutBuf.data;
        outDataLen = (int)mOutBuf.length;
        return 0;
    }
    const char* Reply(
        const char*  inReplyPtr,
        int          inReplyLen,
        const char*& outSessionKeyPtr,
        int&         outSessionKeyLen)
    {
        if (! mInitedFlag) {
            mErrCode  = KRB5_CONFIG_BADFORMAT;
            mErrorMsg = "not initialized yet, invoke KrbClient::Init";
            return mErrorMsg.c_str();
        }
        if (mOutBuf.length <= 0 || ! mOutBuf.data) {
            mErrCode  = KRB5_CONFIG_BADFORMAT;
            mErrorMsg = "not ready to process reply, invoke KrbClient::Request";
            return mErrorMsg.c_str();
        }
        if (mKeyBlockPtr) {
            mErrCode  = KRB5_CONFIG_BADFORMAT;
            mErrorMsg = "possible extraneous invocation of KrbClient::Reply";
            return mErrorMsg.c_str();
        }
        krb5_data theData = { 0 };
        theData.length = max(0, inReplyLen);
        theData.data   = const_cast<char*>(inReplyPtr);
        krb5_ap_rep_enc_part* theReplPtr = 0;
        if ((mErrCode = krb5_rd_rep(
                mCtx,
                mAuthCtx,
                &theData,
                &theReplPtr))) {
            return ErrStr();
        }
        if ((mErrCode = krb5_auth_con_getkey(mCtx, mAuthCtx, &mKeyBlockPtr))) {
            return ErrStr();
        }
        krb5_free_ap_rep_enc_part(mCtx, theReplPtr);
        krb5_free_data_contents(mCtx, &mOutBuf);
        outSessionKeyPtr = (const char*)mKeyBlockPtr->contents;
        outSessionKeyLen = (int)mKeyBlockPtr->length;
        return 0;
    }
    int GetErrorCode() const
    {
        return mErrCode;
    }
private:
    string            mServiceHost;
    krb5_context      mCtx;
    krb5_auth_context mAuthCtx;
    krb5_error_code   mErrCode;
    krb5_data         mOutBuf;
    krb5_creds        mCreds;
    krb5_principal    mServerPtr;
    krb5_keyblock*    mKeyBlockPtr;
    bool              mInitedFlag;
    string            mServiceName;
    string            mErrorMsg;

    void InitSelf()
    {
        mErrCode = krb5_init_context(&mCtx);
        if (mErrCode) {
            return;
        }
        mInitedFlag = true;
        memset(&mCreds, 0, sizeof(mCreds));
	if ((mErrCode = krb5_sname_to_principal(
                mCtx,
                mServiceHost.c_str(),
                mServiceName.c_str(),
                KRB5_NT_UNKNOWN, // KRB5_NT_SRV_HST,
                &mServerPtr
            ))) {
            return;
        }
    }
    krb5_error_code CleanupSelf()
    {
        if (! mInitedFlag) {
            return 0;
        }
        krb5_error_code theErr = CleanupAuth();
        mInitedFlag = false;
        memset(&mCreds, 0, sizeof(mCreds));
	if (mServerPtr) {
            krb5_free_principal(mCtx, mServerPtr);
            mServerPtr = 0;
        }
        krb5_free_context(mCtx);
        return theErr;
    }
    krb5_error_code CleanupAuth()
    {
        if (! mInitedFlag) {
            return 0;
        }
	if (mCreds.client) {
            krb5_free_principal(mCtx, mCreds.client);
            mCreds.client = 0;
        }
        if (mKeyBlockPtr) {
            krb5_free_keyblock(mCtx, mKeyBlockPtr);
            mKeyBlockPtr = 0;
        }
        krb5_free_data_contents(mCtx, &mOutBuf);
        if (! mAuthCtx) {
            return 0;
        }
        const krb5_error_code theErr = krb5_auth_con_free(mCtx, mAuthCtx);
        memset(&mCreds, 0, sizeof(mCreds));
        mAuthCtx = 0;
        return theErr;
    }
    const char* ErrStr()
    {
        mErrorMsg = ErrToStr(mErrCode);
        return mErrorMsg.c_str();
    }
    string ErrToStr(
        krb5_error_code inErrCode) const
    {
        if (! inErrCode) {
            return string();
        }
        if ( ! mCtx) {
            return string("no kerberos context");
        }
        const char* const theMsgPtr = krb5_get_error_message(mCtx, inErrCode);
        return string(theMsgPtr);
    }

private:
    Impl(
        const Impl& inImpl);
    Impl& operator=(
        const Impl& inImpl);
};

KrbClient::KrbClient()
    : mImpl(*(new Impl()))
{
}

KrbClient::~KrbClient()
{
    delete &mImpl;
}

    const char*
KrbClient::Init(
    const char* inServiceHostNamePtr,
    const char* inServeiceNamePtr)
{
    return mImpl.Init(inServiceHostNamePtr, inServeiceNamePtr);
}

    const char*
KrbClient::Cleanup()
{
    return mImpl.Cleanup();
}

    const char*
KrbClient::Request(
    const char*& outDataPtr,
    int&         outDataLen)
{
    return mImpl.Request(outDataPtr, outDataLen);
}

    const char*
KrbClient::Reply(
    const char*  inReplyPtr,
    int          inReplyLen,
    const char*& outSessionKeyPtr,
    int&         outSessionKeyLen)
{
    return mImpl.Reply(
        inReplyPtr, inReplyLen, outSessionKeyPtr, outSessionKeyLen);
}

    int
KrbClient::GetErrorCode() const
{
    return mImpl.GetErrorCode();
}

}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2009 Google Inc. All rights reserved.
 * Copyright (C) 2013 Apple Inc. 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 Google Inc. 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 "config.h"
#include "modules/webdatabase/SQLTransactionCoordinator.h"

#include "modules/webdatabase/DatabaseBackend.h"
#include "modules/webdatabase/SQLTransactionBackend.h"

namespace blink {

static String getDatabaseIdentifier(SQLTransactionBackend* transaction)
{
    DatabaseBackend* database = transaction->database();
    ASSERT(database);
    return database->stringIdentifier();
}

SQLTransactionCoordinator::SQLTransactionCoordinator()
    : m_isShuttingDown(false)
{
}

void SQLTransactionCoordinator::trace(Visitor* visitor)
{
#if ENABLE(OILPAN)
    visitor->trace(m_coordinationInfoMap);
#endif
}

void SQLTransactionCoordinator::processPendingTransactions(CoordinationInfo& info)
{
    if (info.activeWriteTransaction || info.pendingTransactions.isEmpty())
        return;

    RefPtrWillBeRawPtr<SQLTransactionBackend> firstPendingTransaction = info.pendingTransactions.first();
    if (firstPendingTransaction->isReadOnly()) {
        do {
            firstPendingTransaction = info.pendingTransactions.takeFirst();
            info.activeReadTransactions.add(firstPendingTransaction);
            firstPendingTransaction->lockAcquired();
        } while (!info.pendingTransactions.isEmpty() && info.pendingTransactions.first()->isReadOnly());
    } else if (info.activeReadTransactions.isEmpty()) {
        info.pendingTransactions.removeFirst();
        info.activeWriteTransaction = firstPendingTransaction;
        firstPendingTransaction->lockAcquired();
    }
}

void SQLTransactionCoordinator::acquireLock(SQLTransactionBackend* transaction)
{
    ASSERT(!m_isShuttingDown);

    String dbIdentifier = getDatabaseIdentifier(transaction);

    CoordinationInfoHeapMap::iterator coordinationInfoIterator = m_coordinationInfoMap.find(dbIdentifier);
    if (coordinationInfoIterator == m_coordinationInfoMap.end()) {
        // No pending transactions for this DB
        CoordinationInfo& info = m_coordinationInfoMap.add(dbIdentifier, CoordinationInfo()).storedValue->value;
        info.pendingTransactions.append(transaction);
        processPendingTransactions(info);
    } else {
        CoordinationInfo& info = coordinationInfoIterator->value;
        info.pendingTransactions.append(transaction);
        processPendingTransactions(info);
    }

}

void SQLTransactionCoordinator::releaseLock(SQLTransactionBackend* transaction)
{
    if (m_isShuttingDown)
        return;

    String dbIdentifier = getDatabaseIdentifier(transaction);

    CoordinationInfoHeapMap::iterator coordinationInfoIterator = m_coordinationInfoMap.find(dbIdentifier);
    ASSERT_WITH_SECURITY_IMPLICATION(coordinationInfoIterator != m_coordinationInfoMap.end());
    CoordinationInfo& info = coordinationInfoIterator->value;

    if (transaction->isReadOnly()) {
        ASSERT(info.activeReadTransactions.contains(transaction));
        info.activeReadTransactions.remove(transaction);
    } else {
        ASSERT(info.activeWriteTransaction == transaction);
        info.activeWriteTransaction = nullptr;
    }

    processPendingTransactions(info);
}

void SQLTransactionCoordinator::shutdown()
{
    // Prevent releaseLock() from accessing / changing the coordinationInfo
    // while we're shutting down.
    m_isShuttingDown = true;

    // Notify all transactions in progress that the database thread is shutting down
    for (CoordinationInfoHeapMap::iterator coordinationInfoIterator = m_coordinationInfoMap.begin();
         coordinationInfoIterator != m_coordinationInfoMap.end(); ++coordinationInfoIterator) {
        CoordinationInfo& info = coordinationInfoIterator->value;

        // Clean up transactions that have reached "lockAcquired":
        // Transaction phase 4 cleanup. See comment on "What happens if a
        // transaction is interrupted?" at the top of SQLTransactionBackend.cpp.
        if (info.activeWriteTransaction)
            info.activeWriteTransaction->notifyDatabaseThreadIsShuttingDown();
        for (WillBeHeapHashSet<RefPtrWillBeMember<SQLTransactionBackend> >::iterator activeReadTransactionsIterator =
                     info.activeReadTransactions.begin();
             activeReadTransactionsIterator != info.activeReadTransactions.end();
             ++activeReadTransactionsIterator) {
            (*activeReadTransactionsIterator)->notifyDatabaseThreadIsShuttingDown();
        }

        // Clean up transactions that have NOT reached "lockAcquired":
        // Transaction phase 3 cleanup. See comment on "What happens if a
        // transaction is interrupted?" at the top of SQLTransactionBackend.cpp.
        while (!info.pendingTransactions.isEmpty()) {
            RefPtrWillBeRawPtr<SQLTransactionBackend> transaction = info.pendingTransactions.first();
            transaction->notifyDatabaseThreadIsShuttingDown();
        }
    }

    // Clean up all pending transactions for all databases
    m_coordinationInfoMap.clear();
}

} // namespace blink
<commit_msg>Fix hang in SQLTransactionCoordinator::shutdown().<commit_after>/*
 * Copyright (C) 2009 Google Inc. All rights reserved.
 * Copyright (C) 2013 Apple Inc. 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 Google Inc. 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 "config.h"
#include "modules/webdatabase/SQLTransactionCoordinator.h"

#include "modules/webdatabase/DatabaseBackend.h"
#include "modules/webdatabase/SQLTransactionBackend.h"

namespace blink {

static String getDatabaseIdentifier(SQLTransactionBackend* transaction)
{
    DatabaseBackend* database = transaction->database();
    ASSERT(database);
    return database->stringIdentifier();
}

SQLTransactionCoordinator::SQLTransactionCoordinator()
    : m_isShuttingDown(false)
{
}

void SQLTransactionCoordinator::trace(Visitor* visitor)
{
#if ENABLE(OILPAN)
    visitor->trace(m_coordinationInfoMap);
#endif
}

void SQLTransactionCoordinator::processPendingTransactions(CoordinationInfo& info)
{
    if (info.activeWriteTransaction || info.pendingTransactions.isEmpty())
        return;

    RefPtrWillBeRawPtr<SQLTransactionBackend> firstPendingTransaction = info.pendingTransactions.first();
    if (firstPendingTransaction->isReadOnly()) {
        do {
            firstPendingTransaction = info.pendingTransactions.takeFirst();
            info.activeReadTransactions.add(firstPendingTransaction);
            firstPendingTransaction->lockAcquired();
        } while (!info.pendingTransactions.isEmpty() && info.pendingTransactions.first()->isReadOnly());
    } else if (info.activeReadTransactions.isEmpty()) {
        info.pendingTransactions.removeFirst();
        info.activeWriteTransaction = firstPendingTransaction;
        firstPendingTransaction->lockAcquired();
    }
}

void SQLTransactionCoordinator::acquireLock(SQLTransactionBackend* transaction)
{
    ASSERT(!m_isShuttingDown);

    String dbIdentifier = getDatabaseIdentifier(transaction);

    CoordinationInfoHeapMap::iterator coordinationInfoIterator = m_coordinationInfoMap.find(dbIdentifier);
    if (coordinationInfoIterator == m_coordinationInfoMap.end()) {
        // No pending transactions for this DB
        CoordinationInfo& info = m_coordinationInfoMap.add(dbIdentifier, CoordinationInfo()).storedValue->value;
        info.pendingTransactions.append(transaction);
        processPendingTransactions(info);
    } else {
        CoordinationInfo& info = coordinationInfoIterator->value;
        info.pendingTransactions.append(transaction);
        processPendingTransactions(info);
    }

}

void SQLTransactionCoordinator::releaseLock(SQLTransactionBackend* transaction)
{
    if (m_isShuttingDown)
        return;

    String dbIdentifier = getDatabaseIdentifier(transaction);

    CoordinationInfoHeapMap::iterator coordinationInfoIterator = m_coordinationInfoMap.find(dbIdentifier);
    ASSERT_WITH_SECURITY_IMPLICATION(coordinationInfoIterator != m_coordinationInfoMap.end());
    CoordinationInfo& info = coordinationInfoIterator->value;

    if (transaction->isReadOnly()) {
        ASSERT(info.activeReadTransactions.contains(transaction));
        info.activeReadTransactions.remove(transaction);
    } else {
        ASSERT(info.activeWriteTransaction == transaction);
        info.activeWriteTransaction = nullptr;
    }

    processPendingTransactions(info);
}

void SQLTransactionCoordinator::shutdown()
{
    // Prevent releaseLock() from accessing / changing the coordinationInfo
    // while we're shutting down.
    m_isShuttingDown = true;

    // Notify all transactions in progress that the database thread is shutting down
    for (CoordinationInfoHeapMap::iterator coordinationInfoIterator = m_coordinationInfoMap.begin();
         coordinationInfoIterator != m_coordinationInfoMap.end(); ++coordinationInfoIterator) {
        CoordinationInfo& info = coordinationInfoIterator->value;

        // Clean up transactions that have reached "lockAcquired":
        // Transaction phase 4 cleanup. See comment on "What happens if a
        // transaction is interrupted?" at the top of SQLTransactionBackend.cpp.
        if (info.activeWriteTransaction)
            info.activeWriteTransaction->notifyDatabaseThreadIsShuttingDown();
        for (WillBeHeapHashSet<RefPtrWillBeMember<SQLTransactionBackend> >::iterator activeReadTransactionsIterator =
                     info.activeReadTransactions.begin();
             activeReadTransactionsIterator != info.activeReadTransactions.end();
             ++activeReadTransactionsIterator) {
            (*activeReadTransactionsIterator)->notifyDatabaseThreadIsShuttingDown();
        }

        // Clean up transactions that have NOT reached "lockAcquired":
        // Transaction phase 3 cleanup. See comment on "What happens if a
        // transaction is interrupted?" at the top of SQLTransactionBackend.cpp.
        while (!info.pendingTransactions.isEmpty()) {
            RefPtrWillBeRawPtr<SQLTransactionBackend> transaction = info.pendingTransactions.takeFirst();
            transaction->notifyDatabaseThreadIsShuttingDown();
        }
    }

    // Clean up all pending transactions for all databases
    m_coordinationInfoMap.clear();
}

} // namespace blink
<|endoftext|>