<commit_before>/*
 * Copyright (C) 009 Google 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"

#if ENABLE(WORKERS)

#include "WorkerContextExecutionProxy.h"

#include "v8Binding.h"
#include "v8Proxy.h"
#include "v8_index.h"
#include "Event.h"
#include "V8WorkerContextEventListener.h"
#include "WorkerContext.h"
#include "WorkerLocation.h"
#include "WorkerNavigator.h"
#include "WorkerScriptController.h"

namespace WebCore {

static bool isWorkersEnabled = false;

bool WorkerContextExecutionProxy::isWebWorkersEnabled()
{
    return isWorkersEnabled;
}

void WorkerContextExecutionProxy::setIsWebWorkersEnabled(bool value)
{
    isWorkersEnabled = value;
}

WorkerContextExecutionProxy::WorkerContextExecutionProxy(WorkerContext* workerContext)
    : m_workerContext(workerContext)
    , m_recursion(0)
{
}

WorkerContextExecutionProxy::~WorkerContextExecutionProxy()
{
    dispose();
}

void WorkerContextExecutionProxy::dispose()
{
    // Disconnect all event listeners.
    for (EventListenerList::iterator iter = m_listeners.begin(); iter != m_listeners.end();  ++iter)
        static_cast<V8WorkerContextEventListener*>(*iter)->disconnect();

    m_listeners.clear();

    // Detach all events from their JS wrappers.
    for (EventSet::iterator iter = m_events.begin(); iter != m_events.end();  ++iter) {
        Event* event = *iter;
        if (forgetV8EventObject(event))
          event->deref();
    }
    m_events.clear();

    // Dispose the context.
    if (!m_context.IsEmpty()) {
        m_context.Dispose();
        m_context.Clear();
    }

    // Remove the wrapping between JS object and DOM object. This is because
    // the worker context object is going to be disposed immediately when a
    // worker thread is tearing down. We do not want to re-delete the real object
    // when JS object is garbage collected.
    v8::Locker locker;
    v8::HandleScope scope;
    v8::Persistent<v8::Object> wrapper = GetDOMObjectMap().get(m_workerContext);
    if (!wrapper.IsEmpty())
        V8Proxy::SetDOMWrapper(wrapper, V8ClassIndex::INVALID_CLASS_INDEX, NULL);
    GetDOMObjectMap().forget(m_workerContext);
}

WorkerContextExecutionProxy* WorkerContextExecutionProxy::retrieve()
{
    v8::Handle<v8::Context> context = v8::Context::GetCurrent();
    v8::Handle<v8::Object> global = context->Global();
    global = V8Proxy::LookupDOMWrapper(V8ClassIndex::WORKERCONTEXT, global);
    ASSERT(!global.IsEmpty());
    WorkerContext* worker_context = V8Proxy::ToNativeObject<WorkerContext>(V8ClassIndex::WORKERCONTEXT, global);
    return worker_context->script()->proxy();
}

void WorkerContextExecutionProxy::initContextIfNeeded()
{
    // Bail out if the context has already been initialized.
    if (!m_context.IsEmpty())
        return;

    // Create a new environment
    v8::Persistent<v8::ObjectTemplate> global_template;
    m_context = v8::Context::New(NULL, global_template);

    // Starting from now, use local context only.
    v8::Local<v8::Context> context = v8::Local<v8::Context>::New(m_context);
    v8::Context::Scope scope(context);

    // Allocate strings used during initialization.
    v8::Handle<v8::String> implicit_proto_string = v8::String::New("__proto__");

    // Create a new JS object and use it as the prototype for the shadow global object.
    v8::Handle<v8::Function> worker_context_constructor = GetConstructor(V8ClassIndex::WORKERCONTEXT);
    v8::Local<v8::Object> js_worker_context = SafeAllocation::NewInstance(worker_context_constructor);
    // Bail out if allocation failed.
    if (js_worker_context.IsEmpty()) {
        dispose();
        return;
    }

    // Wrap the object.
    V8Proxy::SetDOMWrapper(js_worker_context, V8ClassIndex::ToInt(V8ClassIndex::WORKERCONTEXT), m_workerContext);

    V8Proxy::SetJSWrapperForDOMObject(m_workerContext, v8::Persistent<v8::Object>::New(js_worker_context));

    // Insert the object instance as the prototype of the shadow object.
    v8::Handle<v8::Object> v8_global = m_context->Global();
    v8_global->Set(implicit_proto_string, js_worker_context);
}

v8::Local<v8::Function> WorkerContextExecutionProxy::GetConstructor(V8ClassIndex::V8WrapperType type)
{
    // Enter the context of the proxy to make sure that the function is
    // constructed in the context corresponding to this proxy.
    v8::Context::Scope scope(m_context);
    v8::Handle<v8::FunctionTemplate> functionTemplate = V8Proxy::GetTemplate(type);

    // Getting the function might fail if we're running out of
    // stack or memory.
    v8::TryCatch try_catch;
    v8::Local<v8::Function> value = functionTemplate->GetFunction();
    if (value.IsEmpty())
        return v8::Local<v8::Function>();

    return value;
}

v8::Handle<v8::Value> WorkerContextExecutionProxy::ToV8Object(V8ClassIndex::V8WrapperType type, void* impl)
{
    if (!impl)
        return v8::Null();

    if (type == V8ClassIndex::WORKERCONTEXT)
        return WorkerContextToV8Object(static_cast<WorkerContext*>(impl));

    // Non DOM node
    v8::Persistent<v8::Object> result = GetDOMObjectMap().get(impl);
    if (result.IsEmpty()) {
        v8::Local<v8::Object> v8obj = instantiateV8Object(type, type, impl);
        if (!v8obj.IsEmpty()) {
            switch (type) {
            case V8ClassIndex::WORKERLOCATION:
                static_cast<WorkerLocation*>(impl)->ref();
                break;
            case V8ClassIndex::WORKERNAVIGATOR:
                static_cast<WorkerNavigator*>(impl)->ref();
                break;
            default:
                ASSERT(false);
            }
            result = v8::Persistent<v8::Object>::New(v8obj);
            V8Proxy::SetJSWrapperForDOMObject(impl, result);
        }
    }
    return result;
}

v8::Handle<v8::Value> WorkerContextExecutionProxy::EventToV8Object(Event* event)
{
    if (!event)
        return v8::Null();

    v8::Handle<v8::Object> wrapper = GetDOMObjectMap().get(event);
    if (!wrapper.IsEmpty())
        return wrapper;

    V8ClassIndex::V8WrapperType type = V8ClassIndex::EVENT;

    if (event->isMessageEvent())
        type = V8ClassIndex::MESSAGEEVENT;

    v8::Handle<v8::Object> result = instantiateV8Object(type, V8ClassIndex::EVENT, event);
    if (result.IsEmpty()) {
        // Instantiation failed. Avoid updating the DOM object map and return null which
        // is already handled by callers of this function in case the event is NULL.
        return v8::Null();
    }

    event->ref();  // fast ref
    V8Proxy::SetJSWrapperForDOMObject(event, v8::Persistent<v8::Object>::New(result));

    return result;
}

// A JS object of type EventTarget in the worker context can only be WorkerContext.
v8::Handle<v8::Value> WorkerContextExecutionProxy::EventTargetToV8Object(EventTarget* target)
{
    if (!target)
        return v8::Null();

    WorkerContext* worker_context = target->toWorkerContext();
    if (worker_context)
        return WorkerContextToV8Object(worker_context);

    ASSERT_NOT_REACHED();
    return v8::Handle<v8::Value>();
}

v8::Handle<v8::Value> WorkerContextExecutionProxy::WorkerContextToV8Object(WorkerContext* workerContext)
{
    if (!workerContext)
        return v8::Null();

    v8::Handle<v8::Context> context = workerContext->script()->proxy()->GetContext();

    v8::Handle<v8::Object> global = context->Global();
    ASSERT(!global.IsEmpty());
    return global;
}

v8::Local<v8::Object> WorkerContextExecutionProxy::instantiateV8Object(V8ClassIndex::V8WrapperType descType, V8ClassIndex::V8WrapperType cptrType, void* impl)
{
    v8::Local<v8::Function> function;
    WorkerContextExecutionProxy* proxy = retrieve();
    if (proxy)
        function = proxy->GetConstructor(descType);
    else
        function = V8Proxy::GetTemplate(descType)->GetFunction();

    v8::Local<v8::Object> instance = SafeAllocation::NewInstance(function);
    if (!instance.IsEmpty()) {
        // Avoid setting the DOM wrapper for failed allocations.
        V8Proxy::SetDOMWrapper(instance, V8ClassIndex::ToInt(cptrType), impl);
    }
    return instance;
}

bool WorkerContextExecutionProxy::forgetV8EventObject(Event* event)
{
    if (GetDOMObjectMap().contains(event)) {
        GetDOMObjectMap().forget(event);
        return true;
    } else
        return false;
}

v8::Local<v8::Value> WorkerContextExecutionProxy::evaluate(const String& str, const String& fileName, int baseLine)
{
    v8::Locker locker;
    v8::HandleScope hs;

    initContextIfNeeded();
    v8::Context::Scope scope(m_context);

    v8::Local<v8::String> code = v8ExternalString(str);
    v8::Handle<v8::Script> script = V8Proxy::CompileScript(code, fileName, baseLine);
    return runScript(script);
}

v8::Local<v8::Value> WorkerContextExecutionProxy::runScript(v8::Handle<v8::Script> script)
{
    if (script.IsEmpty())
        return v8::Local<v8::Value>();

    // Compute the source string and prevent against infinite recursion.
    if (m_recursion >= kMaxRecursionDepth) {
        v8::Local<v8::String> code = v8ExternalString("throw RangeError('Recursion too deep')");
        script = V8Proxy::CompileScript(code, "", 0);
    }

    if (V8Proxy::HandleOutOfMemory())
        ASSERT(script.IsEmpty());

    if (script.IsEmpty())
        return v8::Local<v8::Value>();

    // Run the script and keep track of the current recursion depth.
    v8::Local<v8::Value> result;
    {
        m_recursion++;
        result = script->Run();
        m_recursion--;
    }

    // Handle V8 internal error situation (Out-of-memory).
    if (result.IsEmpty())
        return v8::Local<v8::Value>();

    return result;
}

PassRefPtr<V8EventListener> WorkerContextExecutionProxy::FindOrCreateEventListener(v8::Local<v8::Value> obj, bool isInline, bool findOnly)
{
    if (!obj->IsObject())
        return 0;

    for (EventListenerList::iterator iter = m_listeners.begin(); iter != m_listeners.end(); ++iter) {
        V8EventListener* el = *iter;
        if (el->isInline() == isInline && el->getListenerObject() == obj)
            return el;
    }
    if (findOnly)
        return NULL;

    // Create a new one, and add to cache.
    RefPtr<V8EventListener> listener = V8WorkerContextEventListener::create(this, v8::Local<v8::Object>::Cast(obj), isInline);
    m_listeners.push_back(listener.get());

    return listener.release();
}

void WorkerContextExecutionProxy::RemoveEventListener(V8EventListener* listener)
{
    for (EventListenerList::iterator iter = m_listeners.begin(); iter != m_listeners.end();  ++iter) {
        if (*iter == listener) {
            m_listeners.erase(iter);
            return;
        }
    }
}

void WorkerContextExecutionProxy::TrackEvent(Event* event)
{
    m_events.add(event);
}

} // namespace WebCore

#endif // ENABLE(WORKERS)
<commit_msg>Linux: fix include filename case<commit_after>/*
 * Copyright (C) 009 Google 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"

#if ENABLE(WORKERS)

#include "WorkerContextExecutionProxy.h"

#include "V8Binding.h"
#include "V8Proxy.h"
#include "v8_index.h"
#include "Event.h"
#include "V8WorkerContextEventListener.h"
#include "WorkerContext.h"
#include "WorkerLocation.h"
#include "WorkerNavigator.h"
#include "WorkerScriptController.h"

namespace WebCore {

static bool isWorkersEnabled = false;

bool WorkerContextExecutionProxy::isWebWorkersEnabled()
{
    return isWorkersEnabled;
}

void WorkerContextExecutionProxy::setIsWebWorkersEnabled(bool value)
{
    isWorkersEnabled = value;
}

WorkerContextExecutionProxy::WorkerContextExecutionProxy(WorkerContext* workerContext)
    : m_workerContext(workerContext)
    , m_recursion(0)
{
}

WorkerContextExecutionProxy::~WorkerContextExecutionProxy()
{
    dispose();
}

void WorkerContextExecutionProxy::dispose()
{
    // Disconnect all event listeners.
    for (EventListenerList::iterator iter = m_listeners.begin(); iter != m_listeners.end();  ++iter)
        static_cast<V8WorkerContextEventListener*>(*iter)->disconnect();

    m_listeners.clear();

    // Detach all events from their JS wrappers.
    for (EventSet::iterator iter = m_events.begin(); iter != m_events.end();  ++iter) {
        Event* event = *iter;
        if (forgetV8EventObject(event))
          event->deref();
    }
    m_events.clear();

    // Dispose the context.
    if (!m_context.IsEmpty()) {
        m_context.Dispose();
        m_context.Clear();
    }

    // Remove the wrapping between JS object and DOM object. This is because
    // the worker context object is going to be disposed immediately when a
    // worker thread is tearing down. We do not want to re-delete the real object
    // when JS object is garbage collected.
    v8::Locker locker;
    v8::HandleScope scope;
    v8::Persistent<v8::Object> wrapper = GetDOMObjectMap().get(m_workerContext);
    if (!wrapper.IsEmpty())
        V8Proxy::SetDOMWrapper(wrapper, V8ClassIndex::INVALID_CLASS_INDEX, NULL);
    GetDOMObjectMap().forget(m_workerContext);
}

WorkerContextExecutionProxy* WorkerContextExecutionProxy::retrieve()
{
    v8::Handle<v8::Context> context = v8::Context::GetCurrent();
    v8::Handle<v8::Object> global = context->Global();
    global = V8Proxy::LookupDOMWrapper(V8ClassIndex::WORKERCONTEXT, global);
    ASSERT(!global.IsEmpty());
    WorkerContext* worker_context = V8Proxy::ToNativeObject<WorkerContext>(V8ClassIndex::WORKERCONTEXT, global);
    return worker_context->script()->proxy();
}

void WorkerContextExecutionProxy::initContextIfNeeded()
{
    // Bail out if the context has already been initialized.
    if (!m_context.IsEmpty())
        return;

    // Create a new environment
    v8::Persistent<v8::ObjectTemplate> global_template;
    m_context = v8::Context::New(NULL, global_template);

    // Starting from now, use local context only.
    v8::Local<v8::Context> context = v8::Local<v8::Context>::New(m_context);
    v8::Context::Scope scope(context);

    // Allocate strings used during initialization.
    v8::Handle<v8::String> implicit_proto_string = v8::String::New("__proto__");

    // Create a new JS object and use it as the prototype for the shadow global object.
    v8::Handle<v8::Function> worker_context_constructor = GetConstructor(V8ClassIndex::WORKERCONTEXT);
    v8::Local<v8::Object> js_worker_context = SafeAllocation::NewInstance(worker_context_constructor);
    // Bail out if allocation failed.
    if (js_worker_context.IsEmpty()) {
        dispose();
        return;
    }

    // Wrap the object.
    V8Proxy::SetDOMWrapper(js_worker_context, V8ClassIndex::ToInt(V8ClassIndex::WORKERCONTEXT), m_workerContext);

    V8Proxy::SetJSWrapperForDOMObject(m_workerContext, v8::Persistent<v8::Object>::New(js_worker_context));

    // Insert the object instance as the prototype of the shadow object.
    v8::Handle<v8::Object> v8_global = m_context->Global();
    v8_global->Set(implicit_proto_string, js_worker_context);
}

v8::Local<v8::Function> WorkerContextExecutionProxy::GetConstructor(V8ClassIndex::V8WrapperType type)
{
    // Enter the context of the proxy to make sure that the function is
    // constructed in the context corresponding to this proxy.
    v8::Context::Scope scope(m_context);
    v8::Handle<v8::FunctionTemplate> functionTemplate = V8Proxy::GetTemplate(type);

    // Getting the function might fail if we're running out of
    // stack or memory.
    v8::TryCatch try_catch;
    v8::Local<v8::Function> value = functionTemplate->GetFunction();
    if (value.IsEmpty())
        return v8::Local<v8::Function>();

    return value;
}

v8::Handle<v8::Value> WorkerContextExecutionProxy::ToV8Object(V8ClassIndex::V8WrapperType type, void* impl)
{
    if (!impl)
        return v8::Null();

    if (type == V8ClassIndex::WORKERCONTEXT)
        return WorkerContextToV8Object(static_cast<WorkerContext*>(impl));

    // Non DOM node
    v8::Persistent<v8::Object> result = GetDOMObjectMap().get(impl);
    if (result.IsEmpty()) {
        v8::Local<v8::Object> v8obj = instantiateV8Object(type, type, impl);
        if (!v8obj.IsEmpty()) {
            switch (type) {
            case V8ClassIndex::WORKERLOCATION:
                static_cast<WorkerLocation*>(impl)->ref();
                break;
            case V8ClassIndex::WORKERNAVIGATOR:
                static_cast<WorkerNavigator*>(impl)->ref();
                break;
            default:
                ASSERT(false);
            }
            result = v8::Persistent<v8::Object>::New(v8obj);
            V8Proxy::SetJSWrapperForDOMObject(impl, result);
        }
    }
    return result;
}

v8::Handle<v8::Value> WorkerContextExecutionProxy::EventToV8Object(Event* event)
{
    if (!event)
        return v8::Null();

    v8::Handle<v8::Object> wrapper = GetDOMObjectMap().get(event);
    if (!wrapper.IsEmpty())
        return wrapper;

    V8ClassIndex::V8WrapperType type = V8ClassIndex::EVENT;

    if (event->isMessageEvent())
        type = V8ClassIndex::MESSAGEEVENT;

    v8::Handle<v8::Object> result = instantiateV8Object(type, V8ClassIndex::EVENT, event);
    if (result.IsEmpty()) {
        // Instantiation failed. Avoid updating the DOM object map and return null which
        // is already handled by callers of this function in case the event is NULL.
        return v8::Null();
    }

    event->ref();  // fast ref
    V8Proxy::SetJSWrapperForDOMObject(event, v8::Persistent<v8::Object>::New(result));

    return result;
}

// A JS object of type EventTarget in the worker context can only be WorkerContext.
v8::Handle<v8::Value> WorkerContextExecutionProxy::EventTargetToV8Object(EventTarget* target)
{
    if (!target)
        return v8::Null();

    WorkerContext* worker_context = target->toWorkerContext();
    if (worker_context)
        return WorkerContextToV8Object(worker_context);

    ASSERT_NOT_REACHED();
    return v8::Handle<v8::Value>();
}

v8::Handle<v8::Value> WorkerContextExecutionProxy::WorkerContextToV8Object(WorkerContext* workerContext)
{
    if (!workerContext)
        return v8::Null();

    v8::Handle<v8::Context> context = workerContext->script()->proxy()->GetContext();

    v8::Handle<v8::Object> global = context->Global();
    ASSERT(!global.IsEmpty());
    return global;
}

v8::Local<v8::Object> WorkerContextExecutionProxy::instantiateV8Object(V8ClassIndex::V8WrapperType descType, V8ClassIndex::V8WrapperType cptrType, void* impl)
{
    v8::Local<v8::Function> function;
    WorkerContextExecutionProxy* proxy = retrieve();
    if (proxy)
        function = proxy->GetConstructor(descType);
    else
        function = V8Proxy::GetTemplate(descType)->GetFunction();

    v8::Local<v8::Object> instance = SafeAllocation::NewInstance(function);
    if (!instance.IsEmpty()) {
        // Avoid setting the DOM wrapper for failed allocations.
        V8Proxy::SetDOMWrapper(instance, V8ClassIndex::ToInt(cptrType), impl);
    }
    return instance;
}

bool WorkerContextExecutionProxy::forgetV8EventObject(Event* event)
{
    if (GetDOMObjectMap().contains(event)) {
        GetDOMObjectMap().forget(event);
        return true;
    } else
        return false;
}

v8::Local<v8::Value> WorkerContextExecutionProxy::evaluate(const String& str, const String& fileName, int baseLine)
{
    v8::Locker locker;
    v8::HandleScope hs;

    initContextIfNeeded();
    v8::Context::Scope scope(m_context);

    v8::Local<v8::String> code = v8ExternalString(str);
    v8::Handle<v8::Script> script = V8Proxy::CompileScript(code, fileName, baseLine);
    return runScript(script);
}

v8::Local<v8::Value> WorkerContextExecutionProxy::runScript(v8::Handle<v8::Script> script)
{
    if (script.IsEmpty())
        return v8::Local<v8::Value>();

    // Compute the source string and prevent against infinite recursion.
    if (m_recursion >= kMaxRecursionDepth) {
        v8::Local<v8::String> code = v8ExternalString("throw RangeError('Recursion too deep')");
        script = V8Proxy::CompileScript(code, "", 0);
    }

    if (V8Proxy::HandleOutOfMemory())
        ASSERT(script.IsEmpty());

    if (script.IsEmpty())
        return v8::Local<v8::Value>();

    // Run the script and keep track of the current recursion depth.
    v8::Local<v8::Value> result;
    {
        m_recursion++;
        result = script->Run();
        m_recursion--;
    }

    // Handle V8 internal error situation (Out-of-memory).
    if (result.IsEmpty())
        return v8::Local<v8::Value>();

    return result;
}

PassRefPtr<V8EventListener> WorkerContextExecutionProxy::FindOrCreateEventListener(v8::Local<v8::Value> obj, bool isInline, bool findOnly)
{
    if (!obj->IsObject())
        return 0;

    for (EventListenerList::iterator iter = m_listeners.begin(); iter != m_listeners.end(); ++iter) {
        V8EventListener* el = *iter;
        if (el->isInline() == isInline && el->getListenerObject() == obj)
            return el;
    }
    if (findOnly)
        return NULL;

    // Create a new one, and add to cache.
    RefPtr<V8EventListener> listener = V8WorkerContextEventListener::create(this, v8::Local<v8::Object>::Cast(obj), isInline);
    m_listeners.push_back(listener.get());

    return listener.release();
}

void WorkerContextExecutionProxy::RemoveEventListener(V8EventListener* listener)
{
    for (EventListenerList::iterator iter = m_listeners.begin(); iter != m_listeners.end();  ++iter) {
        if (*iter == listener) {
            m_listeners.erase(iter);
            return;
        }
    }
}

void WorkerContextExecutionProxy::TrackEvent(Event* event)
{
    m_events.add(event);
}

} // namespace WebCore

#endif // ENABLE(WORKERS)
<|endoftext|>


<commit_before>// Ouzel by Elviss Strazdins

#ifndef OUZEL_CORE_WORKERPOOL_HPP
#define OUZEL_CORE_WORKERPOOL_HPP

#include <condition_variable>
#include <functional>
#include <memory>
#include <mutex>
#include <queue>
#include <vector>
#include "../thread/Thread.hpp"
#include "../utils/Log.hpp"

namespace ouzel::core
{
    class TaskGroup final
    {
        friend class WorkerPool;
    public:
        TaskGroup() = default;

        void add(std::function<void()> task)
        {
            taskQueue.push(std::move(task));
        }

        std::size_t getTaskCount() const noexcept { return taskQueue.size(); }

    private:
        std::queue<std::function<void()>> taskQueue;
    };

    class Future final
    {
        friend class Promise;
    public:
        void wait()
        {
            std::unique_lock lock{sharedState->mutex};
            sharedState->condition.wait(lock, [this]() noexcept { return sharedState->count == 0; });
        }

    private:
        struct State final
        {
        public:
            State(std::size_t c): count{c}
            {
            }

            std::size_t count = 0;
            std::mutex mutex;
            std::condition_variable condition;
        };

        Future(std::shared_ptr<State> state) noexcept:
            sharedState{state}
        {
        }

        std::shared_ptr<State> sharedState;
    };

    class Promise final
    {
        friend class WorkerPool;
    public:
        Future getFuture() const
        {
            return Future{sharedState};
        }

        void decrement()
        {
            std::unique_lock lock{sharedState->mutex};

            if (sharedState->count == 0 || --sharedState->count == 0)
            {
                lock.unlock();
                sharedState->condition.notify_all();
            }
        }

    private:
        Promise(const TaskGroup& taskGroup) noexcept:
            sharedState{std::make_shared<Future::State>(taskGroup.getTaskCount())}
        {
        }

        std::shared_ptr<Future::State> sharedState;
    };

    class WorkerPool final
    {
    public:
        WorkerPool()
        {
            const std::size_t cpuCount = std::thread::hardware_concurrency();
            const std::size_t count = (cpuCount > 1) ? cpuCount - 1 : 1;

            for (unsigned int i = 0; i < count; ++i)
                workers.emplace_back(&WorkerPool::work, this);
        }

        ~WorkerPool()
        {
            running = false;
            taskQueueCondition.notify_all();
        }

        Future run(TaskGroup&& taskGroup)
        {
            Promise promise{taskGroup};
            Future future = promise.getFuture();

            std::unique_lock lock{taskQueueMutex};

            while (!taskGroup.taskQueue.empty())
            {
                taskQueue.push(std::pair(std::move(promise),
                                         std::move(taskGroup.taskQueue.front())));
                taskGroup.taskQueue.pop();
            }

            lock.unlock();

            taskQueueCondition.notify_all();

            return future;
        }

    private:
        void work()
        {
            log(Log::Level::info) << "Worker started";

            for (;;)
            {
                std::unique_lock lock{taskQueueMutex};
                taskQueueCondition.wait(lock, [this]() noexcept { return !running || !taskQueue.empty(); });
                if (!running) break;
                auto task = std::move(taskQueue.front());
                taskQueue.pop();
                lock.unlock();

                task.second();

                task.first.decrement();
            }

            log(Log::Level::info) << "Worker finished";
        }

        std::vector<thread::Thread> workers;
        bool running = true;
        std::queue<std::pair<Promise, std::function<void()>>> taskQueue;
        std::mutex taskQueueMutex;
        std::condition_variable taskQueueCondition;
    };
}

#endif // OUZEL_CORE_WORKERPOOL_HPP
<commit_msg>Make the constructor of Promise public<commit_after>// Ouzel by Elviss Strazdins

#ifndef OUZEL_CORE_WORKERPOOL_HPP
#define OUZEL_CORE_WORKERPOOL_HPP

#include <condition_variable>
#include <functional>
#include <memory>
#include <mutex>
#include <queue>
#include <vector>
#include "../thread/Thread.hpp"
#include "../utils/Log.hpp"

namespace ouzel::core
{
    class TaskGroup final
    {
        friend class WorkerPool;
    public:
        TaskGroup() = default;

        void add(std::function<void()> task)
        {
            taskQueue.push(std::move(task));
        }

        std::size_t getTaskCount() const noexcept { return taskQueue.size(); }

    private:
        std::queue<std::function<void()>> taskQueue;
    };

    class Future final
    {
        friend class Promise;
    public:
        void wait()
        {
            std::unique_lock lock{sharedState->mutex};
            sharedState->condition.wait(lock, [this]() noexcept { return sharedState->count == 0; });
        }

    private:
        struct State final
        {
        public:
            State(std::size_t c): count{c}
            {
            }

            std::size_t count = 0;
            std::mutex mutex;
            std::condition_variable condition;
        };

        Future(std::shared_ptr<State> state) noexcept:
            sharedState{state}
        {
        }

        std::shared_ptr<State> sharedState;
    };

    class Promise final
    {
    public:
        Promise(const TaskGroup& taskGroup) noexcept:
            sharedState{std::make_shared<Future::State>(taskGroup.getTaskCount())}
        {
        }

        Future getFuture() const
        {
            return Future{sharedState};
        }

        void decrement()
        {
            std::unique_lock lock{sharedState->mutex};

            if (sharedState->count == 0 || --sharedState->count == 0)
            {
                lock.unlock();
                sharedState->condition.notify_all();
            }
        }

    private:
        std::shared_ptr<Future::State> sharedState;
    };

    class WorkerPool final
    {
    public:
        WorkerPool()
        {
            const std::size_t cpuCount = std::thread::hardware_concurrency();
            const std::size_t count = (cpuCount > 1) ? cpuCount - 1 : 1;

            for (std::size_t i = 0; i < count; ++i)
                workers.emplace_back(&WorkerPool::work, this);
        }

        ~WorkerPool()
        {
            running = false;
            taskQueueCondition.notify_all();
        }

        Future run(TaskGroup&& taskGroup)
        {
            Promise promise{taskGroup};
            Future future = promise.getFuture();

            std::unique_lock lock{taskQueueMutex};

            while (!taskGroup.taskQueue.empty())
            {
                taskQueue.push(std::pair(std::move(promise),
                                         std::move(taskGroup.taskQueue.front())));
                taskGroup.taskQueue.pop();
            }

            lock.unlock();

            taskQueueCondition.notify_all();

            return future;
        }

    private:
        void work()
        {
            log(Log::Level::info) << "Worker started";

            for (;;)
            {
                std::unique_lock lock{taskQueueMutex};
                taskQueueCondition.wait(lock, [this]() noexcept { return !running || !taskQueue.empty(); });
                if (!running) break;
                auto task = std::move(taskQueue.front());
                taskQueue.pop();
                lock.unlock();

                task.second();

                task.first.decrement();
            }

            log(Log::Level::info) << "Worker finished";
        }

        std::vector<thread::Thread> workers;
        bool running = true;
        std::queue<std::pair<Promise, std::function<void()>>> taskQueue;
        std::mutex taskQueueMutex;
        std::condition_variable taskQueueCondition;
    };
}

#endif // OUZEL_CORE_WORKERPOOL_HPP
<|endoftext|>


<commit_before>/* -------------------------------------------------------------------------- *
 *                           OpenSim:  Logger.cpp                             *
 * -------------------------------------------------------------------------- *
 * The OpenSim API is a toolkit for musculoskeletal modeling and simulation.  *
 * See http://opensim.stanford.edu and the NOTICE file for more information.  *
 * OpenSim is developed at Stanford University and supported by the US        *
 * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA    *
 * through the Warrior Web program.                                           *
 *                                                                            *
 * Copyright (c) 2005-2019 Stanford University and the Authors                *
 *                                                                            *
 * 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 "Logger.h"

#include "Exception.h"
#include "IO.h"
#include "LogSink.h"

#include "spdlog/sinks/stdout_color_sinks.h"

using namespace OpenSim;

static void initializeLogger(spdlog::logger& l) {
    l.set_level(spdlog::level::info);
    l.set_pattern("%v");
}

// cout logger will be initialized during static initialization time
static std::shared_ptr<spdlog::logger> coutLogger =
        spdlog::stdout_color_mt("cout");

// default logger will be initialized during static initialization time
static std::shared_ptr<spdlog::logger> defaultLogger =
        spdlog::default_logger();

// this function returns a dummy value so that it can be used in an assignment
// expression (below) that *must* be executed in-order at static init time
static bool initializeLogging() {
    initializeLogger(*coutLogger);
    initializeLogger(*defaultLogger);
    spdlog::flush_on(spdlog::level::info);
    return true;
}

// initialization of this variable will have the side-effect of completing the
// initialization of logging
static bool otherStaticInit = initializeLogging();

// the file log sink (e.g. `opensim.log`) is lazily initialized.
//
// it is only initialized when the first log message is about to be written to
// it. Users *may* disable this functionality before the first log message is
// written (or disable it statically, by setting OPENSIM_DISABLE_LOG_FILE)
static std::shared_ptr<spdlog::sinks::basic_file_sink_mt> m_filesink = nullptr;

// if a user manually calls `Logger::(remove|add)FileSink`, auto-initialization
// should be disabled. Manual usage "overrides" lazy auto-initialization.
static bool fileSinkAutoInitDisabled = false;

// attempt to auto-initialize the file log, if applicable
static bool initFileLoggingAsNeeded() {
#ifdef OPENSIM_DISABLE_LOG_FILE
// software builders may want to statically ensure that automatic file logging
// *cannot* happen - even during static initialization. This compiler define
// outright disables the behavior, which is important in Windows applications
// that run multiple instances of OpenSim-linked binaries. In Windows, the
// logs files collide and cause a "multiple processes cannot open the same
// file" error).
return true;
#else
    static bool initialized = []() {
        if (fileSinkAutoInitDisabled) {
            return true;
        }
        Logger::addFileSink();
        return true;
    }();

    return initialized;
#endif
}

// this function is only called when the caller is about to log something, so
// it should perform lazy initialization of the file sink
spdlog::logger& Logger::getCoutLogger() {
    initFileLoggingAsNeeded();
    return *coutLogger;
}

// this function is only called when the caller is about to log something, so
// it should perform lazy initialization of the file sink
spdlog::logger& Logger::getDefaultLogger() {
    initFileLoggingAsNeeded();
    return *defaultLogger;
}

static void addSinkInternal(std::shared_ptr<spdlog::sinks::sink> sink) {
    coutLogger->sinks().push_back(sink);
    defaultLogger->sinks().push_back(sink);
}

static void removeSinkInternal(const std::shared_ptr<spdlog::sinks::sink> sink)
{
    {
        auto& sinks = defaultLogger->sinks();
        auto new_end = std::remove(sinks.begin(), sinks.end(), sink);
        sinks.erase(new_end, sinks.end());
    }
    {
        auto& sinks = coutLogger->sinks();
        auto new_end = std::remove(sinks.begin(), sinks.end(), sink);
        sinks.erase(new_end, sinks.end());
    }
}

void Logger::setLevel(Level level) {
    switch (level) {
    case Level::Off:
        spdlog::set_level(spdlog::level::off);
        break;
    case Level::Critical:
        spdlog::set_level(spdlog::level::critical);
        break;
    case Level::Error:
        spdlog::set_level(spdlog::level::err);
        break;
    case Level::Warn:
        spdlog::set_level(spdlog::level::warn);
        break;
    case Level::Info:
        spdlog::set_level(spdlog::level::info);
        break;
    case Level::Debug:
        spdlog::set_level(spdlog::level::debug);
        break;
    case Level::Trace:
        spdlog::set_level(spdlog::level::trace);
        break;
    default:
        OPENSIM_THROW(Exception, "Internal error.");
    }
    Logger::info("Set log level to {}.", getLevelString());
}

Logger::Level Logger::getLevel() {
    switch (defaultLogger->level()) {
    case spdlog::level::off: return Level::Off;
    case spdlog::level::critical: return Level::Critical;
    case spdlog::level::err: return Level::Error;
    case spdlog::level::warn: return Level::Warn;
    case spdlog::level::info: return Level::Info;
    case spdlog::level::debug: return Level::Debug;
    case spdlog::level::trace: return Level::Trace;
    default:
        OPENSIM_THROW(Exception, "Internal error.");
    }
}

void Logger::setLevelString(std::string str) {
    Level level;
    str = IO::Lowercase(str);
    if (str == "off") level = Level::Off;
    else if (str == "critical") level = Level::Critical;
    else if (str == "error") level = Level::Error;
    else if (str == "warn") level = Level::Warn;
    else if (str == "info") level = Level::Info;
    else if (str == "debug") level = Level::Debug;
    else if (str == "trace") level = Level::Trace;
    else {
        OPENSIM_THROW(Exception,
                "Expected log level to be Off, Critical, Error, "
                "Warn, Info, Debug, or Trace; got {}.",
                str);
    }
    setLevel(level);
}

std::string Logger::getLevelString() {
    const auto level = getLevel();
    switch (level) {
    case Level::Off: return "Off";
    case Level::Critical: return "Critical";
    case Level::Error: return "Error";
    case Level::Warn: return "Warn";
    case Level::Info: return "Info";
    case Level::Debug: return "Debug";
    case Level::Trace: return "Trace";
    default:
        OPENSIM_THROW(Exception, "Internal error.");
    }
}

bool Logger::shouldLog(Level level) {
    spdlog::level::level_enum spdlogLevel;
    switch (level) {
    case Level::Off: spdlogLevel = spdlog::level::off; break;
    case Level::Critical: spdlogLevel = spdlog::level::critical; break;
    case Level::Error: spdlogLevel = spdlog::level::err; break;
    case Level::Warn: spdlogLevel = spdlog::level::warn; break;
    case Level::Info: spdlogLevel = spdlog::level::info; break;
    case Level::Debug: spdlogLevel = spdlog::level::debug; break;
    case Level::Trace: spdlogLevel = spdlog::level::trace; break;
    default:
        OPENSIM_THROW(Exception, "Internal error.");
    }
    return defaultLogger->should_log(spdlogLevel);
}

void Logger::addFileSink(const std::string& filepath) {
    // this method is either called by the file log auto-initializer, which
    // should now be disabled, or by downstream code trying to manually specify
    // a file sink
    //
    // downstream callers would find it quite surprising if the auto-initializer
    // runs *after* they manually specify a log, so just disable it
    fileSinkAutoInitDisabled = true;

    if (m_filesink) {
        defaultLogger->warn("Already logging to file '{}'; log file not added. Call "
             "removeFileSink() first.", m_filesink->filename());
        return;
    }

    // check if file can be opened at the specified path if not return meaningful
    // warning rather than bubble the exception up.
    try {
        m_filesink =
                std::make_shared<spdlog::sinks::basic_file_sink_mt>(filepath);
    }
    catch (...) {
        defaultLogger->warn("Can't open file '{}' for writing. Log file will not be created. "
             "Check that you have write permissions to the specified path.",
                filepath);
        return;
    }
    addSinkInternal(m_filesink);
}

void Logger::removeFileSink() {
    // if this method is called, then we are probably at a point in the
    // application's lifetime where automatic log allocation is going to cause
    // confusion.
    //
    // callers will be surpised if, after calling this method, auto
    // initialization happens afterwards and the log file still exists - even
    // if they called it to remove some manually-specified log
    fileSinkAutoInitDisabled = true;

    if (m_filesink == nullptr) {
        return;
    }

    removeSinkInternal(
            std::static_pointer_cast<spdlog::sinks::sink>(m_filesink));
    m_filesink.reset();
}

void Logger::addSink(const std::shared_ptr<LogSink> sink) {
    addSinkInternal(std::static_pointer_cast<spdlog::sinks::sink>(sink));
}

void Logger::removeSink(const std::shared_ptr<LogSink> sink) {
    removeSinkInternal(std::static_pointer_cast<spdlog::sinks::sink>(sink));
}

<commit_msg>Fixed cout and default logger having different output formats<commit_after>/* -------------------------------------------------------------------------- *
 *                           OpenSim:  Logger.cpp                             *
 * -------------------------------------------------------------------------- *
 * The OpenSim API is a toolkit for musculoskeletal modeling and simulation.  *
 * See http://opensim.stanford.edu and the NOTICE file for more information.  *
 * OpenSim is developed at Stanford University and supported by the US        *
 * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA    *
 * through the Warrior Web program.                                           *
 *                                                                            *
 * Copyright (c) 2005-2019 Stanford University and the Authors                *
 *                                                                            *
 * 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 "Logger.h"

#include "Exception.h"
#include "IO.h"
#include "LogSink.h"

#include "spdlog/sinks/stdout_color_sinks.h"

using namespace OpenSim;

static void initializeLogger(spdlog::logger& l, const char* pattern) {
    l.set_level(spdlog::level::info);
    l.set_pattern(pattern);
}

// cout logger will be initialized during static initialization time
static std::shared_ptr<spdlog::logger> coutLogger =
        spdlog::stdout_color_mt("cout");

// default logger will be initialized during static initialization time
static std::shared_ptr<spdlog::logger> defaultLogger =
        spdlog::default_logger();

// this function returns a dummy value so that it can be used in an assignment
// expression (below) that *must* be executed in-order at static init time
static bool initializeLogging() {
    initializeLogger(*coutLogger, "%v");
    initializeLogger(*defaultLogger, "[%l] %v");
    spdlog::flush_on(spdlog::level::info);
    return true;
}

// initialization of this variable will have the side-effect of completing the
// initialization of logging
static bool otherStaticInit = initializeLogging();

// the file log sink (e.g. `opensim.log`) is lazily initialized.
//
// it is only initialized when the first log message is about to be written to
// it. Users *may* disable this functionality before the first log message is
// written (or disable it statically, by setting OPENSIM_DISABLE_LOG_FILE)
static std::shared_ptr<spdlog::sinks::basic_file_sink_mt> m_filesink = nullptr;

// if a user manually calls `Logger::(remove|add)FileSink`, auto-initialization
// should be disabled. Manual usage "overrides" lazy auto-initialization.
static bool fileSinkAutoInitDisabled = false;

// attempt to auto-initialize the file log, if applicable
static bool initFileLoggingAsNeeded() {
#ifdef OPENSIM_DISABLE_LOG_FILE
// software builders may want to statically ensure that automatic file logging
// *cannot* happen - even during static initialization. This compiler define
// outright disables the behavior, which is important in Windows applications
// that run multiple instances of OpenSim-linked binaries. In Windows, the
// logs files collide and cause a "multiple processes cannot open the same
// file" error).
return true;
#else
    static bool initialized = []() {
        if (fileSinkAutoInitDisabled) {
            return true;
        }
        Logger::addFileSink();
        return true;
    }();

    return initialized;
#endif
}

// this function is only called when the caller is about to log something, so
// it should perform lazy initialization of the file sink
spdlog::logger& Logger::getCoutLogger() {
    initFileLoggingAsNeeded();
    return *coutLogger;
}

// this function is only called when the caller is about to log something, so
// it should perform lazy initialization of the file sink
spdlog::logger& Logger::getDefaultLogger() {
    initFileLoggingAsNeeded();
    return *defaultLogger;
}

static void addSinkInternal(std::shared_ptr<spdlog::sinks::sink> sink) {
    coutLogger->sinks().push_back(sink);
    defaultLogger->sinks().push_back(sink);
}

static void removeSinkInternal(const std::shared_ptr<spdlog::sinks::sink> sink)
{
    {
        auto& sinks = defaultLogger->sinks();
        auto new_end = std::remove(sinks.begin(), sinks.end(), sink);
        sinks.erase(new_end, sinks.end());
    }
    {
        auto& sinks = coutLogger->sinks();
        auto new_end = std::remove(sinks.begin(), sinks.end(), sink);
        sinks.erase(new_end, sinks.end());
    }
}

void Logger::setLevel(Level level) {
    switch (level) {
    case Level::Off:
        spdlog::set_level(spdlog::level::off);
        break;
    case Level::Critical:
        spdlog::set_level(spdlog::level::critical);
        break;
    case Level::Error:
        spdlog::set_level(spdlog::level::err);
        break;
    case Level::Warn:
        spdlog::set_level(spdlog::level::warn);
        break;
    case Level::Info:
        spdlog::set_level(spdlog::level::info);
        break;
    case Level::Debug:
        spdlog::set_level(spdlog::level::debug);
        break;
    case Level::Trace:
        spdlog::set_level(spdlog::level::trace);
        break;
    default:
        OPENSIM_THROW(Exception, "Internal error.");
    }
    Logger::info("Set log level to {}.", getLevelString());
}

Logger::Level Logger::getLevel() {
    switch (defaultLogger->level()) {
    case spdlog::level::off: return Level::Off;
    case spdlog::level::critical: return Level::Critical;
    case spdlog::level::err: return Level::Error;
    case spdlog::level::warn: return Level::Warn;
    case spdlog::level::info: return Level::Info;
    case spdlog::level::debug: return Level::Debug;
    case spdlog::level::trace: return Level::Trace;
    default:
        OPENSIM_THROW(Exception, "Internal error.");
    }
}

void Logger::setLevelString(std::string str) {
    Level level;
    str = IO::Lowercase(str);
    if (str == "off") level = Level::Off;
    else if (str == "critical") level = Level::Critical;
    else if (str == "error") level = Level::Error;
    else if (str == "warn") level = Level::Warn;
    else if (str == "info") level = Level::Info;
    else if (str == "debug") level = Level::Debug;
    else if (str == "trace") level = Level::Trace;
    else {
        OPENSIM_THROW(Exception,
                "Expected log level to be Off, Critical, Error, "
                "Warn, Info, Debug, or Trace; got {}.",
                str);
    }
    setLevel(level);
}

std::string Logger::getLevelString() {
    const auto level = getLevel();
    switch (level) {
    case Level::Off: return "Off";
    case Level::Critical: return "Critical";
    case Level::Error: return "Error";
    case Level::Warn: return "Warn";
    case Level::Info: return "Info";
    case Level::Debug: return "Debug";
    case Level::Trace: return "Trace";
    default:
        OPENSIM_THROW(Exception, "Internal error.");
    }
}

bool Logger::shouldLog(Level level) {
    spdlog::level::level_enum spdlogLevel;
    switch (level) {
    case Level::Off: spdlogLevel = spdlog::level::off; break;
    case Level::Critical: spdlogLevel = spdlog::level::critical; break;
    case Level::Error: spdlogLevel = spdlog::level::err; break;
    case Level::Warn: spdlogLevel = spdlog::level::warn; break;
    case Level::Info: spdlogLevel = spdlog::level::info; break;
    case Level::Debug: spdlogLevel = spdlog::level::debug; break;
    case Level::Trace: spdlogLevel = spdlog::level::trace; break;
    default:
        OPENSIM_THROW(Exception, "Internal error.");
    }
    return defaultLogger->should_log(spdlogLevel);
}

void Logger::addFileSink(const std::string& filepath) {
    // this method is either called by the file log auto-initializer, which
    // should now be disabled, or by downstream code trying to manually specify
    // a file sink
    //
    // downstream callers would find it quite surprising if the auto-initializer
    // runs *after* they manually specify a log, so just disable it
    fileSinkAutoInitDisabled = true;

    if (m_filesink) {
        defaultLogger->warn("Already logging to file '{}'; log file not added. Call "
             "removeFileSink() first.", m_filesink->filename());
        return;
    }

    // check if file can be opened at the specified path if not return meaningful
    // warning rather than bubble the exception up.
    try {
        m_filesink =
                std::make_shared<spdlog::sinks::basic_file_sink_mt>(filepath);
    }
    catch (...) {
        defaultLogger->warn("Can't open file '{}' for writing. Log file will not be created. "
             "Check that you have write permissions to the specified path.",
                filepath);
        return;
    }
    addSinkInternal(m_filesink);
}

void Logger::removeFileSink() {
    // if this method is called, then we are probably at a point in the
    // application's lifetime where automatic log allocation is going to cause
    // confusion.
    //
    // callers will be surpised if, after calling this method, auto
    // initialization happens afterwards and the log file still exists - even
    // if they called it to remove some manually-specified log
    fileSinkAutoInitDisabled = true;

    if (m_filesink == nullptr) {
        return;
    }

    removeSinkInternal(
            std::static_pointer_cast<spdlog::sinks::sink>(m_filesink));
    m_filesink.reset();
}

void Logger::addSink(const std::shared_ptr<LogSink> sink) {
    addSinkInternal(std::static_pointer_cast<spdlog::sinks::sink>(sink));
}

void Logger::removeSink(const std::shared_ptr<LogSink> sink) {
    removeSinkInternal(std::static_pointer_cast<spdlog::sinks::sink>(sink));
}

<|endoftext|>


<commit_before>/*
 * Copyright (c) 2017 Trail of Bits, Inc.
 *
 * 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 <glog/logging.h>
#include <gflags/gflags.h>

#include <iomanip>
#include <iostream>
#include <memory>
#include <string>
#include <sstream>

#include <llvm/IR/Function.h>
#include <llvm/IR/GlobalVariable.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>

#include <remill/Arch/Arch.h>
#include <remill/BC/Util.h>
#include <remill/BC/Version.h>

#include "mcsema/Arch/Arch.h"
#include "mcsema/BC/Lift.h"
#include "mcsema/BC/Util.h"

#ifndef LLVM_VERSION_STRING
# define LLVM_VERSION_STRING LLVM_VERSION_MAJOR << "." << LLVM_VERSION_MINOR
#endif

#ifndef MCSEMA_VERSION_STRING
# define MCSEMA_VERSION_STRING "unknown"
#endif  // MCSEMA_VERSION_STRING

#ifndef MCSEMA_BRANCH_NAME
# define MCSEMA_BRANCH_NAME "unknown"
#endif  // MCSEMA_BRANCH_NAME

DECLARE_string(arch);
DECLARE_string(os);

DEFINE_string(cfg, "", "Path to the CFG file containing code to lift.");

DEFINE_string(output, "", "Output bitcode file name.");

DEFINE_string(abi_library, "", "Path to an LLVM bitcode or IR file that contains "
                               "external library definitions for the C/C++ ABI.");

DECLARE_bool(version);

DECLARE_bool(disable_optimizer);
DECLARE_bool(keep_memops);
DECLARE_bool(explicit_args);
DECLARE_string(pc_annotation);

DEFINE_bool(list_supported, false,
            "List instructions that can be lifted.");
DEFINE_bool(legacy_mode, false,
            "Try to make the output bitcode resemble the original McSema.");

namespace {

static void PrintVersion(void) {
  std::cout
      << "This is mcsema-lift version: " << MCSEMA_VERSION_STRING << std::endl
      << "Built from branch: " << MCSEMA_BRANCH_NAME << std::endl
      << "Using LLVM " << LLVM_VERSION_STRING << std::endl;
}

// Print a list of instructions that Remill can lift.
static void PrintSupportedInstructions(void) {
  remill::ForEachISel(mcsema::gModule,
                      [=](llvm::GlobalVariable *isel, llvm::Function *) {
                        std::cout << isel->getName().str() << std::endl;
                      });
}

static std::unique_ptr<llvm::Module> gLibrary;

// Load in a separate bitcode or IR library, and copy function and variable
// declarations from that library into our module. We can use this feature
// to provide better type information to McSema.
static void LoadLibraryIntoModule(const std::string &path) {
  gLibrary.reset(remill::LoadModuleFromFile(mcsema::gContext, path));
  mcsema::gArch->PrepareModuleDataLayout(gLibrary);

  // Declare the functions from the library in McSema's target module.
  for (auto &func : *gLibrary) {
    auto func_name = func.getName();
    if (func_name.startswith("__mcsema") || func_name.startswith("__remill")) {
      continue;
    }

    if (!func.hasExternalLinkage()) {
      continue;
    }

    if (mcsema::gModule->getFunction(func_name)) {
      continue;
    }

    auto dest_func = llvm::Function::Create(
        func.getFunctionType(), func.getLinkage(),
        func_name, mcsema::gModule);

    dest_func->copyAttributesFrom(&func);
    dest_func->setVisibility(func.getVisibility());
  }

  // Declare the global variables from the library in McSema's target module.
  for (auto &var : gLibrary->globals()) {
    auto var_name = var.getName();
    if (var_name.startswith("__mcsema") || var_name.startswith("__remill")) {
      continue;
    }

    if (!var.hasExternalLinkage()) {
      continue;
    }

    if (mcsema::gModule->getGlobalVariable(var_name)) {
      continue;
    }

    auto dest_var = new llvm::GlobalVariable(
        *mcsema::gModule, var.getType()->getElementType(),
        var.isConstant(), var.getLinkage(), nullptr,
        var_name, nullptr, var.getThreadLocalMode(),
        var.getType()->getAddressSpace());

    dest_var->copyAttributesFrom(&var);
  }
}

// Remove unused functions and globals brought in from the library.
static void UnloadLibraryFromModule(void) {
  for (auto &func : *gLibrary) {
    auto our_func = mcsema::gModule->getFunction(func.getName());
    if (our_func && !our_func->hasNUsesOrMore(1)) {
      our_func->eraseFromParent();
    }
  }

  for (auto &var : gLibrary->globals()) {
    auto our_var = mcsema::gModule->getGlobalVariable(var.getName());
    if (our_var && !our_var->hasNUsesOrMore(1)) {
      our_var->eraseFromParent();
    }
  }
}

}  // namespace

int main(int argc, char *argv[]) {
  std::stringstream ss;
  ss << std::endl << std::endl
     << "  " << argv[0] << " \\" << std::endl
     << "    --output OUTPUT_BC_FILE \\" << std::endl
     << "    --arch ARCH_NAME \\" << std::endl
     << "    --os OS_NAME \\" << std::endl
     << "    --cfg CFG_FILE \\" << std::endl

     // This option is very useful for debugging McSema-lifted bitcode. It
     // injects so-called breakpoint functions before every lifted instruction.
     // For example, the the instruction at PC `0xf00` is lifted, then this
     // option will inject a call to `breakpoint_f00`. With this feature, we
     // can add breakpoints in a debugger on these breakpoint functions, and
     // know that they correspond to locations in the original program.
     << "    [--add_breakpoints] \\" << std::endl

     // This option injects a function call before every lifted instruction.
     // This function is implemented in the McSema runtime and it prints the
     // values of the general purpose registers to `stderr`.
     << "    [--add_reg_tracer] \\" << std::endl

     // This option tells McSema not to optimize the bitcode. This is useful
     // for debugging, especially in conjunction with `--add_breakpoints`.
     << "    [--disable_optimizer] \\" << std::endl

     // This option tells McSema not to lower Remill's memory access intrinsic
     // functions into LLVM `load` and `store` instructions.
     << "    [--keep_memops] \\" << std::endl

     // There are roughly two ways of using McSema-lifted bitcode. The default
     // use case is to compile the bitcode into an executable that behaves like
     // the original program. The other use case is to do some kind of static
     // analysis, e.g. with KLEE. In this use case, calls to external functions
     // are emulated so that we also try to explicitly lift parameter passing.
     // This mode of passing arguments explicitly is enabled by
     // `--explicit_args`, and in situations where we have no knowledge of the
     // argument counts expected by an external function, we fall back on
     // passing `--explicit_args_count` number of arguments to that function.
     << "    [--explicit_args] \\" << std::endl
     << "    [--explicit_args_count NUM_ARGS_FOR_EXTERNALS] \\" << std::endl

     // McSema doesn't have type information about externals, and so it assumes all
     // externals operate on integer-typed arguments, and return integer values.
     // This is wrong in many ways, but tends to work out about 80% of the time.
     // To get McSema better information about externals, one should create a
     // C or C++ file with the declarations of the externals (perhaps by
     // `#include`ing standard headers). Then, should add to this file something
     // like:
     //         __attribute__((used))
     //         void *__mcsema_externs[] = {
     //           (void *) external_func_name_1,
     //           (void *) external_func_name_2,
     //           ...
     //         };
     // And compile this file to bitcode using `remill-clang-M.m` (Major.minor).
     // This bitcode file will then be the source of type information for
     // McSema.
     << "    [--abi_library BITCODE_FILE] \\" << std::endl

     // Annotate each LLVM IR instruction with some metadata that includes the
     // original program counter. The name of the LLVM metadats is
     // `PC_METADATA_ID`. This is enabled by default with `--legacy_mode`,
     // which sets `--pc_annotation` to be `mcsema_real_eip`.
     << "    [--pc_annotation PC_METADATA_ID] \\" << std::endl

     // Try to produce bitcode that looks like McSema version 1. This enables
     // `--explicit_args` and `--pc_annotation`.
     << "    [--legacy_mode] \\" << std::endl

     // Print a list of the instructions that can be lifted.
     << "    [--list-supported]" << std::endl

     // Print the version and exit.
     << "    [--version]" << std::endl
     << std::endl;

  google::InitGoogleLogging(argv[0]);
  google::SetUsageMessage(ss.str());
  google::ParseCommandLineFlags(&argc, &argv, true);

  if (FLAGS_version) {
    PrintVersion();
    return EXIT_SUCCESS;
  }

  CHECK(!FLAGS_os.empty())
      << "Must specify an operating system name to --os.";

  CHECK(!FLAGS_arch.empty())
      << "Must specify a machine code architecture name to --arch.";

  CHECK(!FLAGS_cfg.empty())
      << "Must specify the path to a CFG file to --cfg.";

  mcsema::gContext = new llvm::LLVMContext;

  CHECK(mcsema::InitArch(FLAGS_os, FLAGS_arch))
      << "Cannot initialize for arch " << FLAGS_arch
      << " and OS " << FLAGS_os << std::endl;

  if (FLAGS_legacy_mode) {
    LOG_IF(WARNING, FLAGS_keep_memops)
        << "Disabling --keep_memops in legacy mode.";
    FLAGS_keep_memops = false;

    LOG_IF(WARNING, !FLAGS_explicit_args)
        << "Enabling --explicit_args in legacy mode.";
    FLAGS_explicit_args = true;

    LOG_IF(WARNING, !FLAGS_pc_annotation.empty())
        << "Changing --pc_annotation to mcsema_real_eip in legacy mode.";
    FLAGS_pc_annotation = "mcsema_real_eip";

    LOG_IF(WARNING, FLAGS_disable_optimizer)
        << "Re-enabling the optimizer in legacy mode.";
    FLAGS_disable_optimizer = false;
  }

  mcsema::gModule = remill::LoadTargetSemantics(mcsema::gContext);
  mcsema::gArch->PrepareModule(mcsema::gModule);

  // Load in a special library before CFG processing. This affects the
  // renaming of exported functions.
  if (!FLAGS_abi_library.empty()) {
    LoadLibraryIntoModule(FLAGS_abi_library);
  }

  auto cfg_module = mcsema::ReadProtoBuf(
      FLAGS_cfg, (mcsema::gArch->address_size / 8));

  if (FLAGS_list_supported) {
    PrintSupportedInstructions();
  }

  CHECK(mcsema::LiftCodeIntoModule(cfg_module))
      << "Unable to lift CFG from " << FLAGS_cfg << " into module "
      << FLAGS_output;

  if (!FLAGS_abi_library.empty()) {
    UnloadLibraryFromModule();
    gLibrary.reset(nullptr);
  }

  remill::StoreModuleToFile(mcsema::gModule, FLAGS_output);

  google::ShutDownCommandLineFlags();
  google::ShutdownGoogleLogging();

  return EXIT_SUCCESS;
}
<commit_msg>Suport multiple ABI libraries on command line (#388)<commit_after>/*
 * Copyright (c) 2017 Trail of Bits, Inc.
 *
 * 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 <glog/logging.h>
#include <gflags/gflags.h>

#include <iomanip>
#include <iostream>
#include <memory>
#include <string>
#include <sstream>

#include <llvm/IR/Function.h>
#include <llvm/IR/GlobalVariable.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>

#include <remill/Arch/Arch.h>
#include <remill/BC/Util.h>
#include <remill/BC/Version.h>

#include "mcsema/Arch/Arch.h"
#include "mcsema/BC/Lift.h"
#include "mcsema/BC/Util.h"

#ifndef LLVM_VERSION_STRING
# define LLVM_VERSION_STRING LLVM_VERSION_MAJOR << "." << LLVM_VERSION_MINOR
#endif

#ifndef MCSEMA_VERSION_STRING
# define MCSEMA_VERSION_STRING "unknown"
#endif  // MCSEMA_VERSION_STRING

#ifndef MCSEMA_BRANCH_NAME
# define MCSEMA_BRANCH_NAME "unknown"
#endif  // MCSEMA_BRANCH_NAME

DECLARE_string(arch);
DECLARE_string(os);

DEFINE_string(cfg, "", "Path to the CFG file containing code to lift.");

DEFINE_string(output, "", "Output bitcode file name.");

// Using ',' as it will work well enough on Windows and Linux
// Other suggestions were ':', which is a path character on Windows
// and ';', which is an end of statement escape on Linux shells
static const char kPathDelimeter = ',';
DEFINE_string(abi_libraries, "", "Path to one or more bitcode files that contain "
                               "external library definitions for the C/C++ ABI.");

DECLARE_bool(version);

DECLARE_bool(disable_optimizer);
DECLARE_bool(keep_memops);
DECLARE_bool(explicit_args);
DECLARE_string(pc_annotation);

DEFINE_bool(list_supported, false,
            "List instructions that can be lifted.");
DEFINE_bool(legacy_mode, false,
            "Try to make the output bitcode resemble the original McSema.");

namespace {

static void PrintVersion(void) {
  std::cout
      << "This is mcsema-lift version: " << MCSEMA_VERSION_STRING << std::endl
      << "Built from branch: " << MCSEMA_BRANCH_NAME << std::endl
      << "Using LLVM " << LLVM_VERSION_STRING << std::endl;
}

// Print a list of instructions that Remill can lift.
static void PrintSupportedInstructions(void) {
  remill::ForEachISel(mcsema::gModule,
                      [=](llvm::GlobalVariable *isel, llvm::Function *) {
                        std::cout << isel->getName().str() << std::endl;
                      });
}

// simple function to split a string on a delimeter
// used to separate comma separated arguments
std::vector<std::string>
split(const std::string &s, const char delim) {

	std::vector<std::string> res;
	std::string rem;
	std::istringstream instream(s);

	while(std::getline(instream, rem, delim)) {
		res.push_back(rem);
	}

	return res;
}

static std::unique_ptr<llvm::Module> gLibrary;

// Load in a separate bitcode or IR library, and copy function and variable
// declarations from that library into our module. We can use this feature
// to provide better type information to McSema.
static void LoadLibraryIntoModule(const std::string &path) {
  gLibrary.reset(remill::LoadModuleFromFile(mcsema::gContext, path));
  mcsema::gArch->PrepareModuleDataLayout(gLibrary);

  // Declare the functions from the library in McSema's target module.
  for (auto &func : *gLibrary) {
    auto func_name = func.getName();
    if (func_name.startswith("__mcsema") || func_name.startswith("__remill")) {
      continue;
    }

    if (!func.hasExternalLinkage()) {
      continue;
    }

    if (mcsema::gModule->getFunction(func_name)) {
      continue;
    }

    auto dest_func = llvm::Function::Create(
        func.getFunctionType(), func.getLinkage(),
        func_name, mcsema::gModule);

    dest_func->copyAttributesFrom(&func);
    dest_func->setVisibility(func.getVisibility());
  }

  // Declare the global variables from the library in McSema's target module.
  for (auto &var : gLibrary->globals()) {
    auto var_name = var.getName();
    if (var_name.startswith("__mcsema") || var_name.startswith("__remill")) {
      continue;
    }

    if (!var.hasExternalLinkage()) {
      continue;
    }

    if (mcsema::gModule->getGlobalVariable(var_name)) {
      continue;
    }

    auto dest_var = new llvm::GlobalVariable(
        *mcsema::gModule, var.getType()->getElementType(),
        var.isConstant(), var.getLinkage(), nullptr,
        var_name, nullptr, var.getThreadLocalMode(),
        var.getType()->getAddressSpace());

    dest_var->copyAttributesFrom(&var);
  }
}

// Remove unused functions and globals brought in from the library.
static void UnloadLibraryFromModule(void) {
  for (auto &func : *gLibrary) {
    auto our_func = mcsema::gModule->getFunction(func.getName());
    if (our_func && !our_func->hasNUsesOrMore(1)) {
      our_func->eraseFromParent();
    }
  }

  for (auto &var : gLibrary->globals()) {
    auto our_var = mcsema::gModule->getGlobalVariable(var.getName());
    if (our_var && !our_var->hasNUsesOrMore(1)) {
      our_var->eraseFromParent();
    }
  }
}

}  // namespace

int main(int argc, char *argv[]) {
  std::stringstream ss;
  ss << std::endl << std::endl
     << "  " << argv[0] << " \\" << std::endl
     << "    --output OUTPUT_BC_FILE \\" << std::endl
     << "    --arch ARCH_NAME \\" << std::endl
     << "    --os OS_NAME \\" << std::endl
     << "    --cfg CFG_FILE \\" << std::endl

     // This option is very useful for debugging McSema-lifted bitcode. It
     // injects so-called breakpoint functions before every lifted instruction.
     // For example, the the instruction at PC `0xf00` is lifted, then this
     // option will inject a call to `breakpoint_f00`. With this feature, we
     // can add breakpoints in a debugger on these breakpoint functions, and
     // know that they correspond to locations in the original program.
     << "    [--add_breakpoints] \\" << std::endl

     // This option injects a function call before every lifted instruction.
     // This function is implemented in the McSema runtime and it prints the
     // values of the general purpose registers to `stderr`.
     << "    [--add_reg_tracer] \\" << std::endl

     // This option tells McSema not to optimize the bitcode. This is useful
     // for debugging, especially in conjunction with `--add_breakpoints`.
     << "    [--disable_optimizer] \\" << std::endl

     // This option tells McSema not to lower Remill's memory access intrinsic
     // functions into LLVM `load` and `store` instructions.
     << "    [--keep_memops] \\" << std::endl

     // There are roughly two ways of using McSema-lifted bitcode. The default
     // use case is to compile the bitcode into an executable that behaves like
     // the original program. The other use case is to do some kind of static
     // analysis, e.g. with KLEE. In this use case, calls to external functions
     // are emulated so that we also try to explicitly lift parameter passing.
     // This mode of passing arguments explicitly is enabled by
     // `--explicit_args`, and in situations where we have no knowledge of the
     // argument counts expected by an external function, we fall back on
     // passing `--explicit_args_count` number of arguments to that function.
     << "    [--explicit_args] \\" << std::endl
     << "    [--explicit_args_count NUM_ARGS_FOR_EXTERNALS] \\" << std::endl

     // McSema doesn't have type information about externals, and so it assumes all
     // externals operate on integer-typed arguments, and return integer values.
     // This is wrong in many ways, but tends to work out about 80% of the time.
     // To get McSema better information about externals, one should create a
     // C or C++ file with the declarations of the externals (perhaps by
     // `#include`ing standard headers). Then, should add to this file something
     // like:
     //         __attribute__((used))
     //         void *__mcsema_externs[] = {
     //           (void *) external_func_name_1,
     //           (void *) external_func_name_2,
     //           ...
     //         };
     // And compile this file to bitcode using `remill-clang-M.m` (Major.minor).
     // This bitcode file will then be the source of type information for
     // McSema.
     //
     // One may want multiple such files, such as one for libc, one for exception
     // handling and one for zlib, and so on. McSema supports loading multiple
     // ABI library definitions via a ';' separated list of paths
     << "    [--abi_libraries BITCODE_FILE[" << kPathDelimeter <<
        "BITCODE_FILE" << kPathDelimeter << "...] ] \\" << std::endl

     // Annotate each LLVM IR instruction with some metadata that includes the
     // original program counter. The name of the LLVM metadats is
     // `PC_METADATA_ID`. This is enabled by default with `--legacy_mode`,
     // which sets `--pc_annotation` to be `mcsema_real_eip`.
     << "    [--pc_annotation PC_METADATA_ID] \\" << std::endl

     // Try to produce bitcode that looks like McSema version 1. This enables
     // `--explicit_args` and `--pc_annotation`.
     << "    [--legacy_mode] \\" << std::endl

     // Print a list of the instructions that can be lifted.
     << "    [--list-supported]" << std::endl

     // Print the version and exit.
     << "    [--version]" << std::endl
     << std::endl;

  google::InitGoogleLogging(argv[0]);
  google::SetUsageMessage(ss.str());
  google::ParseCommandLineFlags(&argc, &argv, true);

  if (FLAGS_version) {
    PrintVersion();
    return EXIT_SUCCESS;
  }

  CHECK(!FLAGS_os.empty())
      << "Must specify an operating system name to --os.";

  CHECK(!FLAGS_arch.empty())
      << "Must specify a machine code architecture name to --arch.";

  CHECK(!FLAGS_cfg.empty())
      << "Must specify the path to a CFG file to --cfg.";

  mcsema::gContext = new llvm::LLVMContext;

  CHECK(mcsema::InitArch(FLAGS_os, FLAGS_arch))
      << "Cannot initialize for arch " << FLAGS_arch
      << " and OS " << FLAGS_os << std::endl;

  if (FLAGS_legacy_mode) {
    LOG_IF(WARNING, FLAGS_keep_memops)
        << "Disabling --keep_memops in legacy mode.";
    FLAGS_keep_memops = false;

    LOG_IF(WARNING, !FLAGS_explicit_args)
        << "Enabling --explicit_args in legacy mode.";
    FLAGS_explicit_args = true;

    LOG_IF(WARNING, !FLAGS_pc_annotation.empty())
        << "Changing --pc_annotation to mcsema_real_eip in legacy mode.";
    FLAGS_pc_annotation = "mcsema_real_eip";

    LOG_IF(WARNING, FLAGS_disable_optimizer)
        << "Re-enabling the optimizer in legacy mode.";
    FLAGS_disable_optimizer = false;
  }

  mcsema::gModule = remill::LoadTargetSemantics(mcsema::gContext);
  mcsema::gArch->PrepareModule(mcsema::gModule);

  // Load in a special library before CFG processing. This affects the
  // renaming of exported functions.
  if (!FLAGS_abi_libraries.empty()) {
    auto abi_libs = split(FLAGS_abi_libraries, kPathDelimeter);
    for(const auto &abi_lib : abi_libs) {
      LOG(INFO) << "Loading ABI Library: " << abi_lib << "\n";
      LoadLibraryIntoModule(abi_lib);
    }
  }

  auto cfg_module = mcsema::ReadProtoBuf(
      FLAGS_cfg, (mcsema::gArch->address_size / 8));

  if (FLAGS_list_supported) {
    PrintSupportedInstructions();
  }

  CHECK(mcsema::LiftCodeIntoModule(cfg_module))
      << "Unable to lift CFG from " << FLAGS_cfg << " into module "
      << FLAGS_output;

  if (!FLAGS_abi_libraries.empty()) {
    UnloadLibraryFromModule();
    gLibrary.reset(nullptr);
  }

  remill::StoreModuleToFile(mcsema::gModule, FLAGS_output);

  google::ShutDownCommandLineFlags();
  google::ShutdownGoogleLogging();

  return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>#include <fastrtps/Domain.h>
#include <fastrtps/participant/Participant.h>
#include <fastrtps/attributes/ParticipantAttributes.h>
#include <fastrtps/publisher/Publisher.h>

#include <fastrtps/fastrtps_fwd.h>
#include <fastrtps/publisher/PublisherListener.h>
#include <fastrtps/attributes/PublisherAttributes.h>

#include <fastrtps/subscriber/Subscriber.h>
#include <fastrtps/subscriber/SubscriberListener.h>
#include <fastrtps/attributes/SubscriberAttributes.h>

#include "types/HelloWorldType.h"
#include <string>
#include <chrono>
#include <thread>
#include <fastrtps/rtps/RTPSDomain.h>

#include <fastrtps/rtps/builtin/data/WriterProxyData.h>
#include <fastrtps/rtps/common/CDRMessage_t.h>

#include <fastrtps/rtps/reader/ReaderListener.h>
#include <fastrtps/rtps/reader/RTPSReader.h>
#include <fastrtps/rtps/reader/StatefulReader.h>

#include <boost/interprocess/detail/os_thread_functions.hpp>
#include <gtest/gtest.h>

class pub_dummy_listener:public PublisherListener
{
public:

	void onPublicationMatched(Publisher* /*pub*/, MatchingInfo& /*info*/){};

};
class sub_dummy_listener:public SubscriberListener
{
public:

	void onNewDataMessage(Subscriber* /*sub */){}
	void onSubscriptionMatched(Subscriber* /*sub*/, MatchingInfo& /*info*/){};

};

class gettopicnamesandtypesReaderListener:public ReaderListener
{
	public:
	void onNewCacheChangeAdded(RTPSReader* reader, const CacheChange_t* const change_in){
		CacheChange_t* change = (CacheChange_t*) change_in;
		if(change->kind == ALIVE){
			WriterProxyData proxyData;
			CDRMessage_t tempMsg;
			tempMsg.msg_endian = change->serializedPayload.encapsulation == PL_CDR_BE ? BIGEND:LITTLEEND;
			tempMsg.length = change->serializedPayload.length;
			memcpy(tempMsg.buffer,change->serializedPayload.data,tempMsg.length);
			if(proxyData.readFromCDRMessage(&tempMsg)){
				topicNtypes[proxyData.m_topicName].insert(proxyData.m_typeName);
			}
		}
	}
	std::map<std::string,std::set<std::string>> topicNtypes;

};

TEST(ros2features, EDPSlaveReaderAttachment)
{
	Participant *my_participant;
	Publisher *my_publisher;
	ParticipantAttributes p_attr;
	PublisherAttributes pub_attr;
	HelloWorldType my_type;
	pub_dummy_listener my_dummy_listener;
	ReaderListener *slave_listener = new(ReaderListener);
	bool result;
	p_attr.rtps.builtin.domainId = (uint32_t)boost::interprocess::ipcdetail::get_current_process_id() % 230;
	my_participant = Domain::createParticipant(p_attr);

	ASSERT_NE(my_participant, nullptr);
	ASSERT_EQ(Domain::registerType(my_participant, &my_type), true);

	pub_attr.topic.topicKind = NO_KEY;
	pub_attr.topic.topicDataType = "HelloWorldType";
	my_publisher = Domain::createPublisher(my_participant, pub_attr, &my_dummy_listener);
	ASSERT_NE(my_publisher, nullptr);

	std::pair<StatefulReader*,StatefulReader*> EDP_Readers = my_participant->getEDPReaders();
	InfectableReaderListener* target = dynamic_cast<InfectableReaderListener*>(EDP_Readers.first->getListener());
	result = target->hasReaderAttached();
	ASSERT_EQ(result,false);

	target->attachListener(slave_listener);
	result = target->hasReaderAttached();
	ASSERT_EQ(result, true);

}

