<commit_before>#include "HTTPClientAndroidImpl.h"
#include "components/Exceptions.h"
#include "utils/AndroidUtils.h"
#include "utils/JNIUniqueGlobalRef.h"
#include "utils/Log.h"

#include <chrono>
#include <limits>
#include <regex>

#include <boost/lexical_cast.hpp>

namespace carto {

    struct HTTPClient::AndroidImpl::URLClass {
        JNIUniqueGlobalRef<jclass> clazz;
        jmethodID constructor;
        jmethodID openConnection;

        explicit URLClass(JNIEnv* jenv) {
            clazz = JNIUniqueGlobalRef<jclass>(jenv->NewGlobalRef(jenv->FindClass("java/net/URL")));
            constructor = jenv->GetMethodID(clazz, "<init>", "(Ljava/lang/String;)V");
            openConnection = jenv->GetMethodID(clazz, "openConnection", "()Ljava/net/URLConnection;");
        }
    };

    struct HTTPClient::AndroidImpl::HttpURLConnectionClass {
        JNIUniqueGlobalRef<jclass> clazz;
        jmethodID setRequestMethod;
        jmethodID setDoInput;
        jmethodID setDoOutput;
        jmethodID setUseCaches;
        jmethodID setAllowUserInteraction;
        jmethodID setInstanceFollowRedirects;
        jmethodID setRequestProperty;
        jmethodID connect;
        jmethodID disconnect;
        jmethodID getResponseCode;
        jmethodID getHeaderFieldKey;
        jmethodID getHeaderField;
        jmethodID getInputStream;
        jmethodID getOutputStream;
        jmethodID getErrorStream;

        explicit HttpURLConnectionClass(JNIEnv* jenv) {
            clazz = JNIUniqueGlobalRef<jclass>(jenv->NewGlobalRef(jenv->FindClass("java/net/HttpURLConnection")));
            setRequestMethod = jenv->GetMethodID(clazz, "setRequestMethod", "(Ljava/lang/String;)V");
            setDoInput = jenv->GetMethodID(clazz, "setDoInput", "(Z)V");
            setDoOutput = jenv->GetMethodID(clazz, "setDoOutput", "(Z)V");
            setUseCaches = jenv->GetMethodID(clazz, "setUseCaches", "(Z)V");
            setAllowUserInteraction = jenv->GetMethodID(clazz, "setAllowUserInteraction", "(Z)V");
            setInstanceFollowRedirects = jenv->GetMethodID(clazz, "setInstanceFollowRedirects", "(Z)V");
            setRequestProperty = jenv->GetMethodID(clazz, "setRequestProperty", "(Ljava/lang/String;Ljava/lang/String;)V");
            connect = jenv->GetMethodID(clazz, "connect", "()V");
            disconnect = jenv->GetMethodID(clazz, "disconnect", "()V");
            getResponseCode = jenv->GetMethodID(clazz, "getResponseCode", "()I");
            getHeaderFieldKey = jenv->GetMethodID(clazz, "getHeaderFieldKey", "(I)Ljava/lang/String;");
            getHeaderField = jenv->GetMethodID(clazz, "getHeaderField", "(I)Ljava/lang/String;");
            getInputStream = jenv->GetMethodID(clazz, "getInputStream", "()Ljava/io/InputStream;");
            getOutputStream = jenv->GetMethodID(clazz, "getOutputStream", "()Ljava/io/OutputStream;");
            getErrorStream = jenv->GetMethodID(clazz, "getErrorStream", "()Ljava/io/InputStream;");
        }
    };

    struct HTTPClient::AndroidImpl::BufferedInputStreamClass {
        JNIUniqueGlobalRef<jclass> clazz;
        jmethodID constructor;
        jmethodID read;

        explicit BufferedInputStreamClass(JNIEnv* jenv) {
            clazz = JNIUniqueGlobalRef<jclass>(jenv->NewGlobalRef(jenv->FindClass("java/io/BufferedInputStream")));
            constructor = jenv->GetMethodID(clazz, "<init>", "(Ljava/io/InputStream;)V");
            read = jenv->GetMethodID(clazz, "read", "([B)I");
        }
    };

    struct HTTPClient::AndroidImpl::OutputStreamClass {
        JNIUniqueGlobalRef<jclass> clazz;
        jmethodID write;
        jmethodID close;

        explicit OutputStreamClass(JNIEnv* jenv) {
            clazz = JNIUniqueGlobalRef<jclass>(jenv->NewGlobalRef(jenv->FindClass("java/io/OutputStream")));
            write = jenv->GetMethodID(clazz, "write", "([B)V");
            close = jenv->GetMethodID(clazz, "close", "()V");
        }
    };

    HTTPClient::AndroidImpl::AndroidImpl(bool log) :
        _log(log)
    {
    }

    bool HTTPClient::AndroidImpl::makeRequest(const HTTPClient::Request& request, HeadersFn headersFn, DataFn dataFn) const {
        JNIEnv* jenv = AndroidUtils::GetCurrentThreadJNIEnv();
        AndroidUtils::JNILocalFrame jframe(jenv, 32, "HTTPClient::AndroidImpl::HTTPClientAndroidImpl");
        if (!jframe.isValid()) {
            Log::Error("HTTPClient::AndroidImpl::makeRequest: JNILocalFrame not valid");
            throw std::runtime_error("JNILocalFrame not valid");
        }

        {
            std::lock_guard<std::mutex> lock(_Mutex);
            if (!_URLClass) {
                _URLClass = std::unique_ptr<URLClass>(new URLClass(jenv));
            }
            if (!_HttpURLConnectionClass) {
                _HttpURLConnectionClass = std::unique_ptr<HttpURLConnectionClass>(new HttpURLConnectionClass(jenv));
            }
            if (!_BufferedInputStreamClass) {
                _BufferedInputStreamClass = std::unique_ptr<BufferedInputStreamClass>(new BufferedInputStreamClass(jenv));
            }
            if (!_OutputStreamClass) {
                _OutputStreamClass = std::unique_ptr<OutputStreamClass>(new OutputStreamClass(jenv));
            }
        }

        // Create URL
        jobject url = jenv->NewObject(_URLClass->clazz, _URLClass->constructor, jenv->NewStringUTF(request.url.c_str()));
        if (jenv->ExceptionCheck()) {
            jenv->ExceptionClear();
            throw NetworkException("Invalid URL", request.url);
        }

        // Open HTTP connection
        jobject conn = jenv->CallObjectMethod(url, _URLClass->openConnection);
        if (jenv->ExceptionCheck()) {
            jenv->ExceptionClear();
            throw NetworkException("Unable to open connection", request.url);
        }

        // Configure connection parameters
        jenv->CallVoidMethod(conn, _HttpURLConnectionClass->setRequestMethod, jenv->NewStringUTF("GET"));
        jenv->CallVoidMethod(conn, _HttpURLConnectionClass->setDoInput, (jboolean)true);
        jenv->CallVoidMethod(conn, _HttpURLConnectionClass->setDoOutput, (jboolean)!request.contentType.empty());
        jenv->CallVoidMethod(conn, _HttpURLConnectionClass->setUseCaches, (jboolean)false);
        jenv->CallVoidMethod(conn, _HttpURLConnectionClass->setAllowUserInteraction, (jboolean)false);
        jenv->CallVoidMethod(conn, _HttpURLConnectionClass->setAllowUserInteraction, (jboolean)true);

        // Set request headers
        for (auto it = request.headers.begin(); it != request.headers.end(); it++) {
            jstring key = jenv->NewStringUTF(it->first.c_str());
            jstring value = jenv->NewStringUTF(it->second.c_str());
            jenv->CallVoidMethod(conn, _HttpURLConnectionClass->setRequestProperty, key, value);
        }

        // If Content-Type is set, write request body to output stream
        if (!request.contentType.empty()) {
            jobject outputStream = jenv->CallObjectMethod(conn, _HttpURLConnectionClass->getOutputStream);
            if (jenv->ExceptionCheck()) {
                jenv->ExceptionClear();
                throw NetworkException("Unable to get output stream", request.url);
            }

            jbyteArray jbuf = jenv->NewByteArray(request.body.size());
            jenv->SetByteArrayRegion(jbuf, 0, request.body.size(), reinterpret_cast<const jbyte*>(request.body.data()));

            jenv->CallVoidMethod(outputStream, _OutputStreamClass->write, jbuf);
            if (jenv->ExceptionCheck()) {
                jenv->ExceptionClear();
                throw NetworkException("Unable to write data", request.url);
            }
            jenv->CallVoidMethod(outputStream, _OutputStreamClass->close);
            if (jenv->ExceptionCheck()) {
                jenv->ExceptionClear();
                throw NetworkException("Unable to write data", request.url);
            }
        }

        // Connect
        jenv->CallVoidMethod(conn, _HttpURLConnectionClass->connect);
        if (jenv->ExceptionCheck()) {
            jenv->ExceptionClear();
            throw NetworkException("Unable to connect", request.url);
        }

        // Read response header
        jint responseCode = jenv->CallIntMethod(conn, _HttpURLConnectionClass->getResponseCode);
        std::map<std::string, std::string> headers;
        for (int i = 0; true; i++) {
            jstring key = (jstring)jenv->CallObjectMethod(conn, _HttpURLConnectionClass->getHeaderFieldKey, (jint)i);
            if (!key) {
                break;
            }
            jstring value = (jstring)jenv->CallObjectMethod(conn, _HttpURLConnectionClass->getHeaderField, (jint)i);
            const char* keyStr = jenv->GetStringUTFChars(key, NULL);
            const char* valueStr = jenv->GetStringUTFChars(value, NULL);
            headers[keyStr] = valueStr;
            jenv->ReleaseStringUTFChars(value, valueStr);
            jenv->ReleaseStringUTFChars(key, keyStr);
        }

        bool cancel = false;
        if (!headersFn(responseCode, headers)) {
            cancel = true;
        }

        // Read Content-Length
        std::uint64_t contentLength = std::numeric_limits<std::uint64_t>::max();
        auto it = headers.find("Content-Length");
        if (it != headers.end()) {
            contentLength = boost::lexical_cast<std::uint64_t>(it->second);
        }

        // Create BufferedInputStream for data
        jobject inputStream = jenv->CallObjectMethod(conn, _HttpURLConnectionClass->getInputStream);
        if (jenv->ExceptionCheck()) {
            jenv->ExceptionClear();
            inputStream = jenv->CallObjectMethod(conn, _HttpURLConnectionClass->getErrorStream);
        }

        jobject bufferedInputStream = jenv->NewObject(_BufferedInputStreamClass->clazz, _BufferedInputStreamClass->constructor, inputStream);

        jbyte buf[4096];
        jbyteArray jbuf = jenv->NewByteArray(sizeof(buf));

        for (std::uint64_t offset = 0; offset < contentLength && !cancel; ) {
            jint numBytesRead = jenv->CallIntMethod(bufferedInputStream, _BufferedInputStreamClass->read, jbuf);
            if (jenv->ExceptionCheck()) {
                jenv->ExceptionClear();
                throw NetworkException("Unable to read data", request.url);
            }
            if (numBytesRead < 0) {
                if (contentLength == std::numeric_limits<std::uint64_t>::max()) {
                    break;
                }
                throw NetworkException("Unable to read full data", request.url);
            }
            jenv->GetByteArrayRegion(jbuf, 0, numBytesRead, buf);

            if (!dataFn(reinterpret_cast<const unsigned char*>(&buf[0]), numBytesRead)) {
                cancel = true;
            }

            offset += numBytesRead;
        }

        // Done
        jenv->CallVoidMethod(conn, _HttpURLConnectionClass->disconnect);
        return !cancel;
    }

    std::unique_ptr<HTTPClient::AndroidImpl::URLClass> HTTPClient::AndroidImpl::_URLClass;
    std::unique_ptr<HTTPClient::AndroidImpl::HttpURLConnectionClass> HTTPClient::AndroidImpl::_HttpURLConnectionClass;
    std::unique_ptr<HTTPClient::AndroidImpl::BufferedInputStreamClass> HTTPClient::AndroidImpl::_BufferedInputStreamClass;
    std::unique_ptr<HTTPClient::AndroidImpl::OutputStreamClass> HTTPClient::AndroidImpl::_OutputStreamClass;

    std::mutex HTTPClient::AndroidImpl::_Mutex;

}
<commit_msg>Explictly close HTTP input stream when done with the connection, fixes Android strict mode issues<commit_after>#include "HTTPClientAndroidImpl.h"
#include "components/Exceptions.h"
#include "utils/AndroidUtils.h"
#include "utils/JNIUniqueGlobalRef.h"
#include "utils/Log.h"

#include <chrono>
#include <limits>
#include <regex>

#include <boost/lexical_cast.hpp>

namespace carto {

    struct HTTPClient::AndroidImpl::URLClass {
        JNIUniqueGlobalRef<jclass> clazz;
        jmethodID constructor;
        jmethodID openConnection;

        explicit URLClass(JNIEnv* jenv) {
            clazz = JNIUniqueGlobalRef<jclass>(jenv->NewGlobalRef(jenv->FindClass("java/net/URL")));
            constructor = jenv->GetMethodID(clazz, "<init>", "(Ljava/lang/String;)V");
            openConnection = jenv->GetMethodID(clazz, "openConnection", "()Ljava/net/URLConnection;");
        }
    };

    struct HTTPClient::AndroidImpl::HttpURLConnectionClass {
        JNIUniqueGlobalRef<jclass> clazz;
        jmethodID setRequestMethod;
        jmethodID setDoInput;
        jmethodID setDoOutput;
        jmethodID setUseCaches;
        jmethodID setAllowUserInteraction;
        jmethodID setInstanceFollowRedirects;
        jmethodID setRequestProperty;
        jmethodID connect;
        jmethodID disconnect;
        jmethodID getResponseCode;
        jmethodID getHeaderFieldKey;
        jmethodID getHeaderField;
        jmethodID getInputStream;
        jmethodID getOutputStream;
        jmethodID getErrorStream;

        explicit HttpURLConnectionClass(JNIEnv* jenv) {
            clazz = JNIUniqueGlobalRef<jclass>(jenv->NewGlobalRef(jenv->FindClass("java/net/HttpURLConnection")));
            setRequestMethod = jenv->GetMethodID(clazz, "setRequestMethod", "(Ljava/lang/String;)V");
            setDoInput = jenv->GetMethodID(clazz, "setDoInput", "(Z)V");
            setDoOutput = jenv->GetMethodID(clazz, "setDoOutput", "(Z)V");
            setUseCaches = jenv->GetMethodID(clazz, "setUseCaches", "(Z)V");
            setAllowUserInteraction = jenv->GetMethodID(clazz, "setAllowUserInteraction", "(Z)V");
            setInstanceFollowRedirects = jenv->GetMethodID(clazz, "setInstanceFollowRedirects", "(Z)V");
            setRequestProperty = jenv->GetMethodID(clazz, "setRequestProperty", "(Ljava/lang/String;Ljava/lang/String;)V");
            connect = jenv->GetMethodID(clazz, "connect", "()V");
            disconnect = jenv->GetMethodID(clazz, "disconnect", "()V");
            getResponseCode = jenv->GetMethodID(clazz, "getResponseCode", "()I");
            getHeaderFieldKey = jenv->GetMethodID(clazz, "getHeaderFieldKey", "(I)Ljava/lang/String;");
            getHeaderField = jenv->GetMethodID(clazz, "getHeaderField", "(I)Ljava/lang/String;");
            getInputStream = jenv->GetMethodID(clazz, "getInputStream", "()Ljava/io/InputStream;");
            getOutputStream = jenv->GetMethodID(clazz, "getOutputStream", "()Ljava/io/OutputStream;");
            getErrorStream = jenv->GetMethodID(clazz, "getErrorStream", "()Ljava/io/InputStream;");
        }
    };

    struct HTTPClient::AndroidImpl::BufferedInputStreamClass {
        JNIUniqueGlobalRef<jclass> clazz;
        jmethodID constructor;
        jmethodID read;
        jmethodID close;

        explicit BufferedInputStreamClass(JNIEnv* jenv) {
            clazz = JNIUniqueGlobalRef<jclass>(jenv->NewGlobalRef(jenv->FindClass("java/io/BufferedInputStream")));
            constructor = jenv->GetMethodID(clazz, "<init>", "(Ljava/io/InputStream;)V");
            read = jenv->GetMethodID(clazz, "read", "([B)I");
            close = jenv->GetMethodID(clazz, "close", "()V");
        }
    };

    struct HTTPClient::AndroidImpl::OutputStreamClass {
        JNIUniqueGlobalRef<jclass> clazz;
        jmethodID write;
        jmethodID close;

        explicit OutputStreamClass(JNIEnv* jenv) {
            clazz = JNIUniqueGlobalRef<jclass>(jenv->NewGlobalRef(jenv->FindClass("java/io/OutputStream")));
            write = jenv->GetMethodID(clazz, "write", "([B)V");
            close = jenv->GetMethodID(clazz, "close", "()V");
        }
    };

    HTTPClient::AndroidImpl::AndroidImpl(bool log) :
        _log(log)
    {
    }

    bool HTTPClient::AndroidImpl::makeRequest(const HTTPClient::Request& request, HeadersFn headersFn, DataFn dataFn) const {
        JNIEnv* jenv = AndroidUtils::GetCurrentThreadJNIEnv();
        AndroidUtils::JNILocalFrame jframe(jenv, 32, "HTTPClient::AndroidImpl::HTTPClientAndroidImpl");
        if (!jframe.isValid()) {
            Log::Error("HTTPClient::AndroidImpl::makeRequest: JNILocalFrame not valid");
            throw std::runtime_error("JNILocalFrame not valid");
        }

        {
            std::lock_guard<std::mutex> lock(_Mutex);
            if (!_URLClass) {
                _URLClass = std::unique_ptr<URLClass>(new URLClass(jenv));
            }
            if (!_HttpURLConnectionClass) {
                _HttpURLConnectionClass = std::unique_ptr<HttpURLConnectionClass>(new HttpURLConnectionClass(jenv));
            }
            if (!_BufferedInputStreamClass) {
                _BufferedInputStreamClass = std::unique_ptr<BufferedInputStreamClass>(new BufferedInputStreamClass(jenv));
            }
            if (!_OutputStreamClass) {
                _OutputStreamClass = std::unique_ptr<OutputStreamClass>(new OutputStreamClass(jenv));
            }
        }

        // Create URL
        jobject url = jenv->NewObject(_URLClass->clazz, _URLClass->constructor, jenv->NewStringUTF(request.url.c_str()));
        if (jenv->ExceptionCheck()) {
            jenv->ExceptionClear();
            throw NetworkException("Invalid URL", request.url);
        }

        // Open HTTP connection
        jobject conn = jenv->CallObjectMethod(url, _URLClass->openConnection);
        if (jenv->ExceptionCheck()) {
            jenv->ExceptionClear();
            throw NetworkException("Unable to open connection", request.url);
        }

        // Configure connection parameters
        jenv->CallVoidMethod(conn, _HttpURLConnectionClass->setRequestMethod, jenv->NewStringUTF("GET"));
        jenv->CallVoidMethod(conn, _HttpURLConnectionClass->setDoInput, (jboolean)true);
        jenv->CallVoidMethod(conn, _HttpURLConnectionClass->setDoOutput, (jboolean)!request.contentType.empty());
        jenv->CallVoidMethod(conn, _HttpURLConnectionClass->setUseCaches, (jboolean)false);
        jenv->CallVoidMethod(conn, _HttpURLConnectionClass->setAllowUserInteraction, (jboolean)false);
        jenv->CallVoidMethod(conn, _HttpURLConnectionClass->setAllowUserInteraction, (jboolean)true);

        // Set request headers
        for (auto it = request.headers.begin(); it != request.headers.end(); it++) {
            jstring key = jenv->NewStringUTF(it->first.c_str());
            jstring value = jenv->NewStringUTF(it->second.c_str());
            jenv->CallVoidMethod(conn, _HttpURLConnectionClass->setRequestProperty, key, value);
        }

        // If Content-Type is set, write request body to output stream
        if (!request.contentType.empty()) {
            jobject outputStream = jenv->CallObjectMethod(conn, _HttpURLConnectionClass->getOutputStream);
            if (jenv->ExceptionCheck()) {
                jenv->ExceptionClear();
                throw NetworkException("Unable to get output stream", request.url);
            }

            jbyteArray jbuf = jenv->NewByteArray(request.body.size());
            jenv->SetByteArrayRegion(jbuf, 0, request.body.size(), reinterpret_cast<const jbyte*>(request.body.data()));

            jenv->CallVoidMethod(outputStream, _OutputStreamClass->write, jbuf);
            if (jenv->ExceptionCheck()) {
                jenv->ExceptionClear();
                throw NetworkException("Unable to write data", request.url);
            }
            jenv->CallVoidMethod(outputStream, _OutputStreamClass->close);
            if (jenv->ExceptionCheck()) {
                jenv->ExceptionClear();
                throw NetworkException("Unable to write data", request.url);
            }
        }

        // Connect
        jenv->CallVoidMethod(conn, _HttpURLConnectionClass->connect);
        if (jenv->ExceptionCheck()) {
            jenv->ExceptionClear();
            throw NetworkException("Unable to connect", request.url);
        }

        // Read response header
        jint responseCode = jenv->CallIntMethod(conn, _HttpURLConnectionClass->getResponseCode);
        std::map<std::string, std::string> headers;
        for (int i = 0; true; i++) {
            jstring key = (jstring)jenv->CallObjectMethod(conn, _HttpURLConnectionClass->getHeaderFieldKey, (jint)i);
            if (!key) {
                break;
            }
            jstring value = (jstring)jenv->CallObjectMethod(conn, _HttpURLConnectionClass->getHeaderField, (jint)i);
            const char* keyStr = jenv->GetStringUTFChars(key, NULL);
            const char* valueStr = jenv->GetStringUTFChars(value, NULL);
            headers[keyStr] = valueStr;
            jenv->ReleaseStringUTFChars(value, valueStr);
            jenv->ReleaseStringUTFChars(key, keyStr);
        }

        bool cancel = false;
        if (!headersFn(responseCode, headers)) {
            cancel = true;
        }

        // Read Content-Length
        std::uint64_t contentLength = std::numeric_limits<std::uint64_t>::max();
        auto it = headers.find("Content-Length");
        if (it != headers.end()) {
            contentLength = boost::lexical_cast<std::uint64_t>(it->second);
        }

        // Create BufferedInputStream for data
        jobject inputStream = jenv->CallObjectMethod(conn, _HttpURLConnectionClass->getInputStream);
        if (jenv->ExceptionCheck()) {
            jenv->ExceptionClear();
            inputStream = jenv->CallObjectMethod(conn, _HttpURLConnectionClass->getErrorStream);
        }

        jobject bufferedInputStream = jenv->NewObject(_BufferedInputStreamClass->clazz, _BufferedInputStreamClass->constructor, inputStream);

        try {
            jbyte buf[4096];
            jbyteArray jbuf = jenv->NewByteArray(sizeof(buf));

            for (std::uint64_t offset = 0; offset < contentLength && !cancel; ) {
                jint numBytesRead = jenv->CallIntMethod(bufferedInputStream, _BufferedInputStreamClass->read, jbuf);
                if (jenv->ExceptionCheck()) {
                    jenv->ExceptionClear();
                    throw NetworkException("Unable to read data", request.url);
                }
                if (numBytesRead < 0) {
                    if (contentLength == std::numeric_limits<std::uint64_t>::max()) {
                        break;
                    }
                    throw NetworkException("Unable to read full data", request.url);
                }
                jenv->GetByteArrayRegion(jbuf, 0, numBytesRead, buf);

                if (!dataFn(reinterpret_cast<const unsigned char*>(&buf[0]), numBytesRead)) {
                    cancel = true;
                }

                offset += numBytesRead;
            }
        }
        catch (...) {
            jenv->CallVoidMethod(bufferedInputStream, _BufferedInputStreamClass->close);
            if (jenv->ExceptionCheck()) {
                jenv->ExceptionClear();
            }
            jenv->CallVoidMethod(conn, _HttpURLConnectionClass->disconnect);
            throw;
        }

        // Done
        jenv->CallVoidMethod(bufferedInputStream, _BufferedInputStreamClass->close);
        if (jenv->ExceptionCheck()) {
            jenv->ExceptionClear();
        }
        jenv->CallVoidMethod(conn, _HttpURLConnectionClass->disconnect);
        return !cancel;
    }