TEST(ros2features, PubSubPoll)
{
	const uint8_t max_elements = 4;
	Participant *my_participant;
	Publisher* pub_array[max_elements];
	Subscriber* sub_array[max_elements];
	ParticipantAttributes p_attr;
	HelloWorldType my_type;
	pub_dummy_listener p_listener_array[max_elements];
	sub_dummy_listener s_listener_array[max_elements];

	std::string str("HelloWorldType");
	char *cstr = new  char[str.length()+1];
	std::strcpy(cstr,str.c_str());

	p_attr.rtps.builtin.domainId = (uint32_t)boost::interprocess::ipcdetail::get_current_process_id() % 230 + 2;
	my_participant = Domain::createParticipant(p_attr);
	//Register type

	ASSERT_NE(my_participant, nullptr);
	ASSERT_EQ(Domain::registerType(my_participant, &my_type), true);

	for(int i=0; i<max_elements;i++){
		//Create Publisher
		PublisherAttributes p_attr;
		p_attr.topic.topicKind = NO_KEY;
		p_attr.topic.topicDataType = "HelloWorldType";
		p_attr.topic.topicName = "HelloWorldType";
		//configPublisher(p_attr);
		pub_array[i] = Domain::createPublisher(my_participant,p_attr, &p_listener_array[i]);
		ASSERT_NE(pub_array[i],nullptr);
		//Poll no.Pubs
		ASSERT_EQ(my_participant->get_no_publishers(cstr),i+1);
		//Create Subscriber
		SubscriberAttributes s_attr;
		s_attr.topic.topicKind = NO_KEY;
		s_attr.topic.topicDataType = "HelloWorldType";
		s_attr.topic.topicName = "HelloWorldType";
		//configSubscriber(s_attr);
	 	sub_array[i] =Domain::createSubscriber(my_participant,s_attr, &s_listener_array[i]);
		ASSERT_NE(sub_array[i],nullptr);
		//Poll no.Subs
		ASSERT_EQ(my_participant->get_no_subscribers(cstr),i+1);
	}

}
TEST(ros2features, SlaveListenerCallback){
	Participant *my_participant;
	Publisher *my_publisher;
	ParticipantAttributes p_attr;
	PublisherAttributes pub_attr;
	HelloWorldType my_type;
	pub_dummy_listener my_dummy_listener;
	gettopicnamesandtypesReaderListener* slave_listener = new(gettopicnamesandtypesReaderListener);
	gettopicnamesandtypesReaderListener* slave_target;
	bool result;
	p_attr.rtps.builtin.domainId = (uint32_t)boost::interprocess::ipcdetail::get_current_process_id() % 230 + 35;
	my_participant = Domain::createParticipant(p_attr);

	ASSERT_NE(my_participant, nullptr);
	ASSERT_EQ(Domain::registerType(my_participant, &my_type), true);

	pub_attr.topic.topicKind = NO_KEY;
	pub_attr.topic.topicDataType = "HelloWorldType";
	my_publisher = Domain::createPublisher(my_participant, pub_attr, &my_dummy_listener);
	ASSERT_NE(my_publisher, nullptr);

	std::pair<StatefulReader*,StatefulReader*> EDP_Readers = my_participant->getEDPReaders();
	InfectableReaderListener* target = dynamic_cast<InfectableReaderListener*>(EDP_Readers.second->getListener());
	target->attachListener(slave_listener);
	result = target->hasReaderAttached();
	ASSERT_EQ(result,true);
	slave_target =  dynamic_cast<gettopicnamesandtypesReaderListener*>(target->getAttachedListener());
	ASSERT_EQ(slave_target->topicNtypes.size(),0);

	Participant *my_participant2;
	Publisher *my_publisher2;
	ParticipantAttributes p_attr2;
	PublisherAttributes pub_attr2;
	pub_dummy_listener my_dummy_listener2;
	p_attr2.rtps.builtin.domainId = (uint32_t)boost::interprocess::ipcdetail::get_current_process_id() % 230 + 35;
	my_participant2 = Domain::createParticipant(p_attr2);

	ASSERT_NE(my_participant2, nullptr);
	ASSERT_EQ(Domain::registerType(my_participant2, &my_type), true);

	pub_attr2.topic.topicKind = NO_KEY;
	pub_attr2.topic.topicDataType = "HelloWorldType";
	my_publisher2 = Domain::createPublisher(my_participant2, pub_attr2, &my_dummy_listener2);
	ASSERT_NE(my_publisher2, nullptr);
	ASSERT_EQ(slave_target->topicNtypes.size(),1);

}

int main(int argc, char **argv)
{
	testing::InitGoogleTest(&argc, argv);
	return RUN_ALL_TESTS();
}
<commit_msg>Add mutex to tests<commit_after>#include <fastrtps/Domain.h>
#include <fastrtps/participant/Participant.h>
#include <fastrtps/attributes/ParticipantAttributes.h>
#include <fastrtps/publisher/Publisher.h>

#include <fastrtps/fastrtps_fwd.h>
#include <fastrtps/publisher/PublisherListener.h>
#include <fastrtps/attributes/PublisherAttributes.h>

#include <fastrtps/subscriber/Subscriber.h>
#include <fastrtps/subscriber/SubscriberListener.h>
#include <fastrtps/attributes/SubscriberAttributes.h>

#include "types/HelloWorldType.h"
#include <string>
#include <chrono>
#include <thread>
#include <fastrtps/rtps/RTPSDomain.h>

#include <fastrtps/rtps/builtin/data/WriterProxyData.h>
#include <fastrtps/rtps/common/CDRMessage_t.h>

#include <fastrtps/rtps/reader/ReaderListener.h>
#include <fastrtps/rtps/reader/RTPSReader.h>
#include <fastrtps/rtps/reader/StatefulReader.h>

#include <boost/interprocess/detail/os_thread_functions.hpp>