    std::unique_ptr<HTTPClient::AndroidImpl::URLClass> HTTPClient::AndroidImpl::_URLClass;
    std::unique_ptr<HTTPClient::AndroidImpl::HttpURLConnectionClass> HTTPClient::AndroidImpl::_HttpURLConnectionClass;
    std::unique_ptr<HTTPClient::AndroidImpl::BufferedInputStreamClass> HTTPClient::AndroidImpl::_BufferedInputStreamClass;
    std::unique_ptr<HTTPClient::AndroidImpl::OutputStreamClass> HTTPClient::AndroidImpl::_OutputStreamClass;

    std::mutex HTTPClient::AndroidImpl::_Mutex;

}
<|endoftext|>


<commit_before>//===- FunctionImport.cpp - ThinLTO Summary-based Function Import ---------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements Function import based on summaries.
//
//===----------------------------------------------------------------------===//

#include "llvm/Transforms/IPO/FunctionImport.h"

#include "llvm/ADT/StringSet.h"
#include "llvm/IR/AutoUpgrade.h"
#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Module.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/Linker/Linker.h"
#include "llvm/Object/FunctionIndexObjectFile.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Transforms/Utils/FunctionImportUtils.h"

#include <map>

using namespace llvm;

#define DEBUG_TYPE "function-import"

/// Limit on instruction count of imported functions.
static cl::opt<unsigned> ImportInstrLimit(
    "import-instr-limit", cl::init(100), cl::Hidden, cl::value_desc("N"),
    cl::desc("Only import functions with less than N instructions"));

// Load lazily a module from \p FileName in \p Context.
static std::unique_ptr<Module> loadFile(const std::string &FileName,
                                        LLVMContext &Context) {
  SMDiagnostic Err;
  DEBUG(dbgs() << "Loading '" << FileName << "'\n");
  // Metadata isn't loaded until functions are imported, to minimize
  // the memory overhead.
  std::unique_ptr<Module> Result =
      getLazyIRFileModule(FileName, Err, Context,
                          /* ShouldLazyLoadMetadata = */ true);
  if (!Result) {
    Err.print("function-import", errs());
    return nullptr;
  }

  return Result;
}

namespace {
/// Helper to load on demand a Module from file and cache it for subsequent
/// queries. It can be used with the FunctionImporter.
class ModuleLazyLoaderCache {
  /// Cache of lazily loaded module for import.
  StringMap<std::unique_ptr<Module>> ModuleMap;

  /// Retrieve a Module from the cache or lazily load it on demand.
  std::function<std::unique_ptr<Module>(StringRef FileName)> createLazyModule;

public:
  /// Create the loader, Module will be initialized in \p Context.
  ModuleLazyLoaderCache(std::function<
      std::unique_ptr<Module>(StringRef FileName)> createLazyModule)
      : createLazyModule(createLazyModule) {}

  /// Retrieve a Module from the cache or lazily load it on demand.
  Module &operator()(StringRef FileName);

  std::unique_ptr<Module> takeModule(StringRef FileName) {
    auto I = ModuleMap.find(FileName);
    assert(I != ModuleMap.end());
    std::unique_ptr<Module> Ret = std::move(I->second);
    ModuleMap.erase(I);
    return Ret;
  }
};

// Get a Module for \p FileName from the cache, or load it lazily.
Module &ModuleLazyLoaderCache::operator()(StringRef Identifier) {
  auto &Module = ModuleMap[Identifier];
  if (!Module)
    Module = createLazyModule(Identifier);
  return *Module;
}
} // anonymous namespace

/// Walk through the instructions in \p F looking for external
/// calls not already in the \p CalledFunctions set. If any are
/// found they are added to the \p Worklist for importing.
static void findExternalCalls(const Module &DestModule, Function &F,
                              const FunctionInfoIndex &Index,
                              StringSet<> &CalledFunctions,
                              SmallVector<StringRef, 64> &Worklist) {
  // We need to suffix internal function calls imported from other modules,
  // prepare the suffix ahead of time.
  std::string Suffix;
  if (F.getParent() != &DestModule)
    Suffix =
        (Twine(".llvm.") +
         Twine(Index.getModuleId(F.getParent()->getModuleIdentifier()))).str();

  for (auto &BB : F) {
    for (auto &I : BB) {
      if (isa<CallInst>(I)) {
        auto CalledFunction = cast<CallInst>(I).getCalledFunction();
        // Insert any new external calls that have not already been
        // added to set/worklist.
        if (!CalledFunction || !CalledFunction->hasName())
          continue;
        // Ignore intrinsics early
        if (CalledFunction->isIntrinsic()) {
          assert(CalledFunction->getIntrinsicID() != 0);
          continue;
        }
        auto ImportedName = CalledFunction->getName();
        auto Renamed = (ImportedName + Suffix).str();
        // Rename internal functions
        if (CalledFunction->hasInternalLinkage()) {
          ImportedName = Renamed;
        }
        // Compute the global identifier used in the function index.
        auto CalledFunctionGlobalID = Function::getGlobalIdentifier(
            CalledFunction->getName(), CalledFunction->getLinkage(),
            CalledFunction->getParent()->getSourceFileName());
        auto It = CalledFunctions.insert(CalledFunctionGlobalID);
        if (!It.second) {
          // This is a call to a function we already considered, skip.
          continue;
        }
        // Ignore functions already present in the destination module
        auto *SrcGV = DestModule.getNamedValue(ImportedName);
        if (SrcGV) {
          if (GlobalAlias *SGA = dyn_cast<GlobalAlias>(SrcGV))
            SrcGV = SGA->getBaseObject();
          assert(isa<Function>(SrcGV) && "Name collision during import");
          if (!cast<Function>(SrcGV)->isDeclaration()) {
            DEBUG(dbgs() << DestModule.getModuleIdentifier() << ": Ignoring "
                         << ImportedName << " already in DestinationModule\n");
            continue;
          }
        }

        Worklist.push_back(It.first->getKey());
        DEBUG(dbgs() << DestModule.getModuleIdentifier()
                     << ": Adding callee for : " << ImportedName << " : "
                     << F.getName() << "\n");
      }
    }
  }
}

// Helper function: given a worklist and an index, will process all the worklist
// and decide what to import based on the summary information.
//
// Nothing is actually imported, functions are materialized in their source
// module and analyzed there.
//
// \p ModuleToFunctionsToImportMap is filled with the set of Function to import
// per Module.
static void GetImportList(Module &DestModule,
                          SmallVector<StringRef, 64> &Worklist,
                          StringSet<> &CalledFunctions,
                          std::map<StringRef, DenseSet<const GlobalValue *>>
                              &ModuleToFunctionsToImportMap,
                          const FunctionInfoIndex &Index,
                          ModuleLazyLoaderCache &ModuleLoaderCache) {
  while (!Worklist.empty()) {
    auto CalledFunctionName = Worklist.pop_back_val();
    DEBUG(dbgs() << DestModule.getModuleIdentifier() << ": Process import for "
                 << CalledFunctionName << "\n");

    // Try to get a summary for this function call.
    auto InfoList = Index.findFunctionInfoList(CalledFunctionName);
    if (InfoList == Index.end()) {
      DEBUG(dbgs() << DestModule.getModuleIdentifier() << ": No summary for "
                   << CalledFunctionName << " Ignoring.\n");
      continue;
    }
    assert(!InfoList->second.empty() && "No summary, error at import?");

    // Comdat can have multiple entries, FIXME: what do we do with them?
    auto &Info = InfoList->second[0];
    assert(Info && "Nullptr in list, error importing summaries?\n");

    auto *Summary = Info->functionSummary();
    if (!Summary) {
      // FIXME: in case we are lazyloading summaries, we can do it now.
      DEBUG(dbgs() << DestModule.getModuleIdentifier()
                   << ": Missing summary for  " << CalledFunctionName
                   << ", error at import?\n");
      llvm_unreachable("Missing summary");
    }

    if (Summary->instCount() > ImportInstrLimit) {
      DEBUG(dbgs() << DestModule.getModuleIdentifier() << ": Skip import of "
                   << CalledFunctionName << " with " << Summary->instCount()
                   << " instructions (limit " << ImportInstrLimit << ")\n");
      continue;
    }

    // Get the module path from the summary.
    auto ModuleIdentifier = Summary->modulePath();
    DEBUG(dbgs() << DestModule.getModuleIdentifier() << ": Importing "
                 << CalledFunctionName << " from " << ModuleIdentifier << "\n");

    auto &SrcModule = ModuleLoaderCache(ModuleIdentifier);

    // The function that we will import!
    GlobalValue *SGV = SrcModule.getNamedValue(CalledFunctionName);

    if (!SGV) {
      // The function is referenced by a global identifier, which has the
      // source file name prepended for functions that were originally local
      // in the source module. Strip any prepended name to recover the original
      // name in the source module.
      std::pair<StringRef, StringRef> Split = CalledFunctionName.split(":");
      SGV = SrcModule.getNamedValue(Split.second);
      assert(SGV && "Can't find function to import in source module");
    }
    if (!SGV) {
      report_fatal_error(Twine("Can't load function '") + CalledFunctionName +
                         "' in Module '" + SrcModule.getModuleIdentifier() +
                         "', error in the summary?\n");
    }

    Function *F = dyn_cast<Function>(SGV);
    if (!F && isa<GlobalAlias>(SGV)) {
      auto *SGA = dyn_cast<GlobalAlias>(SGV);
      F = dyn_cast<Function>(SGA->getBaseObject());
      CalledFunctionName = F->getName();
    }
    assert(F && "Imported Function is ... not a Function");

    // We cannot import weak_any functions/aliases without possibly affecting
    // the order they are seen and selected by the linker, changing program
    // semantics.
    if (SGV->hasWeakAnyLinkage()) {
      DEBUG(dbgs() << DestModule.getModuleIdentifier()
                   << ": Ignoring import request for weak-any "
                   << (isa<Function>(SGV) ? "function " : "alias ")
                   << CalledFunctionName << " from "
                   << SrcModule.getModuleIdentifier() << "\n");
      continue;
    }

    // Add the function to the import list
    auto &Entry = ModuleToFunctionsToImportMap[SrcModule.getModuleIdentifier()];
    Entry.insert(F);

    // Process the newly imported functions and add callees to the worklist.
    F->materialize();
    findExternalCalls(DestModule, *F, Index, CalledFunctions, Worklist);
  }
}

// Automatically import functions in Module \p DestModule based on the summaries
// index.
//
// The current implementation imports every called functions that exists in the
// summaries index.
bool FunctionImporter::importFunctions(Module &DestModule) {
  DEBUG(dbgs() << "Starting import for Module "
               << DestModule.getModuleIdentifier() << "\n");
  unsigned ImportedCount = 0;

  /// First step is collecting the called external functions.
  StringSet<> CalledFunctions;
  SmallVector<StringRef, 64> Worklist;
  for (auto &F : DestModule) {
    if (F.isDeclaration() || F.hasFnAttribute(Attribute::OptimizeNone))
      continue;
    findExternalCalls(DestModule, F, Index, CalledFunctions, Worklist);
  }
  if (Worklist.empty())
    return false;

  /// Second step: for every call to an external function, try to import it.

  // Linker that will be used for importing function
  Linker TheLinker(DestModule);

  // Map of Module -> List of Function to import from the Module
  std::map<StringRef, DenseSet<const GlobalValue *>>
      ModuleToFunctionsToImportMap;

  // Analyze the summaries and get the list of functions to import by
  // populating ModuleToFunctionsToImportMap
  ModuleLazyLoaderCache ModuleLoaderCache(ModuleLoader);
  GetImportList(DestModule, Worklist, CalledFunctions,
                ModuleToFunctionsToImportMap, Index, ModuleLoaderCache);
  assert(Worklist.empty() && "Worklist hasn't been flushed in GetImportList");

  // Do the actual import of functions now, one Module at a time
  for (auto &FunctionsToImportPerModule : ModuleToFunctionsToImportMap) {
    // Get the module for the import
    auto &FunctionsToImport = FunctionsToImportPerModule.second;
    std::unique_ptr<Module> SrcModule =
        ModuleLoaderCache.takeModule(FunctionsToImportPerModule.first);
    assert(&DestModule.getContext() == &SrcModule->getContext() &&
           "Context mismatch");

    // If modules were created with lazy metadata loading, materialize it
    // now, before linking it (otherwise this will be a noop).
    SrcModule->materializeMetadata();
    UpgradeDebugInfo(*SrcModule);

    // Link in the specified functions.
    if (TheLinker.linkInModule(std::move(SrcModule), Linker::Flags::None,
                               &Index, &FunctionsToImport))
      report_fatal_error("Function Import: link error");

    ImportedCount += FunctionsToImport.size();
  }

  DEBUG(dbgs() << "Imported " << ImportedCount << " functions for Module "
               << DestModule.getModuleIdentifier() << "\n");
  return ImportedCount;
}

/// Summary file to use for function importing when using -function-import from
/// the command line.
static cl::opt<std::string>
    SummaryFile("summary-file",
                cl::desc("The summary file to use for function importing."));

static void diagnosticHandler(const DiagnosticInfo &DI) {
  raw_ostream &OS = errs();
  DiagnosticPrinterRawOStream DP(OS);
  DI.print(DP);
  OS << '\n';
}

/// Parse the function index out of an IR file and return the function
/// index object if found, or nullptr if not.
static std::unique_ptr<FunctionInfoIndex>
getFunctionIndexForFile(StringRef Path, std::string &Error,
                        DiagnosticHandlerFunction DiagnosticHandler) {
  std::unique_ptr<MemoryBuffer> Buffer;
  ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
      MemoryBuffer::getFile(Path);
  if (std::error_code EC = BufferOrErr.getError()) {
    Error = EC.message();
    return nullptr;
  }
  Buffer = std::move(BufferOrErr.get());
  ErrorOr<std::unique_ptr<object::FunctionIndexObjectFile>> ObjOrErr =
      object::FunctionIndexObjectFile::create(Buffer->getMemBufferRef(),
                                              DiagnosticHandler);
  if (std::error_code EC = ObjOrErr.getError()) {
    Error = EC.message();
    return nullptr;
  }
  return (*ObjOrErr)->takeIndex();
}

namespace {
/// Pass that performs cross-module function import provided a summary file.
class FunctionImportPass : public ModulePass {
  /// Optional function summary index to use for importing, otherwise
  /// the summary-file option must be specified.
  const FunctionInfoIndex *Index;

public:
  /// Pass identification, replacement for typeid
  static char ID;

  /// Specify pass name for debug output
  const char *getPassName() const override {
    return "Function Importing";
  }

  explicit FunctionImportPass(const FunctionInfoIndex *Index = nullptr)
      : ModulePass(ID), Index(Index) {}

  bool runOnModule(Module &M) override {
    if (SummaryFile.empty() && !Index)
      report_fatal_error("error: -function-import requires -summary-file or "
                         "file from frontend\n");
    std::unique_ptr<FunctionInfoIndex> IndexPtr;
    if (!SummaryFile.empty()) {
      if (Index)
        report_fatal_error("error: -summary-file and index from frontend\n");
      std::string Error;
      IndexPtr = getFunctionIndexForFile(SummaryFile, Error, diagnosticHandler);
      if (!IndexPtr) {
        errs() << "Error loading file '" << SummaryFile << "': " << Error
               << "\n";
        return false;
      }
      Index = IndexPtr.get();
    }

    // First we need to promote to global scope and rename any local values that
    // are potentially exported to other modules.
    if (renameModuleForThinLTO(M, Index)) {
      errs() << "Error renaming module\n";
      return false;
    }

    // Perform the import now.
    auto ModuleLoader = [&M](StringRef Identifier) {
      return loadFile(Identifier, M.getContext());
    };
    FunctionImporter Importer(*Index, ModuleLoader);
    return Importer.importFunctions(M);
  }
};
} // anonymous namespace

char FunctionImportPass::ID = 0;
INITIALIZE_PASS_BEGIN(FunctionImportPass, "function-import",
                      "Summary Based Function Import", false, false)
INITIALIZE_PASS_END(FunctionImportPass, "function-import",
                    "Summary Based Function Import", false, false)

namespace llvm {
Pass *createFunctionImportPass(const FunctionInfoIndex *Index = nullptr) {
  return new FunctionImportPass(Index);
}
}
<commit_msg>Fix Windows bot failure in Transforms/FunctionImport/funcimport.ll<commit_after>//===- FunctionImport.cpp - ThinLTO Summary-based Function Import ---------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements Function import based on summaries.
//
//===----------------------------------------------------------------------===//

#include "llvm/Transforms/IPO/FunctionImport.h"

#include "llvm/ADT/StringSet.h"
#include "llvm/IR/AutoUpgrade.h"
#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Module.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/Linker/Linker.h"
#include "llvm/Object/FunctionIndexObjectFile.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Transforms/Utils/FunctionImportUtils.h"

#include <map>

using namespace llvm;

#define DEBUG_TYPE "function-import"

/// Limit on instruction count of imported functions.
static cl::opt<unsigned> ImportInstrLimit(
    "import-instr-limit", cl::init(100), cl::Hidden, cl::value_desc("N"),
    cl::desc("Only import functions with less than N instructions"));

// Load lazily a module from \p FileName in \p Context.
static std::unique_ptr<Module> loadFile(const std::string &FileName,
                                        LLVMContext &Context) {
  SMDiagnostic Err;
  DEBUG(dbgs() << "Loading '" << FileName << "'\n");
  // Metadata isn't loaded until functions are imported, to minimize
  // the memory overhead.
  std::unique_ptr<Module> Result =
      getLazyIRFileModule(FileName, Err, Context,
                          /* ShouldLazyLoadMetadata = */ true);
  if (!Result) {
    Err.print("function-import", errs());
    return nullptr;
  }

  return Result;
}

namespace {
/// Helper to load on demand a Module from file and cache it for subsequent
/// queries. It can be used with the FunctionImporter.
class ModuleLazyLoaderCache {
  /// Cache of lazily loaded module for import.
  StringMap<std::unique_ptr<Module>> ModuleMap;

  /// Retrieve a Module from the cache or lazily load it on demand.
  std::function<std::unique_ptr<Module>(StringRef FileName)> createLazyModule;

public:
  /// Create the loader, Module will be initialized in \p Context.
  ModuleLazyLoaderCache(std::function<
      std::unique_ptr<Module>(StringRef FileName)> createLazyModule)
      : createLazyModule(createLazyModule) {}

  /// Retrieve a Module from the cache or lazily load it on demand.
  Module &operator()(StringRef FileName);

  std::unique_ptr<Module> takeModule(StringRef FileName) {
    auto I = ModuleMap.find(FileName);
    assert(I != ModuleMap.end());
    std::unique_ptr<Module> Ret = std::move(I->second);
    ModuleMap.erase(I);
    return Ret;
  }
};

// Get a Module for \p FileName from the cache, or load it lazily.
Module &ModuleLazyLoaderCache::operator()(StringRef Identifier) {
  auto &Module = ModuleMap[Identifier];
  if (!Module)
    Module = createLazyModule(Identifier);
  return *Module;
}
} // anonymous namespace

/// Walk through the instructions in \p F looking for external
/// calls not already in the \p CalledFunctions set. If any are
/// found they are added to the \p Worklist for importing.
static void findExternalCalls(const Module &DestModule, Function &F,
                              const FunctionInfoIndex &Index,
                              StringSet<> &CalledFunctions,
                              SmallVector<StringRef, 64> &Worklist) {
  // We need to suffix internal function calls imported from other modules,
  // prepare the suffix ahead of time.
  std::string Suffix;
  if (F.getParent() != &DestModule)
    Suffix =
        (Twine(".llvm.") +
         Twine(Index.getModuleId(F.getParent()->getModuleIdentifier()))).str();

  for (auto &BB : F) {
    for (auto &I : BB) {
      if (isa<CallInst>(I)) {
        auto CalledFunction = cast<CallInst>(I).getCalledFunction();
        // Insert any new external calls that have not already been
        // added to set/worklist.
        if (!CalledFunction || !CalledFunction->hasName())
          continue;
        // Ignore intrinsics early
        if (CalledFunction->isIntrinsic()) {
          assert(CalledFunction->getIntrinsicID() != 0);
          continue;
        }
        auto ImportedName = CalledFunction->getName();
        auto Renamed = (ImportedName + Suffix).str();
        // Rename internal functions
        if (CalledFunction->hasInternalLinkage()) {
          ImportedName = Renamed;
        }
        // Compute the global identifier used in the function index.
        auto CalledFunctionGlobalID = Function::getGlobalIdentifier(
            CalledFunction->getName(), CalledFunction->getLinkage(),
            CalledFunction->getParent()->getSourceFileName());
        auto It = CalledFunctions.insert(CalledFunctionGlobalID);
        if (!It.second) {
          // This is a call to a function we already considered, skip.
          continue;
        }
        // Ignore functions already present in the destination module
        auto *SrcGV = DestModule.getNamedValue(ImportedName);
        if (SrcGV) {
          if (GlobalAlias *SGA = dyn_cast<GlobalAlias>(SrcGV))
            SrcGV = SGA->getBaseObject();
          assert(isa<Function>(SrcGV) && "Name collision during import");
          if (!cast<Function>(SrcGV)->isDeclaration()) {
            DEBUG(dbgs() << DestModule.getModuleIdentifier() << ": Ignoring "
                         << ImportedName << " already in DestinationModule\n");
            continue;
          }
        }

        Worklist.push_back(It.first->getKey());
        DEBUG(dbgs() << DestModule.getModuleIdentifier()
                     << ": Adding callee for : " << ImportedName << " : "
                     << F.getName() << "\n");
      }
    }
  }
}