#include <gtest/gtest.h>

#include <mutex>

class pub_dummy_listener:public PublisherListener
{
public:

	void onPublicationMatched(Publisher* /*pub*/, MatchingInfo& /*info*/){};

};
class sub_dummy_listener:public SubscriberListener
{
public:

	void onNewDataMessage(Subscriber* /*sub */){}
	void onSubscriptionMatched(Subscriber* /*sub*/, MatchingInfo& /*info*/){};

};

class gettopicnamesandtypesReaderListener:public ReaderListener
{
	public:
	std::mutex mapmutex;
	std::map<std::string,std::set<std::string>> topicNtypes;
	void onNewCacheChangeAdded(RTPSReader* reader, const CacheChange_t* const change_in){
		CacheChange_t* change = (CacheChange_t*) change_in;
		if(change->kind == ALIVE){
			WriterProxyData proxyData;
			CDRMessage_t tempMsg;
			tempMsg.msg_endian = change->serializedPayload.encapsulation == PL_CDR_BE ? BIGEND:LITTLEEND;
			tempMsg.length = change->serializedPayload.length;
			memcpy(tempMsg.buffer,change->serializedPayload.data,tempMsg.length);
			if(proxyData.readFromCDRMessage(&tempMsg)){
				mapmutex.lock();
				topicNtypes[proxyData.m_topicName].insert(proxyData.m_typeName);
				mapmutex.unlock();
			}
		}
	}
};