// Helper function: given a worklist and an index, will process all the worklist
// and decide what to import based on the summary information.
//
// Nothing is actually imported, functions are materialized in their source
// module and analyzed there.
//
// \p ModuleToFunctionsToImportMap is filled with the set of Function to import
// per Module.
static void GetImportList(Module &DestModule,
                          SmallVector<StringRef, 64> &Worklist,
                          StringSet<> &CalledFunctions,
                          std::map<StringRef, DenseSet<const GlobalValue *>>
                              &ModuleToFunctionsToImportMap,
                          const FunctionInfoIndex &Index,
                          ModuleLazyLoaderCache &ModuleLoaderCache) {
  while (!Worklist.empty()) {
    auto CalledFunctionName = Worklist.pop_back_val();
    DEBUG(dbgs() << DestModule.getModuleIdentifier() << ": Process import for "
                 << CalledFunctionName << "\n");

    // Try to get a summary for this function call.
    auto InfoList = Index.findFunctionInfoList(CalledFunctionName);
    if (InfoList == Index.end()) {
      DEBUG(dbgs() << DestModule.getModuleIdentifier() << ": No summary for "
                   << CalledFunctionName << " Ignoring.\n");
      continue;
    }
    assert(!InfoList->second.empty() && "No summary, error at import?");

    // Comdat can have multiple entries, FIXME: what do we do with them?
    auto &Info = InfoList->second[0];
    assert(Info && "Nullptr in list, error importing summaries?\n");

    auto *Summary = Info->functionSummary();
    if (!Summary) {
      // FIXME: in case we are lazyloading summaries, we can do it now.
      DEBUG(dbgs() << DestModule.getModuleIdentifier()
                   << ": Missing summary for  " << CalledFunctionName
                   << ", error at import?\n");
      llvm_unreachable("Missing summary");
    }

    if (Summary->instCount() > ImportInstrLimit) {
      DEBUG(dbgs() << DestModule.getModuleIdentifier() << ": Skip import of "
                   << CalledFunctionName << " with " << Summary->instCount()
                   << " instructions (limit " << ImportInstrLimit << ")\n");
      continue;
    }

    // Get the module path from the summary.
    auto ModuleIdentifier = Summary->modulePath();
    DEBUG(dbgs() << DestModule.getModuleIdentifier() << ": Importing "
                 << CalledFunctionName << " from " << ModuleIdentifier << "\n");

    auto &SrcModule = ModuleLoaderCache(ModuleIdentifier);

    // The function that we will import!
    GlobalValue *SGV = SrcModule.getNamedValue(CalledFunctionName);

    if (!SGV) {
      // The function is referenced by a global identifier, which has the
      // source file name prepended for functions that were originally local
      // in the source module. Strip any prepended name to recover the original
      // name in the source module.
      std::pair<StringRef, StringRef> Split = CalledFunctionName.rsplit(':');
      SGV = SrcModule.getNamedValue(Split.second);
      assert(SGV && "Can't find function to import in source module");
    }
    if (!SGV) {
      report_fatal_error(Twine("Can't load function '") + CalledFunctionName +
                         "' in Module '" + SrcModule.getModuleIdentifier() +
                         "', error in the summary?\n");
    }

    Function *F = dyn_cast<Function>(SGV);
    if (!F && isa<GlobalAlias>(SGV)) {
      auto *SGA = dyn_cast<GlobalAlias>(SGV);
      F = dyn_cast<Function>(SGA->getBaseObject());
      CalledFunctionName = F->getName();
    }
    assert(F && "Imported Function is ... not a Function");

    // We cannot import weak_any functions/aliases without possibly affecting
    // the order they are seen and selected by the linker, changing program
    // semantics.
    if (SGV->hasWeakAnyLinkage()) {
      DEBUG(dbgs() << DestModule.getModuleIdentifier()
                   << ": Ignoring import request for weak-any "
                   << (isa<Function>(SGV) ? "function " : "alias ")
                   << CalledFunctionName << " from "
                   << SrcModule.getModuleIdentifier() << "\n");
      continue;
    }

    // Add the function to the import list
    auto &Entry = ModuleToFunctionsToImportMap[SrcModule.getModuleIdentifier()];
    Entry.insert(F);

    // Process the newly imported functions and add callees to the worklist.
    F->materialize();
    findExternalCalls(DestModule, *F, Index, CalledFunctions, Worklist);
  }
}

// Automatically import functions in Module \p DestModule based on the summaries
// index.
//
// The current implementation imports every called functions that exists in the
// summaries index.
bool FunctionImporter::importFunctions(Module &DestModule) {
  DEBUG(dbgs() << "Starting import for Module "
               << DestModule.getModuleIdentifier() << "\n");
  unsigned ImportedCount = 0;

  /// First step is collecting the called external functions.
  StringSet<> CalledFunctions;
  SmallVector<StringRef, 64> Worklist;
  for (auto &F : DestModule) {
    if (F.isDeclaration() || F.hasFnAttribute(Attribute::OptimizeNone))
      continue;
    findExternalCalls(DestModule, F, Index, CalledFunctions, Worklist);
  }
  if (Worklist.empty())
    return false;

  /// Second step: for every call to an external function, try to import it.

  // Linker that will be used for importing function
  Linker TheLinker(DestModule);

  // Map of Module -> List of Function to import from the Module
  std::map<StringRef, DenseSet<const GlobalValue *>>
      ModuleToFunctionsToImportMap;

  // Analyze the summaries and get the list of functions to import by
  // populating ModuleToFunctionsToImportMap
  ModuleLazyLoaderCache ModuleLoaderCache(ModuleLoader);
  GetImportList(DestModule, Worklist, CalledFunctions,
                ModuleToFunctionsToImportMap, Index, ModuleLoaderCache);
  assert(Worklist.empty() && "Worklist hasn't been flushed in GetImportList");

  // Do the actual import of functions now, one Module at a time
  for (auto &FunctionsToImportPerModule : ModuleToFunctionsToImportMap) {
    // Get the module for the import
    auto &FunctionsToImport = FunctionsToImportPerModule.second;
    std::unique_ptr<Module> SrcModule =
        ModuleLoaderCache.takeModule(FunctionsToImportPerModule.first);
    assert(&DestModule.getContext() == &SrcModule->getContext() &&
           "Context mismatch");

    // If modules were created with lazy metadata loading, materialize it
    // now, before linking it (otherwise this will be a noop).
    SrcModule->materializeMetadata();
    UpgradeDebugInfo(*SrcModule);

    // Link in the specified functions.
    if (TheLinker.linkInModule(std::move(SrcModule), Linker::Flags::None,
                               &Index, &FunctionsToImport))
      report_fatal_error("Function Import: link error");

    ImportedCount += FunctionsToImport.size();
  }

  DEBUG(dbgs() << "Imported " << ImportedCount << " functions for Module "
               << DestModule.getModuleIdentifier() << "\n");
  return ImportedCount;
}

/// Summary file to use for function importing when using -function-import from
/// the command line.
static cl::opt<std::string>
    SummaryFile("summary-file",
                cl::desc("The summary file to use for function importing."));

static void diagnosticHandler(const DiagnosticInfo &DI) {
  raw_ostream &OS = errs();
  DiagnosticPrinterRawOStream DP(OS);
  DI.print(DP);
  OS << '\n';
}

/// Parse the function index out of an IR file and return the function
/// index object if found, or nullptr if not.
static std::unique_ptr<FunctionInfoIndex>
getFunctionIndexForFile(StringRef Path, std::string &Error,
                        DiagnosticHandlerFunction DiagnosticHandler) {
  std::unique_ptr<MemoryBuffer> Buffer;
  ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
      MemoryBuffer::getFile(Path);
  if (std::error_code EC = BufferOrErr.getError()) {
    Error = EC.message();
    return nullptr;
  }
  Buffer = std::move(BufferOrErr.get());
  ErrorOr<std::unique_ptr<object::FunctionIndexObjectFile>> ObjOrErr =
      object::FunctionIndexObjectFile::create(Buffer->getMemBufferRef(),
                                              DiagnosticHandler);
  if (std::error_code EC = ObjOrErr.getError()) {
    Error = EC.message();
    return nullptr;
  }
  return (*ObjOrErr)->takeIndex();
}

namespace {
/// Pass that performs cross-module function import provided a summary file.
class FunctionImportPass : public ModulePass {
  /// Optional function summary index to use for importing, otherwise
  /// the summary-file option must be specified.
  const FunctionInfoIndex *Index;

public:
  /// Pass identification, replacement for typeid
  static char ID;

  /// Specify pass name for debug output
  const char *getPassName() const override {
    return "Function Importing";
  }

  explicit FunctionImportPass(const FunctionInfoIndex *Index = nullptr)
      : ModulePass(ID), Index(Index) {}

  bool runOnModule(Module &M) override {
    if (SummaryFile.empty() && !Index)
      report_fatal_error("error: -function-import requires -summary-file or "
                         "file from frontend\n");
    std::unique_ptr<FunctionInfoIndex> IndexPtr;
    if (!SummaryFile.empty()) {
      if (Index)
        report_fatal_error("error: -summary-file and index from frontend\n");
      std::string Error;
      IndexPtr = getFunctionIndexForFile(SummaryFile, Error, diagnosticHandler);
      if (!IndexPtr) {
        errs() << "Error loading file '" << SummaryFile << "': " << Error
               << "\n";
        return false;
      }
      Index = IndexPtr.get();
    }

    // First we need to promote to global scope and rename any local values that
    // are potentially exported to other modules.
    if (renameModuleForThinLTO(M, Index)) {
      errs() << "Error renaming module\n";
      return false;
    }

    // Perform the import now.
    auto ModuleLoader = [&M](StringRef Identifier) {
      return loadFile(Identifier, M.getContext());
    };
    FunctionImporter Importer(*Index, ModuleLoader);
    return Importer.importFunctions(M);
  }
};
} // anonymous namespace

char FunctionImportPass::ID = 0;
INITIALIZE_PASS_BEGIN(FunctionImportPass, "function-import",
                      "Summary Based Function Import", false, false)
INITIALIZE_PASS_END(FunctionImportPass, "function-import",
                    "Summary Based Function Import", false, false)

namespace llvm {
Pass *createFunctionImportPass(const FunctionInfoIndex *Index = nullptr) {
  return new FunctionImportPass(Index);
}
}
<|endoftext|>


<commit_before>#include <QString>
#include <typeinfo>
#include "kernel.h"
#include "ilwisdata.h"
#include "range.h"
#include "domainitem.h"
#include "numericrange.h"
#include "domainitem.h"
#include "domain.h"
#include "itemdomain.h"
#include "numericitem.h"
#include "itemrange.h"
#include "numericitemrange.h"

using namespace Ilwis;

NumericItemRange::NumericItemRange()
{
    _vt = itNUMERICITEM;
}

QVariant NumericItemRange::impliedValue(const QVariant& v) const
{
    bool ok;
    quint32 index = v.toUInt(&ok);
    if (!ok){
        ERROR2(ERR_COULD_NOT_CONVERT_2,v.toString(), "raw value");
        return sUNDEF;
    }
    if ( index < _items.size())
        return _items[index]->name();
    return sUNDEF;

}

quint32 NumericItemRange::count() const
{
    return _items.size();
}

SPDomainItem NumericItemRange::item(quint32 index) const
{
       if ( index < _items.size())
           return _items[index];
       return SPDomainItem();
}

SPDomainItem NumericItemRange::item(const QString &item) const
{
    QStringList parts = item.split(" ");
    if ( parts.size() == 0 || parts.size() > 3){
        ERROR2(ERR_ILLEGAL_VALUE_2, TR("numeric range"), item);
        return SPDomainItem();
    }
    bool ok;
    double v1 = index(parts[0].toDouble(&ok));
    if ( v1 == rUNDEF  && ok) {
        ERROR2(ERR_ILLEGAL_VALUE_2, TR("numeric range"), item);
        return SPDomainItem();
    }
    if (parts.size() > 1) {
        double v2 = index(parts[1].toDouble(&ok));
        if ( !ok || ((int)v1 - (int)v2 != 0)) {
            ERROR2(ERR_ILLEGAL_VALUE_2, TR("numeric range"), item);
            return SPDomainItem();
        }
    }
    return _items[(int)v1];
}

SPDomainItem NumericItemRange::itemByOrder(quint32 index) const
{
    return item(index);
}

double NumericItemRange::index(double v) const
{
    if (!isValid())
        return rUNDEF;

    for(quint32 ind = 0; ind < _items.size(); ++ind) {
        quint32 mark1=iUNDEF, mark2=iUNDEF;
        const NumericRange& rng = _items[ind]->range();
        if ( v >= rng.min()){
            mark1 = ind;
        }
        if ( v <= rng.max()){
            mark2 = ind;
        }
        if ( mark1 != iUNDEF && mark2 != iUNDEF) {
            bool ok = isContinuous();
            if (ok ) {
                return ind + (v - rng.min()) / rng.distance();
            }
            return ind;
        }
    }
    return rUNDEF;
}

bool NumericItemRange::validNumber(QString value) const{
    bool oknumber;
    double v = value.toDouble(&oknumber);
    if (!oknumber) {
        return ERROR2(ERR_ILLEGAL_VALUE_2, TR("numeric range"), value);
    }

    return index(v) != rUNDEF;

}

bool NumericItemRange::contains(const QVariant &name, bool ) const
{
    QStringList items = name.toString().split(";");
    for(const QString& item : items) {
        QStringList parts = item.split(" ");
        if ( parts.size() == 0 || parts.size() > 3){
            return ERROR2(ERR_ILLEGAL_VALUE_2, TR("numeric range"), name.toString());
        }
        bool ok = validNumber(parts[0]);
        if (parts.size() > 1)
            ok &= validNumber(parts[1]);
        if (!ok)
            return false;
    }
    return true;
}

bool NumericItemRange::isValid() const
{
    return _items.size() != 0;
}

void NumericItemRange::add(DomainItem *item)
{
    if (!item)
        return;

    if ( item->valueType() != itNUMERICITEM)
        return;

    if (!item->isValid())
        return;
    SPNumericItem nitem(static_cast<NumericItem *>(item));
    for(auto iter = _items.rbegin(); iter != _items.rend(); ++iter) {
        if ( nitem->range() > (*iter)->range()) {
            if ( nitem->raw() == iUNDEF)
                nitem->_raw = _items.size();
            _items.insert(iter.base(),1, nitem );
            return;
        }
    }
    if ( _items.size() == 0) { // no overlapping items allowed; so the only case that is legal here is the first
        nitem->_raw = 0;
        _items.push_back(nitem);
    }
}

void NumericItemRange::add(SPDomainItem item)
{
    if ( item.isNull() || !item->isValid() || item->valueType() != itNUMERICITEM)
        return;

    SPNumericItem nitem = item.staticCast<NumericItem>();
    for(auto iter = _items.rbegin(); iter != _items.rend(); ++iter) {
        if ( nitem->range() > (*iter)->range()) {
            if ( nitem->raw() == iUNDEF)
                nitem->_raw = _items.size();
            _items.insert(iter.base(),1, nitem );
            return;
        }
    }
    if ( _items.size() == 0) { // no overlapping items allowed; so the only case that is legal here is the first
        nitem->_raw = 0;
        _items.push_back(nitem);
    }
}

bool NumericItemRange::alignWithParent(const IDomain &dom)
{
    INumericItemDomain numdom = dom.get<NumericItemDomain>();
    if ( !numdom.isValid())
        return false;
    ItemIterator<NumericItem> iterParent(numdom);
    std::map<QString, DomainItem *> parentItems;
    for(DomainItem *item : iterParent) {
        parentItems[item->name()] = item;
    }

    for(int i=0; i < _items.size(); ++i) {
        // TODO:: a bit more flexible in the future(maybe?) with overlapping ranges; absent step sizes
        auto iter = parentItems.find(_items[i]->name());
        if ( iter == parentItems.end()){
            return ERROR2(ERR_ILLEGAL_VALUE_2, TR("item in child domain"),_items[i]->name());
        }
        _items[i] = SPNumericItem(static_cast<NumericItem *>((*iter).second->clone()));
    }
    return false;

}

void NumericItemRange::remove(const QString &nm)
{
     for(auto iter = _items.begin(); iter != _items.begin(); ++iter) {
        if ( (*iter)->name() == nm) {
            _items.erase(iter);
            break;
        }
     }
}

QString NumericItemRange::toString() const
{   if ( _items.size() == 0)
        return sUNDEF;
    QString names;
    for(const SPNumericItem& it: _items) {
        if ( names != "")
            names += ";";
        names += it->name();
    }
    return names;
}

void NumericItemRange::clear()
{
    _items.clear();
}

ItemRange *NumericItemRange::clone() const
{
    ItemRange *items = new NumericItemRange();
    for(const SPNumericItem& it: _items) {
        items->add(it->clone());
    }
    items->interpolation(interpolation());

    return items;

}

NumericItemRange &NumericItemRange::operator <<(const QString &itemdef)
{
    QStringList parts = itemdef.split(" ");
    if( parts.size() > 1) {
        bool ok;
        double step = 0;
        double mn = parts[0].toDouble(&ok);
        if(ok) {
            double mx = parts[1].toDouble(&ok);
            if ( ok) {
                if ( parts.size() == 3) {
                    step = parts[2].toDouble(&ok);

                }
                if (ok) {
                    add(new NumericItem({mn,mx, step}));
                    return *this;
                }
            }
        }
        ERROR1(ERR_NO_INITIALIZED_1, "numeric item range");
    } if ( parts.size() == 1) {
        if ( count() == 0) {
            ERROR1(ERR_NO_INITIALIZED_1, "numeric item range");
            return *this;
        }
        bool ok;
        double vmin = _items[_items.size() - 1]->range().max();
        double vmax = itemdef.toDouble(&ok) ;
        if (!ok) {
            ERROR1(ERR_NO_INITIALIZED_1, "numeric item range");
        }
        double step = _items[0]->range().resolution();
        if ( step == 0)
            vmin += EPS8;
        add(new NumericItem({vmin+step,vmax, step}));
    }
    return *this;
}

qint32 NumericItemRange::gotoIndex(qint32 index, qint32 step) const
{
    if ( index + step >= _items.size())
        return _items.size();
    if ( index + step < 0)
        return 0;
    return index + step;
}

void NumericItemRange::addRange(const ItemRange &range)
{
    ItemRange::addRange(range);
    _interpolation = static_cast<const NumericItemRange&>(range).interpolation();
}

<commit_msg>name with the item<commit_after>#include <QString>
#include <typeinfo>
#include "kernel.h"
#include "ilwisdata.h"
#include "range.h"
#include "domainitem.h"
#include "numericrange.h"
#include "domainitem.h"
#include "domain.h"
#include "itemdomain.h"
#include "numericitem.h"
#include "itemrange.h"
#include "numericitemrange.h"

using namespace Ilwis;

NumericItemRange::NumericItemRange()
{
    _vt = itNUMERICITEM;
}

QVariant NumericItemRange::impliedValue(const QVariant& v) const
{
    bool ok;
    quint32 index = v.toUInt(&ok);
    if (!ok){
        ERROR2(ERR_COULD_NOT_CONVERT_2,v.toString(), "raw value");
        return sUNDEF;
    }
    if ( index < _items.size())
        return _items[index]->name();
    return sUNDEF;

}

quint32 NumericItemRange::count() const
{
    return _items.size();
}

SPDomainItem NumericItemRange::item(quint32 index) const
{
       if ( index < _items.size())
           return _items[index];
       return SPDomainItem();
}

SPDomainItem NumericItemRange::item(const QString &item) const
{
    QStringList parts = item.split(" ");
    if ( parts.size() == 0 || parts.size() > 3){
        ERROR2(ERR_ILLEGAL_VALUE_2, TR("numeric range"), item);
        return SPDomainItem();
    }
    bool ok;
    double v1 = index(parts[0].toDouble(&ok));
    if ( v1 == rUNDEF  && ok) {
        ERROR2(ERR_ILLEGAL_VALUE_2, TR("numeric range"), item);
        return SPDomainItem();
    }
    if (parts.size() > 1) {
        double v2 = index(parts[1].toDouble(&ok));
        if ( !ok || ((int)v1 - (int)v2 != 0)) {
            ERROR2(ERR_ILLEGAL_VALUE_2, TR("numeric range"), item);
            return SPDomainItem();
        }
    }
    return _items[(int)v1];
}

SPDomainItem NumericItemRange::itemByOrder(quint32 index) const
{
    return item(index);
}

double NumericItemRange::index(double v) const
{
    if (!isValid())
        return rUNDEF;

    for(quint32 ind = 0; ind < _items.size(); ++ind) {
        quint32 mark1=iUNDEF, mark2=iUNDEF;
        const NumericRange& rng = _items[ind]->range();
        if ( v >= rng.min()){
            mark1 = ind;
        }
        if ( v <= rng.max()){
            mark2 = ind;
        }
        if ( mark1 != iUNDEF && mark2 != iUNDEF) {
            bool ok = isContinuous();
            if (ok ) {
                return ind + (v - rng.min()) / rng.distance();
            }
            return ind;
        }
    }
    return rUNDEF;
}

bool NumericItemRange::validNumber(QString value) const{
    bool oknumber;
    double v = value.toDouble(&oknumber);
    if (!oknumber) {
        return ERROR2(ERR_ILLEGAL_VALUE_2, TR("numeric range"), value);
    }

    return index(v) != rUNDEF;

}

bool NumericItemRange::contains(const QVariant &name, bool ) const
{
    QStringList items = name.toString().split(";");
    for(const QString& item : items) {
        QStringList parts = item.split(" ");
        if ( parts.size() == 0 || parts.size() > 3){
            return ERROR2(ERR_ILLEGAL_VALUE_2, TR("numeric range"), name.toString());
        }
        bool ok = validNumber(parts[0]);
        if (parts.size() > 1)
            ok &= validNumber(parts[1]);
        if (!ok)
            return false;
    }
    return true;
}

bool NumericItemRange::isValid() const
{
    return _items.size() != 0;
}

void NumericItemRange::add(DomainItem *item)
{
    if (!item)
        return;

    if ( item->valueType() != itNUMERICITEM)
        return;

    if (!item->isValid())
        return;
    SPNumericItem nitem(static_cast<NumericItem *>(item));
    for(auto iter = _items.rbegin(); iter != _items.rend(); ++iter) {
        if ( nitem->range() > (*iter)->range()) {
            if ( nitem->raw() == iUNDEF)
                nitem->_raw = _items.size();
            _items.insert(iter.base(),1, nitem );
            return;
        }
    }
    if ( _items.size() == 0) { // no overlapping items allowed; so the only case that is legal here is the first
        nitem->_raw = 0;
        _items.push_back(nitem);
    }
}

void NumericItemRange::add(SPDomainItem item)
{
    if ( item.isNull() || !item->isValid() || item->valueType() != itNUMERICITEM)
        return;

    SPNumericItem nitem = item.staticCast<NumericItem>();
    for(auto iter = _items.rbegin(); iter != _items.rend(); ++iter) {
        if ( nitem->range() > (*iter)->range()) {
            if ( nitem->raw() == iUNDEF)
                nitem->_raw = _items.size();
            _items.insert(iter.base(),1, nitem );
            return;
        }
    }
    if ( _items.size() == 0) { // no overlapping items allowed; so the only case that is legal here is the first
        nitem->_raw = 0;
        _items.push_back(nitem);
    }
}

bool NumericItemRange::alignWithParent(const IDomain &dom)
{
    INumericItemDomain numdom = dom.get<NumericItemDomain>();
    if ( !numdom.isValid())
        return false;
    ItemIterator<NumericItem> iterParent(numdom);
    std::map<QString, DomainItem *> parentItems;
    for(DomainItem *item : iterParent) {
        parentItems[item->name()] = item;
    }

    for(int i=0; i < _items.size(); ++i) {
        // TODO:: a bit more flexible in the future(maybe?) with overlapping ranges; absent step sizes
        auto iter = parentItems.find(_items[i]->name());
        if ( iter == parentItems.end()){
            return ERROR2(ERR_ILLEGAL_VALUE_2, TR("item in child domain"),_items[i]->name());
        }
        _items[i] = SPNumericItem(static_cast<NumericItem *>((*iter).second->clone()));
    }
    return false;

}