TEST(ros2features, EDPSlaveReaderAttachment)
{
	Participant *my_participant;
	Publisher *my_publisher;
	ParticipantAttributes p_attr;
	PublisherAttributes pub_attr;
	HelloWorldType my_type;
	pub_dummy_listener my_dummy_listener;
	ReaderListener *slave_listener = new(ReaderListener);
	bool result;
	p_attr.rtps.builtin.domainId = (uint32_t)boost::interprocess::ipcdetail::get_current_process_id() % 230;
	my_participant = Domain::createParticipant(p_attr);

	ASSERT_NE(my_participant, nullptr);
	ASSERT_EQ(Domain::registerType(my_participant, &my_type), true);

	pub_attr.topic.topicKind = NO_KEY;
	pub_attr.topic.topicDataType = "HelloWorldType";
	my_publisher = Domain::createPublisher(my_participant, pub_attr, &my_dummy_listener);
	ASSERT_NE(my_publisher, nullptr);

	std::pair<StatefulReader*,StatefulReader*> EDP_Readers = my_participant->getEDPReaders();
	InfectableReaderListener* target = dynamic_cast<InfectableReaderListener*>(EDP_Readers.first->getListener());
	result = target->hasReaderAttached();
	ASSERT_EQ(result,false);

	target->attachListener(slave_listener);
	result = target->hasReaderAttached();
	ASSERT_EQ(result, true);

}