void NumericItemRange::remove(const QString &nm)
{
     for(auto iter = _items.begin(); iter != _items.begin(); ++iter) {
        if ( (*iter)->name() == nm) {
            _items.erase(iter);
            break;
        }
     }
}

QString NumericItemRange::toString() const
{   if ( _items.size() == 0)
        return sUNDEF;
    QString names;
    for(const SPNumericItem& it: _items) {
        if ( names != "")
            names += ";";
        names += it->name();
    }
    return names;
}

void NumericItemRange::clear()
{
    _items.clear();
}

ItemRange *NumericItemRange::clone() const
{
    ItemRange *items = new NumericItemRange();
    for(const SPNumericItem& it: _items) {
        items->add(it->clone());
    }
    items->interpolation(interpolation());

    return items;

}

NumericItemRange &NumericItemRange::operator <<(const QString &itemdef)
{
    QStringList parts = itemdef.split(" ");
    if( parts.size() > 2) {
        bool ok;
        double step = 0;
        QString name = parts[0];
        double mn = parts[1].toDouble(&ok);
        if(ok) {
            double mx = parts[2].toDouble(&ok);
            if ( ok) {
                if ( parts.size() == 4) {
                    step = parts[3].toDouble(&ok);

                }
                if (ok) {
                    add(new NumericItem(name, {mn,mx, step}));
                    return *this;
                }
            }
        }
        ERROR1(ERR_NO_INITIALIZED_1, "numeric item range");
    } if ( parts.size() == 1) {
        if ( count() == 0) {
            ERROR1(ERR_NO_INITIALIZED_1, "numeric item range");
            return *this;
        }
        bool ok;
        double vmin = _items[_items.size() - 1]->range().max();
        double vmax = itemdef.toDouble(&ok) ;
        if (!ok) {
            ERROR1(ERR_NO_INITIALIZED_1, "numeric item range");
        }
        double step = _items[0]->range().resolution();
        if ( step == 0)
            vmin += EPS8;
        QString label = QString("label_%1").arg(_items.size());
        add(new NumericItem(label, {vmin+step,vmax, step}));
    }
    return *this;
}

qint32 NumericItemRange::gotoIndex(qint32 index, qint32 step) const
{
    if ( index + step >= _items.size())
        return _items.size();
    if ( index + step < 0)
        return 0;
    return index + step;
}

void NumericItemRange::addRange(const ItemRange &range)
{
    ItemRange::addRange(range);
    _interpolation = static_cast<const NumericItemRange&>(range).interpolation();
}

<|endoftext|>


<commit_before>
/*************** <auto-copyright.pl BEGIN do not edit this line> **************
 *
 * VR Juggler is (C) Copyright 1998, 1999, 2000, 2001 by Iowa State University
 *
 * Original Authors:
 *   Allen Bierbaum, Christopher Just,
 *   Patrick Hartling, Kevin Meinert,
 *   Carolina Cruz-Neira, Albert Baker
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 *************** <auto-copyright.pl END do not edit this line> ***************/

/****************** <SNX heading BEGIN do not edit this line> *****************
 *
 * Juggler Juggler
 *
 * Original Authors:
 *   Kevin Meinert, Carolina Cruz-Neira
 *
 * -----------------------------------------------------------------
 * File:          $RCSfile$
 * Date modified: $Date$
 * Version:       $Revision$
 * -----------------------------------------------------------------
 *
 ****************** <SNX heading END do not edit this line> ******************/

// Kevin Meinert
// simple glut skeleton application: look for the !!!TODO!!!s,
//  and fill in your code
//                          there as needed
//

#ifdef WIN32
   #include <windows.h>  // make the app win32 friendly. :)
#endif
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>

#include "snx/sonix.h"    // interface
#include "snx/SoundHandle.h"

#include <iostream.h>
#include <stdlib.h>

// a place to store application data...
class AppWindow
{
public:
   static int width, height;
   static int mainWin_contextID;
};
int AppWindow::width = 0, AppWindow::height = 0;
int AppWindow::mainWin_contextID = -1;

// our sound object...
snx::SoundHandle kevinSound;

void drawGrid()
{
   glBegin( GL_LINES );
      for ( int x = -1000; x < 1000; ++x)
      {
         glVertex3f( -1000, 0, x );
         glVertex3f(  1000, 0, x );
         glVertex3f( x, 0, -1000 );
         glVertex3f( x, 0,  1000 );
      }
   glEnd();
}

//////////////////////////////////////////////////
// This is called on a glutPostRedisplay
//////////////////////////////////////////////////
static void OnRedisplay()
{
   glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
   glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT );
   glEnable( GL_DEPTH_TEST );
   glEnable( GL_BLEND );
   glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );

   // set up the projection matrix
   glMatrixMode( GL_PROJECTION );
      glLoadIdentity();
      gluPerspective( 80.0f, AppWindow::width / AppWindow::height, 0.01f, 1000.0f );

   // initialize your matrix stack used for transforming your models
    glMatrixMode( GL_MODELVIEW );
      glLoadIdentity();

   // !!!TODO!!!: replace the following with your own opengl commands!
   glTranslatef( 0, -10, 0 );
   drawGrid();
   // !!!TODO!!!: ////////////////////////////////////////

   // swaps the front and back frame buffers.
   // hint: you've been drawing on the back, offscreen, buffer.
   // This command then brings that framebuffer onscreen.
   glutSwapBuffers();
}

//////////////////////////////////////////////////
// This is called repeatedly, as fast as possible
//////////////////////////////////////////////////
#include "unistd.h"
static void OnIdle()
{
   // According to the GLUT specification, the current window is
   // undefined during an idle callback.  So we need to explicitly change
   // it if necessary
   if ( glutGetWindow() != AppWindow::mainWin_contextID )
      glutSetWindow( AppWindow::mainWin_contextID );

   // tell glut to call redisplay (which then calls OnRedisplay)
   glutPostRedisplay();

   usleep( 10000 );
   sonix::instance()->step( 0.1 );
}

/////////////////////////////////////////////
// This is called on a Resize of the glut window
/////////////////////////////////////////////
static void OnReshape( int width, int height )
{
   // save these params in case your app needs them
   AppWindow::width = width;
   AppWindow::height = height;

   // set your viewport to the extents of the window
   glViewport( 0, 0, width, height );

   // let the app run idle, while resizing,
   // glut does not do this for us automatically, so call OnIdle explicitly.
   OnIdle();
}

////////////////////////////////
// This is called on a Down Keypress
////////////////////////////////
static void OnKeyboardDown( unsigned char k, int x, int y )
{
   switch (k)
   {
   // If user pressed 'q' or 'ESC', then exit the app.
   // this is really ungraceful, but necessary since GLUT does a while(1)
   // as it's control loop.  There is no GLUT method to exit, unfortunately.
   case 'q':
   case 27:
      exit( 0 );
      break;
   case '1':
   {
      sonix::instance()->changeAPI( "Stub" );
      snx::SoundInfo si;
      si.filename = "../../../data/sample.wav";
      si.datasource = snx::SoundInfo::FILESYSTEM;
      kevinSound.configure( si );

      sonix::instance()->changeAPI( "OpenAL" );
   }
   break;
   case '2':
   {
      sonix::instance()->changeAPI( "Stub" );
      snx::SoundInfo si;
      si.filename = "../../../data/suck1.aiff";
      si.datasource = snx::SoundInfo::FILESYSTEM;
      kevinSound.configure( si );

      sonix::instance()->changeAPI( "AudioWorks" );
   }
   break;
      case '3':
   {
      sonix::instance()->changeAPI( "Stub" );
   }
   break;
      case 'a':
   {
      snx::SoundInfo si;
      si.filename = "../../../data/sample.wav";
      si.datasource = snx::SoundInfo::FILESYSTEM;
      kevinSound.configure( si );
   }
   break;
      case 'b':
   {
      snx::SoundInfo si;
      si.filename = "../../../data/sample-drumsolo-2bars.wav";
      si.datasource = snx::SoundInfo::FILESYSTEM;
      kevinSound.configure( si );
   }
   break;
   case 't':
   {
      kevinSound.trigger();
   }
   break;
   case 'p':
   {
      kevinSound.pause();
   }
   break;
   case 's':
   {
      kevinSound.stop();
   }
   break;

   case ',':
   {
      kevinSound.setPosition( -60, 0, 0 );
   }
   break;

   case '.':
   {
      kevinSound.setPosition( 60, 0, 0 );
   }
   break;

   default:
      // do nothing if no key is pressed
      break;
   }
}
////////////////////////////////
// This is called on a Up Keypress
////////////////////////////////
static void OnKeyboardUp( unsigned char k, int x, int y )
{
   switch (k)
   {
   case 'a':
      // !!!TODO!!!: add handler for when UP is released
      break;

   case 'z':
      // !!!TODO!!!: add handler for when DOWN is released
      break;

   default:
      // do nothing if no key is pressed
      break;
   }
}

////////////////////////////////
// This is called on a Down Keypress
// of a "special" key such as the grey arrows.
////////////////////////////////
static void OnSpecialKeyboardDown(int k, int x, int y)
{
   switch (k)
   {
   case GLUT_KEY_UP:
      // !!!TODO!!!: add handler for when UP is pressed
      break;
   case GLUT_KEY_DOWN:
      // !!!TODO!!!: add handler for when DOWN is pressed
      break;
   default:
      // do nothing if no special key pressed
      break;
   }
}

////////////////////////////////
// This is called on a Up Keypress
////////////////////////////////
static void OnSpecialKeyboardUp( int k, int x, int y )
{
   switch (k)
   {
   case GLUT_KEY_UP:
      // !!!TODO!!!: add handler for when UP is released
      break;
   case GLUT_KEY_DOWN:
      // !!!TODO!!!: add handler for when DOWN is released
      break;
   default:
      // do nothing if no special key pressed
      break;
   }
}

////////////////////////////////
// This is called when mouse changes position
// x and y are the screen position
// in your 2D window's coordinate frame
////////////////////////////////
static void OnMousePos( int x, int y )
{
   // !!!TODO!!!: do something based on mouse position
}

////////////////////////////////
// This is called when mouse clicks
////////////////////////////////
static void OnMouseClick( int a, int b, int c, int d )
{
   // !!!TODO!!!: Need mouse interaction?
   //             read the glut docs/manpage to find out how to query
   //             which button was pressed...
   //             you may have to get this from the glut website
   //             (use www.google.com to search for it)
}

// Initialize the application
// initialize the state of your app here if needed...
static void OnApplicationInit()
{
   // Don't put open GL code here, this func may be called at anytime
   // even before the API is initialized
   // (like before a graphics context is obtained)

   kevinSound.init( "kevin" );
}

int main(int argc, char* argv[])
{
    // Initialize the application
    // initialize the state of your app here if needed...
    OnApplicationInit();

    // Set the window's initial size
    ::glutInitWindowSize( 640, 480 );
    ::glutInit( &argc, argv );

    // Set to double buffer to reduce flicker
    ::glutInitDisplayMode( GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE );

    // Set the window title
    AppWindow::mainWin_contextID = ::glutCreateWindow( "GLUT application" );

    cout<<"\n"<<flush;
    cout<<"sonix sample OpenGL+sound app - by kevin - kevn@vrjuggler.org\n"<<flush;
    cout<<"       usage:  t - trigger\n"<<flush;
    cout<<"               p - pause\n"<<flush;
    cout<<"               s - stop\n"<<flush;
    cout<<"               , - put 3d sound source to the left\n"<<flush;
    cout<<"               . - put 3d sound source to the right\n"<<flush;
    cout<<"               1 - Change subsystem to OpenAL\n"<<flush;
    cout<<"               2 - Change subsystem to AudioWorks\n"<<flush;
    cout<<"               3 - Change subsystem to None\n"<<flush;
    cout<<"               a - configure sound object to sample.wav (OpenAL only)\n"<<flush;
    cout<<"               b - configure sound object to drumsolo.wav (OpenAL only)\n"<<flush;
    cout<<"\n"<<flush;

   // display callbacks.
   ::glutReshapeFunc( OnReshape );
   ::glutIdleFunc( OnIdle );
   ::glutDisplayFunc( OnRedisplay );

   // tell glut to not call the keyboard callback repeatedly
   // when holding down a key. (uses edge triggering, like the mouse)
   ::glutIgnoreKeyRepeat( 1 );

   // keyboard callback functions.
   ::glutKeyboardFunc( OnKeyboardDown );
   ::glutKeyboardUpFunc( OnKeyboardUp );
   ::glutSpecialFunc( OnSpecialKeyboardDown );
   ::glutSpecialUpFunc( OnSpecialKeyboardUp );

   // mouse callback functions...
   ::glutMouseFunc( OnMouseClick );
   ::glutMotionFunc( OnMousePos );
   ::glutPassiveMotionFunc( OnMousePos );

    // start the application loop, your callbacks will now be called
    // time for glut to sit and spin.
    ::glutMainLoop();

    return 1;
}

<commit_msg>added an interactive test for setPosition<commit_after>
/*************** <auto-copyright.pl BEGIN do not edit this line> **************
 *
 * VR Juggler is (C) Copyright 1998, 1999, 2000, 2001 by Iowa State University
 *
 * Original Authors:
 *   Allen Bierbaum, Christopher Just,
 *   Patrick Hartling, Kevin Meinert,
 *   Carolina Cruz-Neira, Albert Baker
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 *************** <auto-copyright.pl END do not edit this line> ***************/

/****************** <SNX heading BEGIN do not edit this line> *****************
 *
 * Juggler Juggler
 *
 * Original Authors:
 *   Kevin Meinert, Carolina Cruz-Neira
 *
 * -----------------------------------------------------------------
 * File:          $RCSfile$
 * Date modified: $Date$
 * Version:       $Revision$
 * -----------------------------------------------------------------
 *
 ****************** <SNX heading END do not edit this line> ******************/

// Kevin Meinert
// simple glut skeleton application: look for the !!!TODO!!!s,
//  and fill in your code
//                          there as needed
//

#ifdef WIN32
   #include <windows.h>  // make the app win32 friendly. :)
#endif
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>

#include "snx/sonix.h"    // interface
#include "snx/SoundHandle.h"

#include <iostream.h>
#include <stdlib.h>

// a place to store application data...
class AppWindow
{
public:
   static int width, height;
   static int mainWin_contextID;
};
int AppWindow::width = 0, AppWindow::height = 0;
int AppWindow::mainWin_contextID = -1;

int soundpos = 0;

// our sound object...
snx::SoundHandle kevinSound;

void drawGrid()
{
   glBegin( GL_LINES );
      for ( int x = -1000; x < 1000; ++x)
      {
         glVertex3f( -1000, 0, x );
         glVertex3f(  1000, 0, x );
         glVertex3f( x, 0, -1000 );
         glVertex3f( x, 0,  1000 );
      }
   glEnd();
}

//////////////////////////////////////////////////
// This is called on a glutPostRedisplay
//////////////////////////////////////////////////
static void OnRedisplay()
{
   glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
   glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT );
   glEnable( GL_DEPTH_TEST );
   glEnable( GL_BLEND );
   glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );

   // set up the projection matrix
   glMatrixMode( GL_PROJECTION );
      glLoadIdentity();
      gluPerspective( 80.0f, AppWindow::width / AppWindow::height, 0.01f, 1000.0f );

   // initialize your matrix stack used for transforming your models
    glMatrixMode( GL_MODELVIEW );
      glLoadIdentity();

   // !!!TODO!!!: replace the following with your own opengl commands!
   glTranslatef( 0, -10, 0 );
   drawGrid();
   // !!!TODO!!!: ////////////////////////////////////////

   // swaps the front and back frame buffers.
   // hint: you've been drawing on the back, offscreen, buffer.
   // This command then brings that framebuffer onscreen.
   glutSwapBuffers();
}

//////////////////////////////////////////////////
// This is called repeatedly, as fast as possible
//////////////////////////////////////////////////
#include "unistd.h"
static void OnIdle()
{
   // According to the GLUT specification, the current window is
   // undefined during an idle callback.  So we need to explicitly change
   // it if necessary
   if ( glutGetWindow() != AppWindow::mainWin_contextID )
      glutSetWindow( AppWindow::mainWin_contextID );

   // tell glut to call redisplay (which then calls OnRedisplay)
   glutPostRedisplay();

   usleep( 10000 );
   sonix::instance()->step( 0.1 );
}

/////////////////////////////////////////////
// This is called on a Resize of the glut window
/////////////////////////////////////////////
static void OnReshape( int width, int height )
{
   // save these params in case your app needs them
   AppWindow::width = width;
   AppWindow::height = height;

   // set your viewport to the extents of the window
   glViewport( 0, 0, width, height );

   // let the app run idle, while resizing,
   // glut does not do this for us automatically, so call OnIdle explicitly.
   OnIdle();
}

////////////////////////////////
// This is called on a Down Keypress
////////////////////////////////
static void OnKeyboardDown( unsigned char k, int x, int y )
{
   switch (k)
   {
   // If user pressed 'q' or 'ESC', then exit the app.
   // this is really ungraceful, but necessary since GLUT does a while(1)
   // as it's control loop.  There is no GLUT method to exit, unfortunately.
   case 'q':
   case 27:
      exit( 0 );
      break;
   case '1':
   {
      sonix::instance()->changeAPI( "Stub" );
      snx::SoundInfo si;
      si.filename = "../../../data/sample.wav";
      si.ambient = false;
      si.datasource = snx::SoundInfo::FILESYSTEM;
      kevinSound.configure( si );

      sonix::instance()->changeAPI( "OpenAL" );
   }
   break;
   case '2':
   {
      sonix::instance()->changeAPI( "Stub" );
      snx::SoundInfo si;
      si.filename = "../../../data/suck1.aiff";
      si.ambient = false;
      si.datasource = snx::SoundInfo::FILESYSTEM;
      kevinSound.configure( si );

      sonix::instance()->changeAPI( "AudioWorks" );
   }
   break;
      case '3':
   {
      sonix::instance()->changeAPI( "Stub" );
   }
   break;
      case 'a':
   {
      snx::SoundInfo si;
      si.ambient = false;
      si.filename = "../../../data/sample.wav";
      si.datasource = snx::SoundInfo::FILESYSTEM;
      kevinSound.configure( si );
   }
   break;
      case 'b':
   {
      snx::SoundInfo si;
      si.ambient = false;
      si.filename = "../../../data/sample-drumsolo-2bars.wav";
      si.datasource = snx::SoundInfo::FILESYSTEM;
      kevinSound.configure( si );
      kevinSound.setAmbient( false );
   }
   break;
   case 't':
   {
      kevinSound.trigger();
   }
   break;
   case 'p':
   {
      kevinSound.pause();
   }
   break;
   case 's':
   {
      kevinSound.stop();
   }
   break;

   case ',':
   {
      soundpos -= 1;
      kevinSound.setPosition( soundpos, 0, 0 );
      std::cout<<soundpos<<std::endl;
   }
   break;

   case '.':
   {
      soundpos += 1;
      kevinSound.setPosition( soundpos, 0, 0 );
      std::cout<<soundpos<<std::endl;
   }
   break;

   default:
      // do nothing if no key is pressed
      break;
   }
}
////////////////////////////////
// This is called on a Up Keypress
////////////////////////////////
static void OnKeyboardUp( unsigned char k, int x, int y )
{
   switch (k)
   {
   case 'a':
      // !!!TODO!!!: add handler for when UP is released
      break;

   case 'z':
      // !!!TODO!!!: add handler for when DOWN is released
      break;

   default:
      // do nothing if no key is pressed
      break;
   }
}

////////////////////////////////
// This is called on a Down Keypress
// of a "special" key such as the grey arrows.
////////////////////////////////
static void OnSpecialKeyboardDown(int k, int x, int y)
{
   switch (k)
   {
   case GLUT_KEY_UP:
      // !!!TODO!!!: add handler for when UP is pressed
      break;
   case GLUT_KEY_DOWN:
      // !!!TODO!!!: add handler for when DOWN is pressed
      break;
   default:
      // do nothing if no special key pressed
      break;
   }
}

////////////////////////////////
// This is called on a Up Keypress
////////////////////////////////
static void OnSpecialKeyboardUp( int k, int x, int y )
{
   switch (k)
   {
   case GLUT_KEY_UP:
      // !!!TODO!!!: add handler for when UP is released
      break;
   case GLUT_KEY_DOWN:
      // !!!TODO!!!: add handler for when DOWN is released
      break;
   default:
      // do nothing if no special key pressed
      break;
   }
}

////////////////////////////////
// This is called when mouse changes position
// x and y are the screen position
// in your 2D window's coordinate frame
////////////////////////////////
static void OnMousePos( int x, int y )
{
   // !!!TODO!!!: do something based on mouse position
}

////////////////////////////////
// This is called when mouse clicks
////////////////////////////////
static void OnMouseClick( int a, int b, int c, int d )
{
   // !!!TODO!!!: Need mouse interaction?
   //             read the glut docs/manpage to find out how to query
   //             which button was pressed...
   //             you may have to get this from the glut website
   //             (use www.google.com to search for it)
}

// Initialize the application
// initialize the state of your app here if needed...
static void OnApplicationInit()
{
   // Don't put open GL code here, this func may be called at anytime
   // even before the API is initialized
   // (like before a graphics context is obtained)

   kevinSound.init( "kevin" );
}

int main(int argc, char* argv[])
{
    // Initialize the application
    // initialize the state of your app here if needed...
    OnApplicationInit();

    // Set the window's initial size
    ::glutInitWindowSize( 640, 480 );
    ::glutInit( &argc, argv );

    // Set to double buffer to reduce flicker
    ::glutInitDisplayMode( GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE );

    // Set the window title
    AppWindow::mainWin_contextID = ::glutCreateWindow( "GLUT application" );

    cout<<"\n"<<flush;
    cout<<"sonix sample OpenGL+sound app - by kevin - kevn@vrjuggler.org\n"<<flush;
    cout<<"       usage:  t - trigger\n"<<flush;
    cout<<"               p - pause\n"<<flush;
    cout<<"               s - stop\n"<<flush;
    cout<<"               , - put 3d sound source to the left\n"<<flush;
    cout<<"               . - put 3d sound source to the right\n"<<flush;
    cout<<"               1 - Change subsystem to OpenAL\n"<<flush;
    cout<<"               2 - Change subsystem to AudioWorks\n"<<flush;
    cout<<"               3 - Change subsystem to None\n"<<flush;
    cout<<"               a - configure sound object to sample.wav (OpenAL only)\n"<<flush;
    cout<<"               b - configure sound object to drumsolo.wav (OpenAL only)\n"<<flush;
    cout<<"\n"<<flush;

   // display callbacks.
   ::glutReshapeFunc( OnReshape );
   ::glutIdleFunc( OnIdle );
   ::glutDisplayFunc( OnRedisplay );

   // tell glut to not call the keyboard callback repeatedly
   // when holding down a key. (uses edge triggering, like the mouse)
   ::glutIgnoreKeyRepeat( 1 );

   // keyboard callback functions.
   ::glutKeyboardFunc( OnKeyboardDown );
   ::glutKeyboardUpFunc( OnKeyboardUp );
   ::glutSpecialFunc( OnSpecialKeyboardDown );
   ::glutSpecialUpFunc( OnSpecialKeyboardUp );

   // mouse callback functions...
   ::glutMouseFunc( OnMouseClick );
   ::glutMotionFunc( OnMousePos );
   ::glutPassiveMotionFunc( OnMousePos );

    // start the application loop, your callbacks will now be called
    // time for glut to sit and spin.
    ::glutMainLoop();

    return 1;
}

<|endoftext|>