TEST(ros2features, PubSubPoll)
{
	const uint8_t max_elements = 4;
	Participant *my_participant;
	Publisher* pub_array[max_elements];
	Subscriber* sub_array[max_elements];
	ParticipantAttributes p_attr;
	HelloWorldType my_type;
	pub_dummy_listener p_listener_array[max_elements];
	sub_dummy_listener s_listener_array[max_elements];

	std::string str("HelloWorldType");
	char *cstr = new  char[str.length()+1];
	std::strcpy(cstr,str.c_str());

	p_attr.rtps.builtin.domainId = (uint32_t)boost::interprocess::ipcdetail::get_current_process_id() % 230 + 2;
	my_participant = Domain::createParticipant(p_attr);
	//Register type

	ASSERT_NE(my_participant, nullptr);
	ASSERT_EQ(Domain::registerType(my_participant, &my_type), true);

	for(int i=0; i<max_elements;i++){
		//Create Publisher
		PublisherAttributes p_attr;
		p_attr.topic.topicKind = NO_KEY;
		p_attr.topic.topicDataType = "HelloWorldType";
		p_attr.topic.topicName = "HelloWorldType";
		//configPublisher(p_attr);
		pub_array[i] = Domain::createPublisher(my_participant,p_attr, &p_listener_array[i]);
		ASSERT_NE(pub_array[i],nullptr);
		//Poll no.Pubs
		ASSERT_EQ(my_participant->get_no_publishers(cstr),i+1);
		//Create Subscriber
		SubscriberAttributes s_attr;
		s_attr.topic.topicKind = NO_KEY;
		s_attr.topic.topicDataType = "HelloWorldType";
		s_attr.topic.topicName = "HelloWorldType";
		//configSubscriber(s_attr);
	 	sub_array[i] =Domain::createSubscriber(my_participant,s_attr, &s_listener_array[i]);
		ASSERT_NE(sub_array[i],nullptr);
		//Poll no.Subs
		ASSERT_EQ(my_participant->get_no_subscribers(cstr),i+1);
	}

}
TEST(ros2features, SlaveListenerCallback){
	Participant *my_participant;
	Publisher *my_publisher;
	ParticipantAttributes p_attr;
	PublisherAttributes pub_attr;
	HelloWorldType my_type;
	pub_dummy_listener my_dummy_listener;
	gettopicnamesandtypesReaderListener* slave_listener = new(gettopicnamesandtypesReaderListener);
	gettopicnamesandtypesReaderListener* slave_target;
	bool result;
	p_attr.rtps.builtin.domainId = (uint32_t)boost::interprocess::ipcdetail::get_current_process_id() % 230 + 35;
	my_participant = Domain::createParticipant(p_attr);

	ASSERT_NE(my_participant, nullptr);
	ASSERT_EQ(Domain::registerType(my_participant, &my_type), true);

	pub_attr.topic.topicKind = NO_KEY;
	pub_attr.topic.topicDataType = "HelloWorldType";
	my_publisher = Domain::createPublisher(my_participant, pub_attr, &my_dummy_listener);
	ASSERT_NE(my_publisher, nullptr);

	std::pair<StatefulReader*,StatefulReader*> EDP_Readers = my_participant->getEDPReaders();
	InfectableReaderListener* target = dynamic_cast<InfectableReaderListener*>(EDP_Readers.second->getListener());
	target->attachListener(slave_listener);
	result = target->hasReaderAttached();
	ASSERT_EQ(result,true);
	slave_target =  dynamic_cast<gettopicnamesandtypesReaderListener*>(target->getAttachedListener());

	slave_target->mapmutex.lock();
	ASSERT_EQ(slave_target->topicNtypes.size(),0);
	slave_target->mapmutex.unlock();

	Participant *my_participant2;
	Publisher *my_publisher2;
	ParticipantAttributes p_attr2;
	PublisherAttributes pub_attr2;
	pub_dummy_listener my_dummy_listener2;
	p_attr2.rtps.builtin.domainId = (uint32_t)boost::interprocess::ipcdetail::get_current_process_id() % 230 + 35;
	my_participant2 = Domain::createParticipant(p_attr2);

	ASSERT_NE(my_participant2, nullptr);
	ASSERT_EQ(Domain::registerType(my_participant2, &my_type), true);

	pub_attr2.topic.topicKind = NO_KEY;
	pub_attr2.topic.topicDataType = "HelloWorldType";
	my_publisher2 = Domain::createPublisher(my_participant2, pub_attr2, &my_dummy_listener2);
	ASSERT_NE(my_publisher2, nullptr);

	slave_target->mapmutex.lock();
	ASSERT_EQ(slave_target->topicNtypes.size(),1);
	slave_target->mapmutex.unlock();

}

int main(int argc, char **argv)
{
	testing::InitGoogleTest(&argc, argv);
	return RUN_ALL_TESTS();
}
<|endoftext|>