<commit_before>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author:  Vassil Vassilev <vvasilev@cern.ch>
//------------------------------------------------------------------------------

#include "MetaParser.h"

#include "MetaLexer.h"
#include "MetaSema.h"

#include "cling/Interpreter/Interpreter.h"
#include "cling/Interpreter/InvocationOptions.h"

#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Path.h"

namespace cling {

  MetaParser::MetaParser(MetaSema* Actions) {
    m_Lexer.reset(0);
    m_Actions.reset(Actions);
    const InvocationOptions& Opts = Actions->getInterpreter().getOptions();
    MetaLexer metaSymbolLexer(Opts.MetaString);
    Token Tok;//Tok.startToken();
    while(true) {
      metaSymbolLexer.Lex(Tok);
      if (Tok.is(tok::eof))
        break;
      m_MetaSymbolCache.push_back(Tok);
    }
  }

  void MetaParser::enterNewInputLine(llvm::StringRef Line) {
    m_Lexer.reset(new MetaLexer(Line));
    m_TokenCache.clear();
  }

  void MetaParser::consumeToken() {
    if (m_TokenCache.size())
      m_TokenCache.erase(m_TokenCache.begin());

    lookAhead(0);
  }

  void MetaParser::consumeAnyStringToken(tok::TokenKind stopAt/*=tok::space*/) {
    consumeToken();
    // we have to merge the tokens from the queue until we reach eof token or
    // space token
    SkipWhitespace();
    // Add the new token in which we will merge the others.
    Token& MergedTok = m_TokenCache.front();

    if (MergedTok.is(stopAt) || MergedTok.is(tok::eof)
        || MergedTok.is(tok::comment))
      return;

    Token Tok = lookAhead(1);
    while (Tok.isNot(stopAt) && Tok.isNot(tok::eof)){
      //MergedTok.setLength(MergedTok.getLength() + Tok.getLength());
      m_TokenCache.erase(m_TokenCache.begin() + 1);
      Tok = lookAhead(1);
    }
    MergedTok.setKind(tok::raw_ident);
    MergedTok.setLength(Tok.getBufStart() - MergedTok.getBufStart());
  }

  const Token& MetaParser::lookAhead(unsigned N) {
    if (N < m_TokenCache.size())
      return m_TokenCache[N];

    for (unsigned C = N+1 - m_TokenCache.size(); C > 0; --C) {
      m_TokenCache.push_back(Token());
      m_Lexer->Lex(m_TokenCache.back());
    }
    return m_TokenCache.back();
  }

  void MetaParser::SkipWhitespace() {
    while(getCurTok().is(tok::space))
      consumeToken();
  }

  bool MetaParser::isMetaCommand() {
    return isCommandSymbol() && isCommand();
  }

  bool MetaParser::isCommandSymbol() {
    for (size_t i = 0; i < m_MetaSymbolCache.size(); ++i) {
      if (getCurTok().getKind() != m_MetaSymbolCache[i].getKind())
        return false;
      consumeToken();
    }
    return true;
  }

  bool MetaParser::isCommand() {
    return isLCommand() || isXCommand() || isqCommand()
      || isUCommand() || isICommand() || israwInputCommand()
      || isprintASTCommand() || isdynamicExtensionsCommand() || ishelpCommand()
      || isfileExCommand() || isfilesCommand() || isClassCommand();
  }

  // L := 'L' FilePath
  // FilePath := AnyString
  // AnyString := .*^(' ' | '\t')
  bool MetaParser::isLCommand() {
    bool result = false;
    if (getCurTok().is(tok::ident) && getCurTok().getIdent().equals("L")) {
      consumeAnyStringToken();
      if (getCurTok().is(tok::raw_ident)) {
        result = true;
        m_Actions->actOnLCommand(llvm::sys::Path(getCurTok().getIdent()));
        consumeToken();
        if (getCurTok().is(tok::comment)) {
          consumeAnyStringToken();
          m_Actions->actOnComment(getCurTok().getIdent());
        }
      }
    }
    // TODO: Some fine grained diagnostics
    return result;
  }

  // XCommand := 'x' FilePath[ArgList] | 'X' FilePath[ArgList]
  // FilePath := AnyString
  // ArgList := (ExtraArgList) ' ' [ArgList]
  // ExtraArgList := AnyString [, ExtraArgList]
  bool MetaParser::isXCommand() {
    bool result = false;
    const Token& Tok = getCurTok();
    if (Tok.is(tok::ident) && (Tok.getIdent().equals("x")
                               || Tok.getIdent().equals("X"))) {
      // There might be ArgList
      consumeAnyStringToken(tok::l_paren);
      llvm::sys::Path file(getCurTok().getIdent());
      llvm::StringRef args;
      result = true;
      consumeToken();
      if (getCurTok().is(tok::l_paren) && isExtraArgList()) {
        args = getCurTok().getIdent();
        consumeToken(); // consume the closing paren
      }
      m_Actions->actOnxCommand(file, args);
      if (getCurTok().is(tok::comment)) {
        consumeAnyStringToken();
        m_Actions->actOnComment(getCurTok().getIdent());
      }
    }

    return result;
  }

  // ExtraArgList := AnyString [, ExtraArgList]
  bool MetaParser::isExtraArgList() {
    // This might be expanded if we need better arg parsing.
    consumeAnyStringToken(tok::r_paren);

    return getCurTok().is(tok::raw_ident);
  }

  bool MetaParser::isqCommand() {
    bool result = false;
    if (getCurTok().is(tok::ident) && getCurTok().getIdent().equals("q")) {
      result = true;
      m_Actions->actOnqCommand();
    }
    return result;
  }

  bool MetaParser::isUCommand() {
    if (getCurTok().is(tok::ident) && getCurTok().getIdent().equals("q")) {
      m_Actions->actOnUCommand();
      return true;
    }
    return false;
  }

  bool MetaParser::isICommand() {
    if (getCurTok().is(tok::ident) && getCurTok().getIdent().equals("I")) {
      consumeAnyStringToken();
      llvm::sys::Path path;
      if (getCurTok().is(tok::raw_ident))
        path = getCurTok().getIdent();
      m_Actions->actOnICommand(path);
      return true;
    }
    return false;
  }

  bool MetaParser::israwInputCommand() {
    if (getCurTok().is(tok::ident) &&
        getCurTok().getIdent().equals("rawInput")) {
      MetaSema::SwitchMode mode = MetaSema::kToggle;
      consumeToken();
      SkipWhitespace();
      if (getCurTok().is(tok::constant))
        mode = (MetaSema::SwitchMode)getCurTok().getConstant();
      m_Actions->actOnrawInputCommand(mode);
      return true;
    }
    return false;
  }

  bool MetaParser::isprintASTCommand() {
    if (getCurTok().is(tok::ident) &&
        getCurTok().getIdent().equals("printAST")) {
      MetaSema::SwitchMode mode = MetaSema::kToggle;
      consumeToken();
      SkipWhitespace();
      if (getCurTok().is(tok::constant))
        mode = (MetaSema::SwitchMode)getCurTok().getConstant();
      m_Actions->actOnprintASTCommand(mode);
      return true;
    }
    return false;
  }

  bool MetaParser::isdynamicExtensionsCommand() {
    if (getCurTok().is(tok::ident) &&
        getCurTok().getIdent().equals("dynamicExtensions")) {
      MetaSema::SwitchMode mode = MetaSema::kToggle;
      consumeToken();
      SkipWhitespace();
      if (getCurTok().is(tok::constant))
        mode = (MetaSema::SwitchMode)getCurTok().getConstant();
      m_Actions->actOndynamicExtensionsCommand(mode);
      return true;
    }
    return false;
  }

  bool MetaParser::ishelpCommand() {
    if (getCurTok().is(tok::ident) && getCurTok().getIdent().equals("help")) {
      m_Actions->actOnhelpCommand();
      return true;
    }
    return false;
  }

  bool MetaParser::isfileExCommand() {
    if (getCurTok().is(tok::ident) && getCurTok().getIdent().equals("fileEx")) {
      m_Actions->actOnfileExCommand();
      return true;
    }
    return false;
  }

  bool MetaParser::isfilesCommand() {
    if (getCurTok().is(tok::ident) && getCurTok().getIdent().equals("files")) {
      m_Actions->actOnfilesCommand();
      return true;
    }
    return false;
  }

  bool MetaParser::isClassCommand() {
    const Token& Tok = getCurTok();
    if (Tok.is(tok::ident)) {
      if (Tok.getIdent().equals("class")) {
        consumeAnyStringToken();
        const Token& NextTok = getCurTok();
        llvm::StringRef className;
        if (NextTok.is(tok::raw_ident))
          className = Tok.getIdent();
        m_Actions->actOnclassCommand(className);
        return true;
      }
      else if (Tok.getIdent().equals("Class")) {
        m_Actions->actOnClassCommand();
        return true;
      }
    }
    return false;
  }

} // end namespace cling
<commit_msg>Remove redundant comment.<commit_after>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author:  Vassil Vassilev <vvasilev@cern.ch>
//------------------------------------------------------------------------------

#include "MetaParser.h"

#include "MetaLexer.h"
#include "MetaSema.h"

#include "cling/Interpreter/Interpreter.h"
#include "cling/Interpreter/InvocationOptions.h"

#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Path.h"

namespace cling {

  MetaParser::MetaParser(MetaSema* Actions) {
    m_Lexer.reset(0);
    m_Actions.reset(Actions);
    const InvocationOptions& Opts = Actions->getInterpreter().getOptions();
    MetaLexer metaSymbolLexer(Opts.MetaString);
    Token Tok;
    while(true) {
      metaSymbolLexer.Lex(Tok);
      if (Tok.is(tok::eof))
        break;
      m_MetaSymbolCache.push_back(Tok);
    }
  }

  void MetaParser::enterNewInputLine(llvm::StringRef Line) {
    m_Lexer.reset(new MetaLexer(Line));
    m_TokenCache.clear();
  }

  void MetaParser::consumeToken() {
    if (m_TokenCache.size())
      m_TokenCache.erase(m_TokenCache.begin());

    lookAhead(0);
  }

  void MetaParser::consumeAnyStringToken(tok::TokenKind stopAt/*=tok::space*/) {
    consumeToken();
    // we have to merge the tokens from the queue until we reach eof token or
    // space token
    SkipWhitespace();
    // Add the new token in which we will merge the others.
    Token& MergedTok = m_TokenCache.front();

    if (MergedTok.is(stopAt) || MergedTok.is(tok::eof)
        || MergedTok.is(tok::comment))
      return;

    Token Tok = lookAhead(1);
    while (Tok.isNot(stopAt) && Tok.isNot(tok::eof)){
      //MergedTok.setLength(MergedTok.getLength() + Tok.getLength());
      m_TokenCache.erase(m_TokenCache.begin() + 1);
      Tok = lookAhead(1);
    }
    MergedTok.setKind(tok::raw_ident);
    MergedTok.setLength(Tok.getBufStart() - MergedTok.getBufStart());
  }

  const Token& MetaParser::lookAhead(unsigned N) {
    if (N < m_TokenCache.size())
      return m_TokenCache[N];

    for (unsigned C = N+1 - m_TokenCache.size(); C > 0; --C) {
      m_TokenCache.push_back(Token());
      m_Lexer->Lex(m_TokenCache.back());
    }
    return m_TokenCache.back();
  }

  void MetaParser::SkipWhitespace() {
    while(getCurTok().is(tok::space))
      consumeToken();
  }

  bool MetaParser::isMetaCommand() {
    return isCommandSymbol() && isCommand();
  }

  bool MetaParser::isCommandSymbol() {
    for (size_t i = 0; i < m_MetaSymbolCache.size(); ++i) {
      if (getCurTok().getKind() != m_MetaSymbolCache[i].getKind())
        return false;
      consumeToken();
    }
    return true;
  }

  bool MetaParser::isCommand() {
    return isLCommand() || isXCommand() || isqCommand()
      || isUCommand() || isICommand() || israwInputCommand()
      || isprintASTCommand() || isdynamicExtensionsCommand() || ishelpCommand()
      || isfileExCommand() || isfilesCommand() || isClassCommand();
  }

  // L := 'L' FilePath
  // FilePath := AnyString
  // AnyString := .*^(' ' | '\t')
  bool MetaParser::isLCommand() {
    bool result = false;
    if (getCurTok().is(tok::ident) && getCurTok().getIdent().equals("L")) {
      consumeAnyStringToken();
      if (getCurTok().is(tok::raw_ident)) {
        result = true;
        m_Actions->actOnLCommand(llvm::sys::Path(getCurTok().getIdent()));
        consumeToken();
        if (getCurTok().is(tok::comment)) {
          consumeAnyStringToken();
          m_Actions->actOnComment(getCurTok().getIdent());
        }
      }
    }
    // TODO: Some fine grained diagnostics
    return result;
  }

  // XCommand := 'x' FilePath[ArgList] | 'X' FilePath[ArgList]
  // FilePath := AnyString
  // ArgList := (ExtraArgList) ' ' [ArgList]
  // ExtraArgList := AnyString [, ExtraArgList]
  bool MetaParser::isXCommand() {
    bool result = false;
    const Token& Tok = getCurTok();
    if (Tok.is(tok::ident) && (Tok.getIdent().equals("x")
                               || Tok.getIdent().equals("X"))) {
      // There might be ArgList
      consumeAnyStringToken(tok::l_paren);
      llvm::sys::Path file(getCurTok().getIdent());
      llvm::StringRef args;
      result = true;
      consumeToken();
      if (getCurTok().is(tok::l_paren) && isExtraArgList()) {
        args = getCurTok().getIdent();
        consumeToken(); // consume the closing paren
      }
      m_Actions->actOnxCommand(file, args);
      if (getCurTok().is(tok::comment)) {
        consumeAnyStringToken();
        m_Actions->actOnComment(getCurTok().getIdent());
      }
    }

    return result;
  }

  // ExtraArgList := AnyString [, ExtraArgList]
  bool MetaParser::isExtraArgList() {
    // This might be expanded if we need better arg parsing.
    consumeAnyStringToken(tok::r_paren);

    return getCurTok().is(tok::raw_ident);
  }

  bool MetaParser::isqCommand() {
    bool result = false;
    if (getCurTok().is(tok::ident) && getCurTok().getIdent().equals("q")) {
      result = true;
      m_Actions->actOnqCommand();
    }
    return result;
  }

  bool MetaParser::isUCommand() {
    if (getCurTok().is(tok::ident) && getCurTok().getIdent().equals("q")) {
      m_Actions->actOnUCommand();
      return true;
    }
    return false;
  }

  bool MetaParser::isICommand() {
    if (getCurTok().is(tok::ident) && getCurTok().getIdent().equals("I")) {
      consumeAnyStringToken();
      llvm::sys::Path path;
      if (getCurTok().is(tok::raw_ident))
        path = getCurTok().getIdent();
      m_Actions->actOnICommand(path);
      return true;
    }
    return false;
  }

  bool MetaParser::israwInputCommand() {
    if (getCurTok().is(tok::ident) &&
        getCurTok().getIdent().equals("rawInput")) {
      MetaSema::SwitchMode mode = MetaSema::kToggle;
      consumeToken();
      SkipWhitespace();
      if (getCurTok().is(tok::constant))
        mode = (MetaSema::SwitchMode)getCurTok().getConstant();
      m_Actions->actOnrawInputCommand(mode);
      return true;
    }
    return false;
  }

  bool MetaParser::isprintASTCommand() {
    if (getCurTok().is(tok::ident) &&
        getCurTok().getIdent().equals("printAST")) {
      MetaSema::SwitchMode mode = MetaSema::kToggle;
      consumeToken();
      SkipWhitespace();
      if (getCurTok().is(tok::constant))
        mode = (MetaSema::SwitchMode)getCurTok().getConstant();
      m_Actions->actOnprintASTCommand(mode);
      return true;
    }
    return false;
  }

  bool MetaParser::isdynamicExtensionsCommand() {
    if (getCurTok().is(tok::ident) &&
        getCurTok().getIdent().equals("dynamicExtensions")) {
      MetaSema::SwitchMode mode = MetaSema::kToggle;
      consumeToken();
      SkipWhitespace();
      if (getCurTok().is(tok::constant))
        mode = (MetaSema::SwitchMode)getCurTok().getConstant();
      m_Actions->actOndynamicExtensionsCommand(mode);
      return true;
    }
    return false;
  }

  bool MetaParser::ishelpCommand() {
    if (getCurTok().is(tok::ident) && getCurTok().getIdent().equals("help")) {
      m_Actions->actOnhelpCommand();
      return true;
    }
    return false;
  }

  bool MetaParser::isfileExCommand() {
    if (getCurTok().is(tok::ident) && getCurTok().getIdent().equals("fileEx")) {
      m_Actions->actOnfileExCommand();
      return true;
    }
    return false;
  }

  bool MetaParser::isfilesCommand() {
    if (getCurTok().is(tok::ident) && getCurTok().getIdent().equals("files")) {
      m_Actions->actOnfilesCommand();
      return true;
    }
    return false;
  }

  bool MetaParser::isClassCommand() {
    const Token& Tok = getCurTok();
    if (Tok.is(tok::ident)) {
      if (Tok.getIdent().equals("class")) {
        consumeAnyStringToken();
        const Token& NextTok = getCurTok();
        llvm::StringRef className;
        if (NextTok.is(tok::raw_ident))
          className = Tok.getIdent();
        m_Actions->actOnclassCommand(className);
        return true;
      }
      else if (Tok.getIdent().equals("Class")) {
        m_Actions->actOnClassCommand();
        return true;
      }
    }
    return false;
  }

} // end namespace cling
<|endoftext|>


<commit_before>#ifdef NDEBUG
#undef NDEBUG
#endif

#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/Verifier.h>
#include <vector>
#include <string>

struct LLVMCodeGen {
  using IRBuilder = llvm::IRBuilder<>;
  llvm::LLVMContext &Context;
  llvm::Module &M;
  IRBuilder &Builder;

  LLVMCodeGen(llvm::LLVMContext &context, llvm::Module &m,
              llvm::IRBuilder<> &builder)
      : Context(context), M(m), Builder(builder) {}
  void dump() const { M.dump(); }

  //----------------------- Type ---------------------------------------------

  struct Type {
    enum Kind { int32, float32, float64 };

    LLVMCodeGen *CG;
    Kind TypeKind;

    Type(LLVMCodeGen *cg, Kind type_kind) : CG(cg), TypeKind(type_kind) {}
    Type(LLVMCodeGen *cg, llvm::Type *ty) : CG(cg) {
      if (ty->getTypeID() == CG->Builder.getInt32Ty()->getTypeID()) {
        TypeKind = int32;
      } else if (ty->getTypeID() == CG->Builder.getFloatTy()->getTypeID()) {
        TypeKind = float32;
      } else if (ty->getTypeID() == CG->Builder.getDoubleTy()->getTypeID()) {
        TypeKind = float64;
      } else {
        assert(0 && "Must not happend");
      }
    }

    llvm::Type *get() {
      switch (TypeKind) {
      case int32:
        return CG->Builder.getInt32Ty();
      case float32:
        return CG->Builder.getFloatTy();
      case float64:
        return CG->Builder.getDoubleTy();
      default:
        assert(0 && "Must not happen");
      }
    }

    size_t size() {
      switch (TypeKind) {
      case int32:
      case float32:
        return 4;
      case float64:
        return 8;
      default:
        assert(0 && "Must not happen");
      }
      return 0;
    }

  };

  //------------------- Basic Block -------------------------------------------

  struct Function;
  struct BasicBlock {
    LLVMCodeGen *CG;
    llvm::BasicBlock *BB;
    BasicBlock(LLVMCodeGen *cg, llvm::BasicBlock *bb) : CG(cg), BB(bb) {}
    llvm::BasicBlock *get() { return BB; }
    void set() { CG->Builder.SetInsertPoint(BB); }
    Function getParent();
  };

  // -----------------------  Boolean -------------------------------------------

  struct Value;
  struct Boolean {
    LLVMCodeGen *CG;
    llvm::Value *V;

    Boolean(LLVMCodeGen *cg, llvm::Value *value) : CG(cg), V(value) {}

    llvm::Value *get() { return V; }

    void mkIfThenElse(BasicBlock thenBB, BasicBlock elseBB) {
      CG->Builder.CreateCondBr(V, thenBB.get(), elseBB.get());
    }
    Value mkSelect(Value thenV, Value elseV);
  };

  // -----------------------  Value -------------------------------------------
  struct Value {
    LLVMCodeGen *CG;
    llvm::Value *V;

    Value(LLVMCodeGen *cg, llvm::Value *value) : CG(cg), V(value) {}
    Type getType() { return {CG, V->getType()}; }

    llvm::Value *get() { return V; }

    Value operator*(Value R) {
      return {CG, CG->Builder.CreateMul(V, R.get(), "mul")};
    }
    Value operator+(Value R) {
      return {CG, CG->Builder.CreateAdd(V, R.get(), "add")};
    }

    Boolean operator<(Value R) {
      return {CG, CG->Builder.CreateICmpULT(V, R.get(), "lt")};
    }
    Boolean operator!=(Value R) {
      return {CG, CG->Builder.CreateICmpNE(V, R.get(), "ne")};
    }
  };

#if 0
  // --------------------- Global Variable -----------------------------------

  struct GlobalVar {
    LLVMCodeGen *CG;
    llvm::GlobalVariable *Var;
    GlobalVar(LLVMCodeGen *cg, llvm::GlobalVariable *var)
        : CG(cg), Var(var) {}
  };

  GlobalVar mkGlobalVar(std::string name, Type type) {
    M.getOrInsertGlobal(name, type.get());
    llvm::GlobalVariable *gvar = M.getNamedGlobal(name);
    gvar->setLinkage(llvm::GlobalValue::CommonLinkage);
    gvar->setAlignment(type.size());
    return GlobalVar{this, gvar};
  }
#endif

  //---------------------- Function -------------------------------------------

  struct Function {
    LLVMCodeGen *CG;
    llvm::Function *F;
    std::vector<llvm::Value*> Args;
    Function(LLVMCodeGen *cg, llvm::Function *f) : CG(cg), F(f) {
      Args.reserve(16);
      for (auto &arg : F->args()) {
        Args.push_back(&arg);
      }
    }
    bool verify() const { return llvm::verifyFunction(*F); }
    size_t n_args() const { return Args.size(); }

    llvm::Function *get() { return F; }

    Value arg(size_t num) {
      assert(num < Args.size());
      return Value{CG, Args[num]};
    }

    BasicBlock mkBasicBlock(std::string name) {
      llvm::BasicBlock *bb = llvm::BasicBlock::Create(CG->Context, name, F);
      return {CG, bb};
    }
  };

  Function mkFunction(std::string name, Type ret_type,
                      std::vector<std::pair<Type, std::string>> args = {}) {
    std::vector<llvm::Type*> args_type;
    args_type.reserve(args.size());
    for (auto arg : args) {
      args_type.push_back(arg.first.get());
    }
    llvm::FunctionType *funcType =
        llvm::FunctionType::get(ret_type.get(), args_type, false);
    llvm::Function *func = llvm::Function::Create(
        funcType, llvm::Function::ExternalLinkage, name, &M);
    int count = 0;
    for (auto &arg : func->args()) {
      arg.setName(args[count++].second);
    }
    return Function{this, func};
  }
  Function mkFunction(std::string name,
                      std::vector<std::pair<Type, std::string>> args = {}) {
    std::vector<llvm::Type*> args_type;
    args_type.reserve(args.size());
    for (auto arg : args) {
      args_type.push_back(arg.first.get());
    }
    llvm::FunctionType *funcType =
        llvm::FunctionType::get(Builder.getVoidTy(), args_type, false);
    llvm::Function *func = llvm::Function::Create(
        funcType, llvm::Function::ExternalLinkage, name, &M);
    int count = 0;
    for (auto &arg : func->args()) {
      arg.setName(args[count++].second);
    }
    return Function{this, func};
  }

  // -------------------------- Call ------------------------------------------
  struct CallInst {
    LLVMCodeGen *CG;
    llvm::CallInst *Inst;
    CallInst(LLVMCodeGen *cg, llvm::CallInst *inst) : CG(cg), Inst(inst) {}

    operator Value() { return {CG, Inst}; }
    llvm::CallInst *get() { return Inst; }
  };

  CallInst mkCall(Function &f, std::vector<Value> args) {
    assert(args.size() == f.n_args() && "Argument count mismatch");
    std::vector<llvm::Value*> arguments;
    arguments.reserve(args.size());
    for (auto &arg : args) {
      arguments.push_back(arg.get());
    }
    return {this, Builder.CreateCall(f.get(), arguments)};
  }

  // --------------------------- Phi node ------------------------------------
  struct Phi {
    LLVMCodeGen *CG;
    llvm::PHINode *V;

    Phi(LLVMCodeGen *cg, llvm::PHINode *v) : CG(cg), V(v) {}
    llvm::PHINode *get() { return V; }

    operator Value() { return {CG, V}; }

    Phi &operator+=(std::pair<Value, BasicBlock> rhs) {
      V->addIncoming(rhs.first.get(), rhs.second.get());
      return *this;
    }
  };

  Phi mkPhi(Type type, std::vector<std::pair<Value, BasicBlock>> edge_list) {
    llvm::PHINode *phi = Builder.CreatePHI(type.get(), edge_list.size());
    for (auto &edge : edge_list) {
      phi->addIncoming(edge.first.get(), edge.second.get());
    }
    return {this, phi};
  }

  // ------------- type & vals

  Type mkIntTy() { return Type(this, Type::int32); }
  Value mkInt(int val) { return {this, Builder.getInt32(val)}; }
  Type mkFloatTy() { return Type(this, Type::float32); }
  Type mkDoubleTy() { return Type(this, Type::float64); }

  BasicBlock getCurrentBasicBlock() { return {this, Builder.GetInsertBlock()}; }

  //-------------------- Ops

  void mkRet(Value val) { Builder.CreateRet(val.get()); }
  void mkRetVoid() { Builder.CreateRetVoid(); }

  // ---------------- loop
  Value mkLoop(Value begin, Value end, Value step, Function loop_body) {
    auto bb = getCurrentBasicBlock();
    auto preBB   = bb.getParent().mkBasicBlock("preBB");
    auto loopBB  = bb.getParent().mkBasicBlock("loopBB");
    auto afterBB = bb.getParent().mkBasicBlock("afterBB");
    mkBranch(preBB);

    preBB.set();
    auto iv = mkPhi(step.getType(), {{begin, bb}});
    auto cond = Value(iv) < end;
    cond.mkIfThenElse(loopBB, afterBB);

    loopBB.set();
    mkCall(loop_body, {iv});
    auto next = step + iv;
    iv += {next, loopBB};
    mkBranch(preBB);

    afterBB.set();
    return iv;
  }

  // ---------------- branchInst
  struct BranchInst {
    LLVMCodeGen *CG;
    llvm::BranchInst *Inst;
    BranchInst(LLVMCodeGen *cg, llvm::BranchInst *inst) : CG(cg), Inst(inst) {}

    operator Value() { return {CG, Inst}; }
    llvm::BranchInst *get() { return Inst; }
  };

  BranchInst mkBranch(BasicBlock &bb) {
    return {this, Builder.CreateBr(bb.get())};
  }

  bool verifyModule() {
    return llvm::verifyModule(M);
  }

};
LLVMCodeGen::Function LLVMCodeGen::BasicBlock::getParent() {
  return {CG, BB->getParent()};
}
LLVMCodeGen::Value LLVMCodeGen::Boolean::mkSelect(Value thenV, Value elseV) {
  return {CG, CG->Builder.CreateSelect(V, thenV.get(), elseV.get())};
}

static llvm::LLVMContext &ContextRef = llvm::getGlobalContext();
static llvm::Module *ModuleOb = new llvm::Module("my compiler", ContextRef);

int main(int argc, char *argv[]) {
  static llvm::IRBuilder<> BuilderObj(ContextRef);
  LLVMCodeGen cg(ContextRef, *ModuleOb, BuilderObj);

#if 0
  auto gvar = cg.mkGlobalVar("x", cg.mkFloatTy());
#endif

  auto f = cg.mkFunction("foo", cg.mkIntTy(),
                         {{cg.mkIntTy(), "a"}, {cg.mkIntTy(), "b"}});

  auto entryBB = f.mkBasicBlock("entry");
  auto thenBB = f.mkBasicBlock("then");
  auto elseBB = f.mkBasicBlock("else");
  auto mergeBB = f.mkBasicBlock("cont");

  entryBB.set();
  auto val = cg.mkInt(100);
  auto cnd = f.arg(0) < val;
  cnd.mkIfThenElse(thenBB, elseBB);

  thenBB.set();
  auto then_val = f.arg(0) + cg.mkInt(1);
  cg.mkBranch(mergeBB);

  elseBB.set();
  auto else_val = f.arg(1) + cg.mkInt(2);
  cg.mkBranch(mergeBB);

  mergeBB.set();
  auto phi = cg.mkPhi(cg.mkIntTy(), {{then_val, thenBB}, {else_val, elseBB}});

  auto f1 = cg.mkFunction("foo", {{cg.mkIntTy(), "m"}});
  auto f1entryBB = f1.mkBasicBlock("entry");

  auto last = cg.mkLoop(cg.mkInt(0),  phi, cg.mkInt(1), f1);
  auto cmp = last != cg.mkInt(32);
  cg.mkRet(cmp.mkSelect(last, cg.mkInt(44)));

  f1entryBB.set();
  cg.mkRetVoid();

  assert(!f.verify());
  assert(!f1.verify());
  assert(!cg.verifyModule());

  cg.dump();
  return 0;
}
<commit_msg>added error<commit_after>#ifdef NDEBUG
#undef NDEBUG
#endif

#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/Verifier.h>
#include <llvm/Support/raw_ostream.h>
#include <vector>
#include <string>
#include <iostream>

struct LLVMCodeGen {
  using IRBuilder = llvm::IRBuilder<>;
  llvm::LLVMContext &Context;
  llvm::Module &M;
  IRBuilder &Builder;

  LLVMCodeGen(llvm::LLVMContext &context, llvm::Module &m,
              llvm::IRBuilder<> &builder)
      : Context(context), M(m), Builder(builder) {}
  void dump() const { M.dump(); }

  //----------------------- Type ---------------------------------------------

  struct Type {
    enum Kind { int32, float32, float64 };

    LLVMCodeGen *CG;
    Kind TypeKind;

    Type(LLVMCodeGen *cg, Kind type_kind) : CG(cg), TypeKind(type_kind) {}
    Type(LLVMCodeGen *cg, llvm::Type *ty) : CG(cg) {
      if (ty->getTypeID() == CG->Builder.getInt32Ty()->getTypeID()) {
        TypeKind = int32;
      } else if (ty->getTypeID() == CG->Builder.getFloatTy()->getTypeID()) {
        TypeKind = float32;
      } else if (ty->getTypeID() == CG->Builder.getDoubleTy()->getTypeID()) {
        TypeKind = float64;
      } else {
        assert(0 && "Must not happend");
      }
    }

    llvm::Type *get() {
      switch (TypeKind) {
      case int32:
        return CG->Builder.getInt32Ty();
      case float32:
        return CG->Builder.getFloatTy();
      case float64:
        return CG->Builder.getDoubleTy();
      default:
        assert(0 && "Must not happen");
      }
    }

    size_t size() {
      switch (TypeKind) {
      case int32:
      case float32:
        return 4;
      case float64:
        return 8;
      default:
        assert(0 && "Must not happen");
      }
      return 0;
    }

  };

  //------------------- Basic Block -------------------------------------------

  struct Function;
  struct BasicBlock {
    LLVMCodeGen *CG;
    llvm::BasicBlock *BB;
    BasicBlock(LLVMCodeGen *cg, llvm::BasicBlock *bb) : CG(cg), BB(bb) {}
    llvm::BasicBlock *get() { return BB; }
    void set() { CG->Builder.SetInsertPoint(BB); }
    Function getParent();
  };

  // -----------------------  Boolean -------------------------------------------

  struct Value;

  struct Boolean {
    LLVMCodeGen *CG;
    llvm::Value *V;

    Boolean(LLVMCodeGen *cg, llvm::Value *value) : CG(cg), V(value) {}

    llvm::Value *get() { return V; }

    void mkIfThenElse(BasicBlock thenBB, BasicBlock elseBB) {
      CG->Builder.CreateCondBr(V, thenBB.get(), elseBB.get());
    }
    Value mkSelect(Value thenV, Value elseV);
  };

  // -----------------------  Value -------------------------------------------
  struct Value {
    LLVMCodeGen *CG;
    llvm::Value *V;

    Value(LLVMCodeGen *cg, llvm::Value *value) : CG(cg), V(value) {}
    Type getType() { return {CG, V->getType()}; }

    llvm::Value *get() { return V; }
    void set(llvm::Value *v) {
    }

    Value operator*(Value R) {
      return {CG, CG->Builder.CreateMul(V, R.get(), "mul")};
    }
    Value operator+(Value R) {
      return {CG, CG->Builder.CreateAdd(V, R.get(), "add")};
    }

    Boolean operator<(Value R) {
      return {CG, CG->Builder.CreateICmpULT(V, R.get(), "lt")};
    }
    Boolean operator!=(Value R) {
      return {CG, CG->Builder.CreateICmpNE(V, R.get(), "ne")};
    }
  };

#if 0
  // --------------------- Global Variable -----------------------------------

  struct GlobalVar {
    LLVMCodeGen *CG;
    llvm::GlobalVariable *Var;
    GlobalVar(LLVMCodeGen *cg, llvm::GlobalVariable *var)
        : CG(cg), Var(var) {}
  };

  GlobalVar mkGlobalVar(std::string name, Type type) {
    M.getOrInsertGlobal(name, type.get());
    llvm::GlobalVariable *gvar = M.getNamedGlobal(name);
    gvar->setLinkage(llvm::GlobalValue::CommonLinkage);
    gvar->setAlignment(type.size());
    return GlobalVar{this, gvar};
  }
#endif

  //---------------------- Function -------------------------------------------

  struct Function {
    LLVMCodeGen *CG;
    llvm::Function *F;
    std::vector<llvm::Value*> Args;
    Function(LLVMCodeGen *cg, llvm::Function *f) : CG(cg), F(f) {
      Args.reserve(16);
      for (auto &arg : F->args()) {
        Args.push_back(&arg);
      }
    }
    std::string ErrorString;
    bool verify() {
      ErrorString.clear();
      llvm::raw_string_ostream os(ErrorString);
      return llvm::verifyFunction(*F, &os);
    }
    size_t n_args() const { return Args.size(); }

    llvm::Function *get() { return F; }
    std::string const& getErrorString() { return ErrorString;}

    Value arg(size_t num) {
      assert(num < Args.size());
      return Value{CG, Args[num]};
    }

    BasicBlock mkBasicBlock(std::string name) {
      llvm::BasicBlock *bb = llvm::BasicBlock::Create(CG->Context, name, F);
      return {CG, bb};
    }
  };

  Function mkFunction(std::string name, Type ret_type,
                      std::vector<std::pair<Type, std::string>> args = {}) {
    std::vector<llvm::Type*> args_type;
    args_type.reserve(args.size());
    for (auto arg : args) {
      args_type.push_back(arg.first.get());
    }
    llvm::FunctionType *funcType =
        llvm::FunctionType::get(ret_type.get(), args_type, false);
    llvm::Function *func = llvm::Function::Create(
        funcType, llvm::Function::ExternalLinkage, name, &M);
    int count = 0;
    for (auto &arg : func->args()) {
      arg.setName(args[count++].second);
    }
    return Function{this, func};
  }
  Function mkFunction(std::string name,
                      std::vector<std::pair<Type, std::string>> args = {}) {
    std::vector<llvm::Type*> args_type;
    args_type.reserve(args.size());
    for (auto arg : args) {
      args_type.push_back(arg.first.get());
    }
    llvm::FunctionType *funcType =
        llvm::FunctionType::get(Builder.getVoidTy(), args_type, false);
    llvm::Function *func = llvm::Function::Create(
        funcType, llvm::Function::ExternalLinkage, name, &M);
    int count = 0;
    for (auto &arg : func->args()) {
      arg.setName(args[count++].second);
    }
    return Function{this, func};
  }

  // -------------------------- Call ------------------------------------------
  struct CallInst {
    LLVMCodeGen *CG;
    llvm::CallInst *Inst;
    CallInst(LLVMCodeGen *cg, llvm::CallInst *inst) : CG(cg), Inst(inst) {}

    operator Value() { return {CG, Inst}; }
    llvm::CallInst *get() { return Inst; }
  };

  CallInst mkCall(Function &f, std::vector<Value> args) {
    assert(args.size() == f.n_args() && "Argument count mismatch");
    std::vector<llvm::Value*> arguments;
    arguments.reserve(args.size());
    for (auto &arg : args) {
      arguments.push_back(arg.get());
    }
    return {this, Builder.CreateCall(f.get(), arguments)};
  }

  // --------------------------- Phi node ------------------------------------
  struct Phi {
    LLVMCodeGen *CG;
    llvm::PHINode *V;

    Phi(LLVMCodeGen *cg, llvm::PHINode *v) : CG(cg), V(v) {}
    llvm::PHINode *get() { return V; }

    operator Value() { return {CG, V}; }

    Phi &operator+=(std::pair<Value, BasicBlock> rhs) {
      V->addIncoming(rhs.first.get(), rhs.second.get());
      return *this;
    }
  };

  Phi mkPhi(Type type, std::vector<std::pair<Value, BasicBlock>> edge_list) {
    llvm::PHINode *phi = Builder.CreatePHI(type.get(), edge_list.size());
    for (auto &edge : edge_list) {
      phi->addIncoming(edge.first.get(), edge.second.get());
    }
    return {this, phi};
  }

  // ------------- type & vals

  Type mkIntTy() { return Type(this, Type::int32); }
  Value mkInt(int val) { return {this, Builder.getInt32(val)}; }
  Type mkFloatTy() { return Type(this, Type::float32); }
  Type mkDoubleTy() { return Type(this, Type::float64); }

  BasicBlock getCurrentBasicBlock() { return {this, Builder.GetInsertBlock()}; }

  //-------------------- Ops

  void mkRet(Value val) { Builder.CreateRet(val.get()); }
  void mkRetVoid() { Builder.CreateRetVoid(); }

  // ---------------- loop

  Value mkLoop(Value begin, Value end, Value step, Function loop_body) {
    auto bb = getCurrentBasicBlock();
    auto preBB   = bb.getParent().mkBasicBlock("preBB");
    auto loopBB  = bb.getParent().mkBasicBlock("loopBB");
    auto afterBB = bb.getParent().mkBasicBlock("afterBB");
    mkBranch(preBB);

    preBB.set();
    auto iv = mkPhi(step.getType(), {{begin, bb}});
    auto cond = Value(iv) < end;
    cond.mkIfThenElse(loopBB, afterBB);

    loopBB.set();
    mkCall(loop_body, {iv});
    auto next = step + iv;
    iv += {next, loopBB};
    mkBranch(preBB);

    afterBB.set();
    return iv;
  }

  template <class F> Value mkLoop(Value begin, Value end, Value step, F body) {
    auto bb = getCurrentBasicBlock();
    auto preBB   = bb.getParent().mkBasicBlock("preBB");
    auto loopBB  = bb.getParent().mkBasicBlock("loopBB");
    auto afterBB = bb.getParent().mkBasicBlock("afterBB");
    mkBranch(preBB);

    preBB.set();
    auto iv = mkPhi(step.getType(), {{begin, bb}});
    auto cond = Value(iv) < end;
    cond.mkIfThenElse(loopBB, afterBB);

    loopBB.set();
    body(iv);
    auto next = step + iv;
    iv += {next, loopBB};
    mkBranch(preBB);

    afterBB.set();
    return iv;
  }

  template <class F>
  void mkLoopImpl(std::vector<Value> begs, std::vector<Value> ends,
                   std::vector<Value> steps, F body, std::vector<Value> &ivs,
                   std::vector<Value> &end_ivs,
                   std::vector<BasicBlock> &end_bbs) {
    // must be matching sizes
    assert(begs.size() == ends.size());
    assert(begs.size() == steps.size());

    // if we proceeded all indices, just emplace body of the loop, and return
    if (begs.empty()) {

      // since ivs are stored in reverse, so reverse the list :)
      std::reverse(ivs.begin(), ivs.end());

      // emplace function body
      body(ivs.data());
      return;
    }

    // otherwise, process next index in reverse order
    auto beg = begs.back();
    auto end = ends.back();
    auto step = steps.back();
    begs.pop_back();
    ends.pop_back();
    steps.pop_back();

    // make 1d loop, which recursively calls this function with 1 index less
    auto ret = mkLoop(beg, end, step, [&](Value iv) {
      ivs.push_back(iv);
      mkLoopImpl(begs, ends, steps, body, ivs, end_ivs, end_bbs);
    });
    auto bb = getCurrentBasicBlock();
    end_ivs.push_back(ret);
    end_bbs.push_back(bb);
  }

  template <class F>
  std::vector<Value> mkLoopV(std::vector<Value> begs, std::vector<Value> ends,
                             std::vector<Value> steps, F body) {
    assert(!begs.empty());

    // since impl iterates from the last index to the first, so invert the list
    std::reverse(begs.begin(), begs.end());
    std::reverse(ends.begin(), ends.end());
    std::reverse(steps.begin(), steps.end());

    // ending inductin variable list
    std::vector<Value> ivs, end_ivs;
    std::vector<BasicBlock> end_bbs;
    ivs.reserve(begs.size());
    end_ivs.reserve(begs.size());
    end_bbs.reserve(begs.size());

    // generate the loop
    mkLoopImpl(begs, ends, steps, body, ivs, end_ivs, end_bbs);
    std::reverse(end_ivs.begin(), end_ivs.end());
    end_bbs.back().set();

    return end_ivs;
  }

  // ---------------- branchInst
  struct BranchInst {
    LLVMCodeGen *CG;
    llvm::BranchInst *Inst;
    BranchInst(LLVMCodeGen *cg, llvm::BranchInst *inst) : CG(cg), Inst(inst) {}

    operator Value() { return {CG, Inst}; }
    llvm::BranchInst *get() { return Inst; }
    };

    BranchInst mkBranch(BasicBlock & bb) {
      return {this, Builder.CreateBr(bb.get())};
    }

    bool verifyModule() { return llvm::verifyModule(M); }

};
LLVMCodeGen::Function LLVMCodeGen::BasicBlock::getParent() {
  return {CG, BB->getParent()};
}
LLVMCodeGen::Value LLVMCodeGen::Boolean::mkSelect(Value thenV, Value elseV) {
  return {CG, CG->Builder.CreateSelect(V, thenV.get(), elseV.get())};
}

static llvm::LLVMContext &ContextRef = llvm::getGlobalContext();
static llvm::Module *ModuleOb = new llvm::Module("my compiler", ContextRef);

int main(int argc, char *argv[]) {
  static llvm::IRBuilder<> BuilderObj(ContextRef);
  LLVMCodeGen cg(ContextRef, *ModuleOb, BuilderObj);

#if 0
  auto gvar = cg.mkGlobalVar("x", cg.mkFloatTy());
#endif

  auto f = cg.mkFunction("foo", cg.mkIntTy(),
                         {{cg.mkIntTy(), "a"}, {cg.mkIntTy(), "b"}});

  auto entryBB = f.mkBasicBlock("entry");
  auto thenBB = f.mkBasicBlock("then");
  auto elseBB = f.mkBasicBlock("else");
  auto mergeBB = f.mkBasicBlock("cont");

  entryBB.set();
  auto val = cg.mkInt(100);
  auto cnd = f.arg(0) < val;
  cnd.mkIfThenElse(thenBB, elseBB);

  thenBB.set();
  auto then_val = f.arg(0) + cg.mkInt(1);
  cg.mkBranch(mergeBB);

  elseBB.set();
  auto else_val = f.arg(1) + cg.mkInt(2);
  cg.mkBranch(mergeBB);

  mergeBB.set();
  auto phi = cg.mkPhi(cg.mkIntTy(), {{then_val, thenBB}, {else_val, elseBB}});

  auto f1 = [&](LLVMCodeGen::Value /*iv*/) {}; // auto v = iv + cg.mkInt(33); };
  auto last = cg.mkLoop(cg.mkInt(0),  phi, cg.mkInt(1), f1);
  auto cmp = last != cg.mkInt(32);

  auto sum = cg.mkInt(0);
#if 0
  auto last1 = cg.mkLoop({cg.mkInt(0), cg.mkInt(0)}, {cg.mkInt(3), cg.mkInt(5)},
                         {cg.mkInt(1), cg.mkInt(1)},
                         [&](LLVMCodeGen::Value *iv) { sum = sum + iv[0] * iv[1]; });
#else
  auto last1 =
      cg.mkLoopV({cg.mkInt(0)}, {cg.mkInt(5)}, {cg.mkInt(1)},
                [&](LLVMCodeGen::Value *iv) { sum = sum + iv[0];});
#endif

  cg.mkRet(cmp.mkSelect(last, sum));

  if (f.verify()) {
    std::cerr << f.getErrorString() << std::endl;
    exit(1);
  }
  assert(!cg.verifyModule());

  cg.dump();
  return 0;
}
<|endoftext|>


<commit_before>#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/Verifier.h>
#include <vector>
#include <string>

struct LLVMCodeGen {
  using IRBuilder = llvm::IRBuilder<>;
  llvm::LLVMContext &Context;
  llvm::Module &M;
  IRBuilder &Builder;

  LLVMCodeGen(llvm::LLVMContext &context, llvm::Module &m,
              llvm::IRBuilder<> &builder)
      : Context(context), M(m), Builder(builder) {}
  void dump() const { M.dump(); }

  //------------------- Basic Block -------------------------------------------

  struct BasicBlock {
    LLVMCodeGen &CG;
    llvm::BasicBlock &BB;
    BasicBlock(LLVMCodeGen &cg, llvm::BasicBlock &bb) : CG(cg), BB(bb) {}
    llvm::BasicBlock *get() { return &BB; }
    void set() { CG.Builder.SetInsertPoint(&BB); }

  };

  // -----------------------  Boolean -------------------------------------------

  struct Boolean {
    LLVMCodeGen *CG;
    llvm::Value *V;

    Boolean(LLVMCodeGen *cg, llvm::Value *value) : CG(cg), V(value) {}

    llvm::Value *get() { return V; }

    void mkIfThenElse(BasicBlock thenBB, BasicBlock elseBB,
                      BasicBlock mergeBB) {
      CG->Builder.CreateCondBr(V, thenBB.get(), elseBB.get());
      CG->Builder.SetInsertPoint(thenBB.get());
      CG->Builder.CreateBr(mergeBB.get());
      CG->Builder.SetInsertPoint(elseBB.get());
      CG->Builder.CreateBr(mergeBB.get());
    }
  };

  // -----------------------  Value -------------------------------------------
  struct Value {
    LLVMCodeGen *CG;
    llvm::Value *V;

    Value(LLVMCodeGen *cg, llvm::Value *value) : CG(cg), V(value) {}

    llvm::Value *get() { return V; }

    Value operator*(Value R) {
      return {CG, CG->Builder.CreateMul(V, R.get(), "mul")};
    }
    Value operator+(Value R) {
      return {CG, CG->Builder.CreateAdd(V, R.get(), "add")};
    }

    Value operator<(Value R) {
      return {CG, CG->Builder.CreateICmpULT(V, R.get(), "lt")};
    }
    Boolean operator!=(Value R) {
      return {CG, CG->Builder.CreateICmpNE(V, R.get(), "ne")};
    }
  };

  //----------------------- Type ---------------------------------------------

  struct Type {
    enum Kind { int32, float32, float64 };

    LLVMCodeGen &CG;
    Kind TypeKind;

    Type(LLVMCodeGen &cg, Kind type_kind) : CG(cg), TypeKind(type_kind) {}

    llvm::Type *get() {
      switch (TypeKind) {
      case int32:
        return CG.Builder.getInt32Ty();
      case float32:
        return CG.Builder.getFloatTy();
      case float64:
        return CG.Builder.getDoubleTy();
      default:
        assert(0 && "Must not happen");
      }
    }

    size_t size() {
      switch (TypeKind) {
      case int32:
      case float32:
        return 4;
      case float64:
        return 8;
      default:
        assert(0 && "Must not happen");
      }
      return 0;
    }
  };

  // --------------------- Global Variable -----------------------------------

  struct GlobalVar {
    LLVMCodeGen &CG;
    llvm::GlobalVariable &Var;
    GlobalVar(LLVMCodeGen &cg, llvm::GlobalVariable &var)
        : CG(cg), Var(var) {}
  };

  GlobalVar mkGlobalVar(std::string name, Type type) {
    M.getOrInsertGlobal(name, type.get());
    llvm::GlobalVariable *gvar = M.getNamedGlobal(name);
    gvar->setLinkage(llvm::GlobalValue::CommonLinkage);
    gvar->setAlignment(type.size());
    return GlobalVar{*this, *gvar};
  }

  //---------------------- Function -------------------------------------------

  struct Function {
    LLVMCodeGen &CG;
    llvm::Function &F;
    std::vector<llvm::Value*> Args;
    Function(LLVMCodeGen &cg, llvm::Function &f) : CG(cg), F(f) {
      Args.reserve(16);
      for (auto &arg : F.args()) {
        Args.push_back(&arg);
      }
    }
    void verify() const { llvm::verifyFunction(F); }

    llvm::Function *get() { return &F; }

    Value arg(size_t num) {
      assert(num < Args.size());
      return Value{&CG, Args[num]};
    }

    BasicBlock mkBasicBlock(std::string name) {
      llvm::BasicBlock *bb = llvm::BasicBlock::Create(CG.Context, name, &F);
      return {CG, *bb};
    }
  };

  Function mkFunction(std::string name, Type ret_type,
                      std::vector<std::pair<Type, std::string>> args = {}) {
    std::vector<llvm::Type*> args_type;
    args_type.reserve(args.size());
    for (auto arg : args) {
      args_type.push_back(arg.first.get());
    }
    llvm::FunctionType *funcType =
        llvm::FunctionType::get(ret_type.get(), args_type, false);
    llvm::Function *func = llvm::Function::Create(
        funcType, llvm::Function::ExternalLinkage, name, &M);
    int count = 0;
    for (auto &arg : func->args()) {
      arg.setName(args[count++].second);
    }
    return Function{*this, *func};
  }

  // --------------------------- Phi node ------------------------------------
  struct Phi {
    LLVMCodeGen *CG;
    llvm::Value *V;

    Phi(LLVMCodeGen *cg, llvm::Value *v) : CG(cg), V(v) {}
    llvm::Value *get() { return V; }

    operator Value() { return {CG, V}; }
  };

  Phi mkPhi(Type type, std::vector<std::pair<Value, BasicBlock>> edge_list) {
    llvm::PHINode *phi = Builder.CreatePHI(type.get(), edge_list.size());
    for (auto &edge : edge_list) {
      phi->addIncoming(edge.first.get(), edge.second.get());
    }
    return {this, phi};
  }

  // ------------- type & vals

  Type mkIntTy() { return Type(*this, Type::int32); }
  Value mkInt(int val) { return {this, Builder.getInt32(val)}; }
  Type mkFloatTy() { return Type(*this, Type::float32); }
  Type mkDoubleTy() { return Type(*this, Type::float64); }

  //-------------------- Ops

  void mkRet(Value val) { Builder.CreateRet(val.get()); }
};

static llvm::LLVMContext &ContextRef = llvm::getGlobalContext();
static llvm::Module *ModuleOb = new llvm::Module("my compiler", ContextRef);

int main(int argc, char *argv[]) {
  static llvm::IRBuilder<> BuilderObj(ContextRef);
  LLVMCodeGen cg(ContextRef, *ModuleOb, BuilderObj);

  auto gvar = cg.mkGlobalVar("x", cg.mkFloatTy());

  auto f = cg.mkFunction("foo", cg.mkIntTy(),
                         {{cg.mkIntTy(), "a"}, {cg.mkFloatTy(), "b"}});

  auto thenBB = f.mkBasicBlock("then");
  thenBB.set();
  auto then_val = f.arg(0) + cg.mkInt(1);

  auto elseBB = f.mkBasicBlock("else");
  elseBB.set();
  auto else_val = f.arg(1) + cg.mkInt(2);

  auto mergeBB = f.mkBasicBlock("cont");
  mergeBB.set();
  auto phi = cg.mkPhi(cg.mkIntTy(), {{then_val, thenBB}, {else_val, elseBB}});
  cg.mkRet(phi);

  auto entry = f.mkBasicBlock("entry");
  entry.set();
  auto val = cg.mkInt(100);
  auto cmp = f.arg(0) < val;
  auto cnd = cmp != cg.mkInt(0);
  cnd.mkIfThenElse(thenBB, elseBB, mergeBB);

  f.verify();

  cg.dump();
  return 0;
}
<commit_msg>add mkAndSetBasicBlock<commit_after>#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/Verifier.h>
#include <vector>
#include <string>

struct LLVMCodeGen {
  using IRBuilder = llvm::IRBuilder<>;
  llvm::LLVMContext &Context;
  llvm::Module &M;
  IRBuilder &Builder;

  LLVMCodeGen(llvm::LLVMContext &context, llvm::Module &m,
              llvm::IRBuilder<> &builder)
      : Context(context), M(m), Builder(builder) {}
  void dump() const { M.dump(); }

  //------------------- Basic Block -------------------------------------------

  struct BasicBlock {
    LLVMCodeGen &CG;
    llvm::BasicBlock &BB;
    BasicBlock(LLVMCodeGen &cg, llvm::BasicBlock &bb) : CG(cg), BB(bb) {}
    llvm::BasicBlock *get() { return &BB; }
    void set() { CG.Builder.SetInsertPoint(&BB); }

  };

  // -----------------------  Boolean -------------------------------------------

  struct Boolean {
    LLVMCodeGen *CG;
    llvm::Value *V;

    Boolean(LLVMCodeGen *cg, llvm::Value *value) : CG(cg), V(value) {}

    llvm::Value *get() { return V; }

    void mkIfThenElse(BasicBlock thenBB, BasicBlock elseBB,
                      BasicBlock mergeBB) {
      CG->Builder.CreateCondBr(V, thenBB.get(), elseBB.get());
      CG->Builder.SetInsertPoint(thenBB.get());
      CG->Builder.CreateBr(mergeBB.get());
      CG->Builder.SetInsertPoint(elseBB.get());
      CG->Builder.CreateBr(mergeBB.get());
    }
  };

  // -----------------------  Value -------------------------------------------
  struct Value {
    LLVMCodeGen *CG;
    llvm::Value *V;

    Value(LLVMCodeGen *cg, llvm::Value *value) : CG(cg), V(value) {}

    llvm::Value *get() { return V; }

    Value operator*(Value R) {
      return {CG, CG->Builder.CreateMul(V, R.get(), "mul")};
    }
    Value operator+(Value R) {
      return {CG, CG->Builder.CreateAdd(V, R.get(), "add")};
    }

    Value operator<(Value R) {
      return {CG, CG->Builder.CreateICmpULT(V, R.get(), "lt")};
    }
    Boolean operator!=(Value R) {
      return {CG, CG->Builder.CreateICmpNE(V, R.get(), "ne")};
    }
  };

  //----------------------- Type ---------------------------------------------

  struct Type {
    enum Kind { int32, float32, float64 };

    LLVMCodeGen &CG;
    Kind TypeKind;

    Type(LLVMCodeGen &cg, Kind type_kind) : CG(cg), TypeKind(type_kind) {}

    llvm::Type *get() {
      switch (TypeKind) {
      case int32:
        return CG.Builder.getInt32Ty();
      case float32:
        return CG.Builder.getFloatTy();
      case float64:
        return CG.Builder.getDoubleTy();
      default:
        assert(0 && "Must not happen");
      }
    }

    size_t size() {
      switch (TypeKind) {
      case int32:
      case float32:
        return 4;
      case float64:
        return 8;
      default:
        assert(0 && "Must not happen");
      }
      return 0;
    }
  };

  // --------------------- Global Variable -----------------------------------

  struct GlobalVar {
    LLVMCodeGen &CG;
    llvm::GlobalVariable &Var;
    GlobalVar(LLVMCodeGen &cg, llvm::GlobalVariable &var)
        : CG(cg), Var(var) {}
  };

  GlobalVar mkGlobalVar(std::string name, Type type) {
    M.getOrInsertGlobal(name, type.get());
    llvm::GlobalVariable *gvar = M.getNamedGlobal(name);
    gvar->setLinkage(llvm::GlobalValue::CommonLinkage);
    gvar->setAlignment(type.size());
    return GlobalVar{*this, *gvar};
  }

  //---------------------- Function -------------------------------------------

  struct Function {
    LLVMCodeGen &CG;
    llvm::Function &F;
    std::vector<llvm::Value*> Args;
    Function(LLVMCodeGen &cg, llvm::Function &f) : CG(cg), F(f) {
      Args.reserve(16);
      for (auto &arg : F.args()) {
        Args.push_back(&arg);
      }
    }
    void verify() const { llvm::verifyFunction(F); }

    llvm::Function *get() { return &F; }

    Value arg(size_t num) {
      assert(num < Args.size());
      return Value{&CG, Args[num]};
    }

    BasicBlock mkBasicBlock(std::string name) {
      llvm::BasicBlock *bb = llvm::BasicBlock::Create(CG.Context, name, &F);
      return {CG, *bb};
    }
    BasicBlock mkAndSetBasicBlock(std::string name) {
      auto bb = mkBasicBlock(std::move(name));
      bb.set();
      return bb;
    }
  };

  Function mkFunction(std::string name, Type ret_type,
                      std::vector<std::pair<Type, std::string>> args = {}) {
    std::vector<llvm::Type*> args_type;
    args_type.reserve(args.size());
    for (auto arg : args) {
      args_type.push_back(arg.first.get());
    }
    llvm::FunctionType *funcType =
        llvm::FunctionType::get(ret_type.get(), args_type, false);
    llvm::Function *func = llvm::Function::Create(
        funcType, llvm::Function::ExternalLinkage, name, &M);
    int count = 0;
    for (auto &arg : func->args()) {
      arg.setName(args[count++].second);
    }
    return Function{*this, *func};
  }

  // --------------------------- Phi node ------------------------------------
  struct Phi {
    LLVMCodeGen *CG;
    llvm::Value *V;

    Phi(LLVMCodeGen *cg, llvm::Value *v) : CG(cg), V(v) {}
    llvm::Value *get() { return V; }

    operator Value() { return {CG, V}; }
  };

  Phi mkPhi(Type type, std::vector<std::pair<Value, BasicBlock>> edge_list) {
    llvm::PHINode *phi = Builder.CreatePHI(type.get(), edge_list.size());
    for (auto &edge : edge_list) {
      phi->addIncoming(edge.first.get(), edge.second.get());
    }
    return {this, phi};
  }

  // ------------- type & vals

  Type mkIntTy() { return Type(*this, Type::int32); }
  Value mkInt(int val) { return {this, Builder.getInt32(val)}; }
  Type mkFloatTy() { return Type(*this, Type::float32); }
  Type mkDoubleTy() { return Type(*this, Type::float64); }

  //-------------------- Ops

  void mkRet(Value val) { Builder.CreateRet(val.get()); }
};

static llvm::LLVMContext &ContextRef = llvm::getGlobalContext();
static llvm::Module *ModuleOb = new llvm::Module("my compiler", ContextRef);

int main(int argc, char *argv[]) {
  static llvm::IRBuilder<> BuilderObj(ContextRef);
  LLVMCodeGen cg(ContextRef, *ModuleOb, BuilderObj);

  auto gvar = cg.mkGlobalVar("x", cg.mkFloatTy());

  auto f = cg.mkFunction("foo", cg.mkIntTy(),
                         {{cg.mkIntTy(), "a"}, {cg.mkFloatTy(), "b"}});

  auto thenBB = f.mkAndSetBasicBlock("then");
  auto then_val = f.arg(0) + cg.mkInt(1);

  auto elseBB = f.mkAndSetBasicBlock("else");
  auto else_val = f.arg(1) + cg.mkInt(2);

  auto mergeBB = f.mkAndSetBasicBlock("cont");
  auto phi = cg.mkPhi(cg.mkIntTy(), {{then_val, thenBB}, {else_val, elseBB}});
  cg.mkRet(phi);

  auto entry = f.mkAndSetBasicBlock("entry");
  auto val = cg.mkInt(100);
  auto cmp = f.arg(0) < val;
  auto cnd = cmp != cg.mkInt(0);
  cnd.mkIfThenElse(thenBB, elseBB, mergeBB);

  f.verify();

  cg.dump();
  return 0;
}
<|endoftext|>


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2020.  All rights reserved
 */
#include "../StroikaPreComp.h"

#include <algorithm>
#include <cstdlib>

#include "../../Foundation/Characters/ToString.h"
#include "../../Foundation/Containers/Common.h"
#include "../../Foundation/DataExchange/BadFormatException.h"
#include "../../Foundation/Debug/Assertions.h"
#include "../../Foundation/Debug/Trace.h"
#include "../../Foundation/Execution/Sleep.h"
#include "../../Foundation/Execution/Throw.h"
#include "../../Foundation/IO/Network/HTTP/Exception.h"
#include "../../Foundation/IO/Network/HTTP/Headers.h"
#include "../../Foundation/IO/Network/HTTP/Methods.h"
#include "../../Foundation/Memory/SmallStackBuffer.h"

#include "DefaultFaultInterceptor.h"

#include "ConnectionManager.h"

using namespace Stroika::Foundation;
using namespace Stroika::Foundation::Characters;
using namespace Stroika::Foundation::Containers;
using namespace Stroika::Foundation::Memory;

using namespace Stroika::Frameworks;
using namespace Stroika::Foundation::IO::Network;
using namespace Stroika::Frameworks::WebServer;

using Execution::Thread;

// Comment this in to turn on aggressive noisy DbgTrace in this module
//#define USE_NOISY_TRACE_IN_THIS_MODULE_ 1

namespace {
    struct ServerHeadersInterceptor_ : public Interceptor {
        struct Rep_ : Interceptor::_IRep {
            Rep_ (const optional<String>& serverHeader, ConnectionManager::CORSModeSupport corsSupportMode)
                : fServerHeader_ (serverHeader)
                , fCORSModeSupport (corsSupportMode)
            {
            }
            virtual void HandleFault ([[maybe_unused]] Message* m, [[maybe_unused]] const exception_ptr& e) noexcept override
            {
            }
            virtual void HandleMessage (Message* m) override
            {
                Response& response = *m->PeekResponse ();
                if (fServerHeader_) {
                    response.AddHeader (IO::Network::HTTP::HeaderName::kServer, *fServerHeader_);
                }
                if (fCORSModeSupport == ConnectionManager::CORSModeSupport::eSuppress) {
                    /*
                     *  From what I gather from https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS - the Allow-Origin is needed
                     *  on all responses, but these others just when you do a preflight - using OPTIONS.
                     */
                    response.AddHeader (IO::Network::HTTP::HeaderName::kAccessControlAllowOrigin, L"*"sv);
                    if (m->PeekRequest ()->GetHTTPMethod () == IO::Network::HTTP::Methods::kOptions) {
                        response.AddHeader (IO::Network::HTTP::HeaderName::kAccessControlAllowCredentials, L"true"sv);
                        response.AddHeader (IO::Network::HTTP::HeaderName::kAccessControlAllowHeaders, L"Accept, Access-Control-Allow-Origin, Authorization, Cache-Control, Content-Type, Connection, Pragma, X-Requested-With"sv);
                        response.AddHeader (IO::Network::HTTP::HeaderName::kAccessControlAllowMethods, L"DELETE, GET, OPTIONS, POST, PUT, TRACE, UPDATE"sv);
                        response.AddHeader (IO::Network::HTTP::HeaderName::kAccessControlMaxAge, L"86400"sv);
                    }
                }
            }
            const optional<String>                   fServerHeader_; // no need for synchronization cuz constant - just set on construction
            const ConnectionManager::CORSModeSupport fCORSModeSupport;
        };
        ServerHeadersInterceptor_ (const optional<String>& serverHeader, ConnectionManager::CORSModeSupport corsSupportMode)
            : Interceptor (make_shared<Rep_> (serverHeader, corsSupportMode))
        {
        }
    };
}

namespace {
    Sequence<Interceptor> mkEarlyInterceptors_ (const optional<Interceptor>& defaultFaultHandler, const Interceptor& serverEtcInterceptor)
    {
        Sequence<Interceptor> interceptors;
        interceptors += serverEtcInterceptor;
        if (defaultFaultHandler) {
            interceptors += *defaultFaultHandler;
        }
        return interceptors;
    }
    InterceptorChain mkInterceptorChain_ (const Router& router, const Sequence<Interceptor>& earlyInterceptors, const Sequence<Interceptor>& beforeInterceptors, const Sequence<Interceptor>& afterInterceptors)
    {
        Sequence<Interceptor> interceptors;
        interceptors += earlyInterceptors;
        interceptors += beforeInterceptors;
        interceptors += router;
        interceptors += afterInterceptors;
        return InterceptorChain{interceptors};
    }
}

/*
 ********************************************************************************
 ***************** WebServer::ConnectionManager::Options ************************
 ********************************************************************************
 */
constexpr unsigned int                       ConnectionManager::Options::kDefault_MaxConnections;
constexpr Socket::BindFlags                  ConnectionManager::Options::kDefault_BindFlags;
const optional<String>                       ConnectionManager::Options::kDefault_ServerHeader = L"Stroika/2.0"sv;
constexpr ConnectionManager::CORSModeSupport ConnectionManager::Options::kDefault_CORSModeSupport;
constexpr Time::DurationSecondsType          ConnectionManager::Options::kDefault_AutomaticTCPDisconnectOnClose;
constexpr optional<int>                      ConnectionManager::Options::kDefault_Linger;

/*
 ********************************************************************************
 ************************* WebServer::ConnectionManager *************************
 ********************************************************************************
 */
const ConnectionManager::Options ConnectionManager::kDefaultOptions;

ConnectionManager::ConnectionManager (const SocketAddress& bindAddress, const Router& router, const Options& options)
    : ConnectionManager (Sequence<SocketAddress>{bindAddress}, router, options)
{
}

namespace {
    inline unsigned int ComputeThreadPoolSize_ (const ConnectionManager::Options& options)
    {
        using Options = ConnectionManager::Options;
        constexpr unsigned int kMinThreadCnt_{1u}; // one enough now that we support separate thread doing epoll/select and one read when data avail
        return Math::AtLeast (kMinThreadCnt_, options.fMaxConnections.value_or (Options::kDefault_MaxConnections) / 10);
    }
    inline unsigned int ComputeConnectionBacklog_ (const ConnectionManager::Options& options)
    {
        using Options = ConnectionManager::Options;
        constexpr unsigned int kMinDefaultTCPBacklog_{3u};
        return options.fTCPBacklog.value_or (Math::AtLeast (kMinDefaultTCPBacklog_, options.fMaxConnections.value_or (Options::kDefault_MaxConnections) * 3 / 4));
    }
}

ConnectionManager::ConnectionManager (const Traversal::Iterable<SocketAddress>& bindAddresses, const Router& router, const Options& options)
    : fServerHeader_ (Memory::OptionalValue (options.fServerHeader, Options::kDefault_ServerHeader))
    , fCORSModeSupport_ (options.fCORSModeSupport.value_or (Options::kDefault_CORSModeSupport))
    , fServerAndCORSEtcInterceptor_{ServerHeadersInterceptor_{fServerHeader_, fCORSModeSupport_}}
    , fDefaultErrorHandler_ (DefaultFaultInterceptor{})
    , fEarlyInterceptors_{mkEarlyInterceptors_ (fDefaultErrorHandler_, fServerAndCORSEtcInterceptor_)}
    , fBeforeInterceptors_{}
    , fAfterInterceptors_{}
    , fLinger_ (Memory::OptionalValue (options.fLinger, Options::kDefault_Linger))
    , fAutomaticTCPDisconnectOnClose_ (options.fAutomaticTCPDisconnectOnClose.value_or (Options::kDefault_AutomaticTCPDisconnectOnClose))
    , fRouter_ (router)
    , fInterceptorChain_{mkInterceptorChain_ (fRouter_, fEarlyInterceptors_, fBeforeInterceptors_, fAfterInterceptors_)}
    , fActiveConnectionThreads_{ComputeThreadPoolSize_ (options), options.fThreadPoolName} // implementation detail - due to EXPENSIVE blcoking read strategy - see https://stroika.atlassian.net/browse/STK-638
    , fListener_{bindAddresses,
                 options.fBindFlags.value_or (Options::kDefault_BindFlags),
                 [this] (const ConnectionOrientedStreamSocket::Ptr& s) { onConnect_ (s); },
                 ComputeConnectionBacklog_ (options)}
    , fWaitForReadyConnectionThread_{Execution::Thread::CleanupPtr::eAbortBeforeWaiting, Thread::New ([this] () { WaitForReadyConnectionLoop_ (); }, L"ConnectionMgr-Wait4IOReady"_k)}
{
    fWaitForReadyConnectionThread_.Start (); // start here instead of autostart so a guaranteed initialized before thead main starts - see https://stroika.atlassian.net/browse/STK-706
}

void ConnectionManager::onConnect_ (const ConnectionOrientedStreamSocket::Ptr& s)
{
#if USE_NOISY_TRACE_IN_THIS_MODULE_
    Debug::TraceContextBumper ctx (Stroika_Foundation_Debug_OptionalizeTraceArgs (L"ConnectionManager::onConnect_", L"s=%s", Characters::ToString (s).c_str ()));
#endif
    s.SetAutomaticTCPDisconnectOnClose (GetAutomaticTCPDisconnectOnClose ());
    s.SetLinger (GetLinger ()); // 'missing' has meaning (feature disabled) for socket, so allow setting that too - doesn't mean don't pass on/use-default
    shared_ptr<Connection> conn = make_shared<Connection> (s, fInterceptorChain_);
    fInactiveOpenConnections_.rwget ()->Add (conn, s.GetNativeSocket ());
    fWaitForReadyConnectionThread_.Interrupt (); // wakeup so checks this one too
#if USE_NOISY_TRACE_IN_THIS_MODULE_
    DbgTrace (L"In onConnect_ (after adding connection): fActiveConnections_=%s, fInactiveOpenConnections_=%s", Characters::ToString (fActiveConnections_.cget ().cref ()).c_str (), Characters::ToString (fInactiveOpenConnections_.cget ().cref ()).c_str ());
#endif
}

void ConnectionManager::WaitForReadyConnectionLoop_ ()
{
#if USE_NOISY_TRACE_IN_THIS_MODULE_
    Debug::TraceContextBumper ctx (Stroika_Foundation_Debug_OptionalizeTraceArgs (L"ConnectionManager::WaitForReadyConnectionLoop_"));
#endif

    // run til thread aboorted
    while (true) {
        try {
            Execution::CheckForThreadInterruption ();

            Bijection<shared_ptr<Connection>, Execution::WaitForIOReady<>::SDKPollableType> seeIfReady = fInactiveOpenConnections_.cget ().cref ();

#if USE_NOISY_TRACE_IN_THIS_MODULE_
            DbgTrace (L"At top of WaitForReadyConnectionLoop_: fActiveConnections_=%s, fInactiveOpenConnections_=%s", Characters::ToString (fActiveConnections_.cget ().cref ()).c_str (), Characters::ToString (fInactiveOpenConnections_.cget ().cref ()).c_str ());
#endif

            // This will wake up early if another thread adds an inactive connection (thread gets interruppted)
            if (seeIfReady.Image ().empty ()) {
                Execution::Sleep (2h); // can be any amount of time, since we will be interuppted
                continue;
            }
            Execution::WaitForIOReady sockSetPoller{seeIfReady.Image ()};
            for (auto readyFD : sockSetPoller.WaitQuietly ()) {
                shared_ptr<Connection> conn = *seeIfReady.InverseLookup (readyFD);

                Execution::Thread::SuppressInterruptionInContext suppressInterruption;

                // @todo these three steps SB atomic/and transactional
                fInactiveOpenConnections_.rwget ().rwref ().RemoveDomainElement (conn);
                fActiveConnections_.rwget ().rwref ().Add (conn);
                fActiveConnectionThreads_.AddTask (
                    [this, conn] () mutable {
#if USE_NOISY_TRACE_IN_THIS_MODULE_
                        Debug::TraceContextBumper ctx (Stroika_Foundation_Debug_OptionalizeTraceArgs (L"ConnectionManager::...processConnectionLoop"));
#endif
                        bool keepAlive = (conn->ReadAndProcessMessage () == Connection::eTryAgainLater);

                        // no matter what, remove from active connecitons
                        fActiveConnections_.rwget ().rwref ().Remove (conn);

                        if (keepAlive) {
                            fInactiveOpenConnections_.rwget ().rwref ().Add (conn, conn->GetSocket ().GetNativeSocket ());
                            fWaitForReadyConnectionThread_.Interrupt (); // wakeup so checks this one too
                        }
                        else {
                            if (conn->GetResponse ().GetState () != Response::State::eCompleted) {
                                IgnoreExceptionsForCall (conn->GetResponse ().End ());
                            }
                        }
#if USE_NOISY_TRACE_IN_THIS_MODULE_
                        DbgTrace (L"at end of read&process task (keepAlive=%s) for connection %s: fActiveConnections_=%s, fInactiveOpenConnections_=%s", Characters::ToString (keepAlive).c_str (), Characters::ToString (conn).c_str (), Characters::ToString (fActiveConnections_.cget ().cref ()).c_str (), Characters::ToString (fInactiveOpenConnections_.cget ().cref ()).c_str ());
#endif
                    });
            }
        }
        catch (Thread::AbortException&) {
            Execution::ReThrow ();
        }
        catch (Thread::InterruptException&) {
#if USE_NOISY_TRACE_IN_THIS_MODULE_
            DbgTrace (L"Normal receipt of interruption because connection added to open but inactive connections.");
#endif
            // OK - completely ignore - this just means the list of sockets to watch has changed
        }
        catch (...) {
            DbgTrace (L"Internal exception in WaitForReadyConnectionLoop_ loop suppressed: %s", Characters::ToString (current_exception ()).c_str ());
        }
    }
}

void ConnectionManager::FixupInterceptorChain_ ()
{
    fInterceptorChain_ = InterceptorChain{mkInterceptorChain_ (fRouter_, fEarlyInterceptors_, fBeforeInterceptors_, fAfterInterceptors_)};
}

void ConnectionManager::ReplaceInEarlyInterceptor_ (const optional<Interceptor>& oldValue, const optional<Interceptor>& newValue)
{
    // replace old error handler in the interceptor chain, in the same spot if possible, and otherwise append
    auto                  rwLock = this->fEarlyInterceptors_.rwget ();
    Sequence<Interceptor> newInterceptors;
    bool                  addedDefault = false;
    for (Interceptor i : rwLock.load ()) {
        if (oldValue == i) {
            if (newValue) {
                newInterceptors += *newValue;
            }
            addedDefault = true;
        }
        else {
            newInterceptors += i;
        }
    }
    if (newValue and not addedDefault) {
        newInterceptors += *newValue;
    }
    rwLock.store (newInterceptors);
    FixupInterceptorChain_ ();
}

void ConnectionManager::AbortConnection (const shared_ptr<Connection>& /*conn*/)
{
    AssertNotImplemented ();
}

Collection<shared_ptr<Connection>> ConnectionManager::GetConnections () const
{
    return Collection<shared_ptr<Connection>>{fInactiveOpenConnections_.cget ().cref ().Preimage ()} + fActiveConnections_.cget ().load ();
}

void ConnectionManager::SetServerHeader (optional<String> server)
{
    if (fServerHeader_ != server) {
        Interceptor old               = fServerAndCORSEtcInterceptor_;
        fServerAndCORSEtcInterceptor_ = ServerHeadersInterceptor_{server, fCORSModeSupport_};
        fServerHeader_                = server;
        ReplaceInEarlyInterceptor_ (old, fServerAndCORSEtcInterceptor_);
    }
}

void ConnectionManager::SetCORSModeSupport (CORSModeSupport support)
{
    if (fCORSModeSupport_ != support) {
        Interceptor old               = fServerAndCORSEtcInterceptor_;
        fServerAndCORSEtcInterceptor_ = ServerHeadersInterceptor_{fServerHeader_, fCORSModeSupport_};
        fCORSModeSupport_             = support;
        ReplaceInEarlyInterceptor_ (old, fServerAndCORSEtcInterceptor_);
    }
}

void ConnectionManager::SetDefaultErrorHandler (const optional<Interceptor>& defaultErrorHandler)
{
    if (fDefaultErrorHandler_ != defaultErrorHandler) {
        ReplaceInEarlyInterceptor_ (fDefaultErrorHandler_.load (), defaultErrorHandler);
        fDefaultErrorHandler_ = defaultErrorHandler;
    }
}

void ConnectionManager::SetEarlyInterceptors (const Sequence<Interceptor>& earlyInterceptors)
{
    fEarlyInterceptors_ = earlyInterceptors;
    FixupInterceptorChain_ ();
}

void ConnectionManager::SetBeforeInterceptors (const Sequence<Interceptor>& beforeInterceptors)
{
    fBeforeInterceptors_ = beforeInterceptors;
    FixupInterceptorChain_ ();
}

void ConnectionManager::SetAfterInterceptors (const Sequence<Interceptor>& afterInterceptors)
{
    fAfterInterceptors_ = afterInterceptors;
    FixupInterceptorChain_ ();
}

void ConnectionManager::AddInterceptor (const Interceptor& i, InterceptorAddRelativeTo relativeTo)
{
    switch (relativeTo) {
        case ePrependsToEarly:
            fEarlyInterceptors_.rwget ()->Prepend (i);
            break;
        case ePrepend:
            fBeforeInterceptors_.rwget ()->Prepend (i);
            break;
        case eAppend:
            fAfterInterceptors_.rwget ()->Append (i);
            break;
        case eAfterBeforeInterceptors:
            fBeforeInterceptors_.rwget ()->Append (i);
            break;
    }
    FixupInterceptorChain_ ();
}

void ConnectionManager::RemoveInterceptor (const Interceptor& i)
{
    bool found = false;
    {
        auto b4 = fBeforeInterceptors_.rwget ();
        if (optional<size_t> idx = b4->IndexOf (i)) {
            b4->Remove (*idx);
            found = true;
        }
    }
    if (not found) {
        auto after = fAfterInterceptors_.rwget ();
        if (optional<size_t> idx = after->IndexOf (i)) {
            after->Remove (*idx);
            found = true;
        }
    }
    Require (found);
    FixupInterceptorChain_ ();
}
<commit_msg>cosmetic<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2020.  All rights reserved
 */
#include "../StroikaPreComp.h"

#include <algorithm>
#include <cstdlib>

#include "../../Foundation/Characters/ToString.h"
#include "../../Foundation/Containers/Common.h"
#include "../../Foundation/DataExchange/BadFormatException.h"
#include "../../Foundation/Debug/Assertions.h"
#include "../../Foundation/Debug/Trace.h"
#include "../../Foundation/Execution/Sleep.h"
#include "../../Foundation/Execution/Throw.h"
#include "../../Foundation/IO/Network/HTTP/Exception.h"
#include "../../Foundation/IO/Network/HTTP/Headers.h"
#include "../../Foundation/IO/Network/HTTP/Methods.h"
#include "../../Foundation/Memory/SmallStackBuffer.h"

#include "DefaultFaultInterceptor.h"

#include "ConnectionManager.h"

using namespace Stroika::Foundation;
using namespace Stroika::Foundation::Characters;
using namespace Stroika::Foundation::Containers;
using namespace Stroika::Foundation::Memory;

using namespace Stroika::Frameworks;
using namespace Stroika::Foundation::IO::Network;
using namespace Stroika::Frameworks::WebServer;

using Execution::Thread;

// Comment this in to turn on aggressive noisy DbgTrace in this module
//#define USE_NOISY_TRACE_IN_THIS_MODULE_ 1

namespace {
    struct ServerHeadersInterceptor_ : public Interceptor {
        struct Rep_ : Interceptor::_IRep {
            Rep_ (const optional<String>& serverHeader, ConnectionManager::CORSModeSupport corsSupportMode)
                : fServerHeader_ (serverHeader)
                , fCORSModeSupport (corsSupportMode)
            {
            }
            virtual void HandleFault ([[maybe_unused]] Message* m, [[maybe_unused]] const exception_ptr& e) noexcept override
            {
            }
            virtual void HandleMessage (Message* m) override
            {
                Response& response = *m->PeekResponse ();
                if (fServerHeader_) {
                    response.AddHeader (IO::Network::HTTP::HeaderName::kServer, *fServerHeader_);
                }
                if (fCORSModeSupport == ConnectionManager::CORSModeSupport::eSuppress) {
                    /*
                     *  From what I gather from https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS - the Allow-Origin is needed
                     *  on all responses, but these others just when you do a preflight - using OPTIONS.
                     */
                    response.AddHeader (IO::Network::HTTP::HeaderName::kAccessControlAllowOrigin, L"*"sv);
                    if (m->PeekRequest ()->GetHTTPMethod () == IO::Network::HTTP::Methods::kOptions) {
                        response.AddHeader (IO::Network::HTTP::HeaderName::kAccessControlAllowCredentials, L"true"sv);
                        response.AddHeader (IO::Network::HTTP::HeaderName::kAccessControlAllowHeaders, L"Accept, Access-Control-Allow-Origin, Authorization, Cache-Control, Content-Type, Connection, Pragma, X-Requested-With"sv);
                        response.AddHeader (IO::Network::HTTP::HeaderName::kAccessControlAllowMethods, L"DELETE, GET, OPTIONS, POST, PUT, TRACE, UPDATE"sv);
                        response.AddHeader (IO::Network::HTTP::HeaderName::kAccessControlMaxAge, L"86400"sv);
                    }
                }
            }
            const optional<String>                   fServerHeader_; // no need for synchronization cuz constant - just set on construction
            const ConnectionManager::CORSModeSupport fCORSModeSupport;
        };
        ServerHeadersInterceptor_ (const optional<String>& serverHeader, ConnectionManager::CORSModeSupport corsSupportMode)
            : Interceptor (make_shared<Rep_> (serverHeader, corsSupportMode))
        {
        }
    };
}

namespace {
    Sequence<Interceptor> mkEarlyInterceptors_ (const optional<Interceptor>& defaultFaultHandler, const Interceptor& serverEtcInterceptor)
    {
        Sequence<Interceptor> interceptors;
        interceptors += serverEtcInterceptor;
        if (defaultFaultHandler) {
            interceptors += *defaultFaultHandler;
        }
        return interceptors;
    }
    InterceptorChain mkInterceptorChain_ (const Router& router, const Sequence<Interceptor>& earlyInterceptors, const Sequence<Interceptor>& beforeInterceptors, const Sequence<Interceptor>& afterInterceptors)
    {
        Sequence<Interceptor> interceptors;
        interceptors += earlyInterceptors;
        interceptors += beforeInterceptors;
        interceptors += router;
        interceptors += afterInterceptors;
        return InterceptorChain{interceptors};
    }
}

/*
 ********************************************************************************
 ***************** WebServer::ConnectionManager::Options ************************
 ********************************************************************************
 */
constexpr unsigned int                       ConnectionManager::Options::kDefault_MaxConnections;
constexpr Socket::BindFlags                  ConnectionManager::Options::kDefault_BindFlags;
const optional<String>                       ConnectionManager::Options::kDefault_ServerHeader = L"Stroika/2.0"sv;
constexpr ConnectionManager::CORSModeSupport ConnectionManager::Options::kDefault_CORSModeSupport;
constexpr Time::DurationSecondsType          ConnectionManager::Options::kDefault_AutomaticTCPDisconnectOnClose;
constexpr optional<int>                      ConnectionManager::Options::kDefault_Linger;

/*
 ********************************************************************************
 ************************* WebServer::ConnectionManager *************************
 ********************************************************************************
 */
const ConnectionManager::Options ConnectionManager::kDefaultOptions;

ConnectionManager::ConnectionManager (const SocketAddress& bindAddress, const Router& router, const Options& options)
    : ConnectionManager (Sequence<SocketAddress>{bindAddress}, router, options)
{
}

namespace {
    inline unsigned int ComputeThreadPoolSize_ (const ConnectionManager::Options& options)
    {
        using Options = ConnectionManager::Options;
        constexpr unsigned int kMinThreadCnt_{1u}; // one enough now that we support separate thread doing epoll/select and one read when data avail
        return Math::AtLeast (kMinThreadCnt_, options.fMaxConnections.value_or (Options::kDefault_MaxConnections) / 10);
    }
    inline unsigned int ComputeConnectionBacklog_ (const ConnectionManager::Options& options)
    {
        using Options = ConnectionManager::Options;
        constexpr unsigned int kMinDefaultTCPBacklog_{3u};
        return options.fTCPBacklog.value_or (Math::AtLeast (kMinDefaultTCPBacklog_, options.fMaxConnections.value_or (Options::kDefault_MaxConnections) * 3 / 4));
    }
}

ConnectionManager::ConnectionManager (const Traversal::Iterable<SocketAddress>& bindAddresses, const Router& router, const Options& options)
    : fServerHeader_ (Memory::OptionalValue (options.fServerHeader, Options::kDefault_ServerHeader))
    , fCORSModeSupport_ (options.fCORSModeSupport.value_or (Options::kDefault_CORSModeSupport))
    , fServerAndCORSEtcInterceptor_{ServerHeadersInterceptor_{fServerHeader_, fCORSModeSupport_}}
    , fDefaultErrorHandler_ (DefaultFaultInterceptor{})
    , fEarlyInterceptors_{mkEarlyInterceptors_ (fDefaultErrorHandler_, fServerAndCORSEtcInterceptor_)}
    , fBeforeInterceptors_{}
    , fAfterInterceptors_{}
    , fLinger_ (Memory::OptionalValue (options.fLinger, Options::kDefault_Linger))
    , fAutomaticTCPDisconnectOnClose_ (options.fAutomaticTCPDisconnectOnClose.value_or (Options::kDefault_AutomaticTCPDisconnectOnClose))
    , fRouter_ (router)
    , fInterceptorChain_{mkInterceptorChain_ (fRouter_, fEarlyInterceptors_, fBeforeInterceptors_, fAfterInterceptors_)}
    , fActiveConnectionThreads_{ComputeThreadPoolSize_ (options), options.fThreadPoolName} // implementation detail - due to EXPENSIVE blcoking read strategy - see https://stroika.atlassian.net/browse/STK-638
    , fListener_{bindAddresses,
                 options.fBindFlags.value_or (Options::kDefault_BindFlags),
                 [this] (const ConnectionOrientedStreamSocket::Ptr& s) { onConnect_ (s); },
                 ComputeConnectionBacklog_ (options)}
    , fWaitForReadyConnectionThread_{Execution::Thread::CleanupPtr::eAbortBeforeWaiting, Thread::New ([this] () { WaitForReadyConnectionLoop_ (); }, L"WebServer-ConnectionMgr-Wait4IOReady"_k)}
{
    fWaitForReadyConnectionThread_.Start (); // start here instead of autostart so a guaranteed initialized before thead main starts - see https://stroika.atlassian.net/browse/STK-706
}

void ConnectionManager::onConnect_ (const ConnectionOrientedStreamSocket::Ptr& s)
{
#if USE_NOISY_TRACE_IN_THIS_MODULE_
    Debug::TraceContextBumper ctx (Stroika_Foundation_Debug_OptionalizeTraceArgs (L"ConnectionManager::onConnect_", L"s=%s", Characters::ToString (s).c_str ()));
#endif
    s.SetAutomaticTCPDisconnectOnClose (GetAutomaticTCPDisconnectOnClose ());
    s.SetLinger (GetLinger ()); // 'missing' has meaning (feature disabled) for socket, so allow setting that too - doesn't mean don't pass on/use-default
    shared_ptr<Connection> conn = make_shared<Connection> (s, fInterceptorChain_);
    fInactiveOpenConnections_.rwget ()->Add (conn, s.GetNativeSocket ());
    fWaitForReadyConnectionThread_.Interrupt (); // wakeup so checks this one too
#if USE_NOISY_TRACE_IN_THIS_MODULE_
    DbgTrace (L"In onConnect_ (after adding connection): fActiveConnections_=%s, fInactiveOpenConnections_=%s", Characters::ToString (fActiveConnections_.cget ().cref ()).c_str (), Characters::ToString (fInactiveOpenConnections_.cget ().cref ()).c_str ());
#endif
}

void ConnectionManager::WaitForReadyConnectionLoop_ ()
{
#if USE_NOISY_TRACE_IN_THIS_MODULE_
    Debug::TraceContextBumper ctx (Stroika_Foundation_Debug_OptionalizeTraceArgs (L"ConnectionManager::WaitForReadyConnectionLoop_"));
#endif

    // run til thread aboorted
    while (true) {
        try {
            Execution::CheckForThreadInterruption ();

            Bijection<shared_ptr<Connection>, Execution::WaitForIOReady<>::SDKPollableType> seeIfReady = fInactiveOpenConnections_.cget ().cref ();

#if USE_NOISY_TRACE_IN_THIS_MODULE_
            DbgTrace (L"At top of WaitForReadyConnectionLoop_: fActiveConnections_=%s, fInactiveOpenConnections_=%s", Characters::ToString (fActiveConnections_.cget ().cref ()).c_str (), Characters::ToString (fInactiveOpenConnections_.cget ().cref ()).c_str ());
#endif

            // This will wake up early if another thread adds an inactive connection (thread gets interruppted)
            if (seeIfReady.Image ().empty ()) {
                Execution::Sleep (2h); // can be any amount of time, since we will be interuppted
                continue;
            }
            Execution::WaitForIOReady sockSetPoller{seeIfReady.Image ()};
            for (auto readyFD : sockSetPoller.WaitQuietly ()) {
                shared_ptr<Connection> conn = *seeIfReady.InverseLookup (readyFD);

                Execution::Thread::SuppressInterruptionInContext suppressInterruption;

                // @todo these three steps SB atomic/and transactional
                fInactiveOpenConnections_.rwget ().rwref ().RemoveDomainElement (conn);
                fActiveConnections_.rwget ().rwref ().Add (conn);
                fActiveConnectionThreads_.AddTask (
                    [this, conn] () mutable {
#if USE_NOISY_TRACE_IN_THIS_MODULE_
                        Debug::TraceContextBumper ctx (Stroika_Foundation_Debug_OptionalizeTraceArgs (L"ConnectionManager::...processConnectionLoop"));
#endif
                        bool keepAlive = (conn->ReadAndProcessMessage () == Connection::eTryAgainLater);

                        // no matter what, remove from active connecitons
                        fActiveConnections_.rwget ().rwref ().Remove (conn);

                        if (keepAlive) {
                            fInactiveOpenConnections_.rwget ().rwref ().Add (conn, conn->GetSocket ().GetNativeSocket ());
                            fWaitForReadyConnectionThread_.Interrupt (); // wakeup so checks this one too
                        }
                        else {
                            if (conn->GetResponse ().GetState () != Response::State::eCompleted) {
                                IgnoreExceptionsForCall (conn->GetResponse ().End ());
                            }
                        }
#if USE_NOISY_TRACE_IN_THIS_MODULE_
                        DbgTrace (L"at end of read&process task (keepAlive=%s) for connection %s: fActiveConnections_=%s, fInactiveOpenConnections_=%s", Characters::ToString (keepAlive).c_str (), Characters::ToString (conn).c_str (), Characters::ToString (fActiveConnections_.cget ().cref ()).c_str (), Characters::ToString (fInactiveOpenConnections_.cget ().cref ()).c_str ());
#endif
                    });
            }
        }
        catch (Thread::AbortException&) {
            Execution::ReThrow ();
        }
        catch (Thread::InterruptException&) {
#if USE_NOISY_TRACE_IN_THIS_MODULE_
            DbgTrace (L"Normal receipt of interruption because connection added to open but inactive connections.");
#endif
            // OK - completely ignore - this just means the list of sockets to watch has changed
        }
        catch (...) {
            DbgTrace (L"Internal exception in WaitForReadyConnectionLoop_ loop suppressed: %s", Characters::ToString (current_exception ()).c_str ());
        }
    }
}

void ConnectionManager::FixupInterceptorChain_ ()
{
    fInterceptorChain_ = InterceptorChain{mkInterceptorChain_ (fRouter_, fEarlyInterceptors_, fBeforeInterceptors_, fAfterInterceptors_)};
}

void ConnectionManager::ReplaceInEarlyInterceptor_ (const optional<Interceptor>& oldValue, const optional<Interceptor>& newValue)
{
    // replace old error handler in the interceptor chain, in the same spot if possible, and otherwise append
    auto                  rwLock = this->fEarlyInterceptors_.rwget ();
    Sequence<Interceptor> newInterceptors;
    bool                  addedDefault = false;
    for (Interceptor i : rwLock.load ()) {
        if (oldValue == i) {
            if (newValue) {
                newInterceptors += *newValue;
            }
            addedDefault = true;
        }
        else {
            newInterceptors += i;
        }
    }
    if (newValue and not addedDefault) {
        newInterceptors += *newValue;
    }
    rwLock.store (newInterceptors);
    FixupInterceptorChain_ ();
}

void ConnectionManager::AbortConnection (const shared_ptr<Connection>& /*conn*/)
{
    AssertNotImplemented ();
}

Collection<shared_ptr<Connection>> ConnectionManager::GetConnections () const
{
    return Collection<shared_ptr<Connection>>{fInactiveOpenConnections_.cget ().cref ().Preimage ()} + fActiveConnections_.cget ().load ();
}

void ConnectionManager::SetServerHeader (optional<String> server)
{
    if (fServerHeader_ != server) {
        Interceptor old               = fServerAndCORSEtcInterceptor_;
        fServerAndCORSEtcInterceptor_ = ServerHeadersInterceptor_{server, fCORSModeSupport_};
        fServerHeader_                = server;
        ReplaceInEarlyInterceptor_ (old, fServerAndCORSEtcInterceptor_);
    }
}

void ConnectionManager::SetCORSModeSupport (CORSModeSupport support)
{
    if (fCORSModeSupport_ != support) {
        Interceptor old               = fServerAndCORSEtcInterceptor_;
        fServerAndCORSEtcInterceptor_ = ServerHeadersInterceptor_{fServerHeader_, fCORSModeSupport_};
        fCORSModeSupport_             = support;
        ReplaceInEarlyInterceptor_ (old, fServerAndCORSEtcInterceptor_);
    }
}

void ConnectionManager::SetDefaultErrorHandler (const optional<Interceptor>& defaultErrorHandler)
{
    if (fDefaultErrorHandler_ != defaultErrorHandler) {
        ReplaceInEarlyInterceptor_ (fDefaultErrorHandler_.load (), defaultErrorHandler);
        fDefaultErrorHandler_ = defaultErrorHandler;
    }
}

void ConnectionManager::SetEarlyInterceptors (const Sequence<Interceptor>& earlyInterceptors)
{
    fEarlyInterceptors_ = earlyInterceptors;
    FixupInterceptorChain_ ();
}

void ConnectionManager::SetBeforeInterceptors (const Sequence<Interceptor>& beforeInterceptors)
{
    fBeforeInterceptors_ = beforeInterceptors;
    FixupInterceptorChain_ ();
}

void ConnectionManager::SetAfterInterceptors (const Sequence<Interceptor>& afterInterceptors)
{
    fAfterInterceptors_ = afterInterceptors;
    FixupInterceptorChain_ ();
}

void ConnectionManager::AddInterceptor (const Interceptor& i, InterceptorAddRelativeTo relativeTo)
{
    switch (relativeTo) {
        case ePrependsToEarly:
            fEarlyInterceptors_.rwget ()->Prepend (i);
            break;
        case ePrepend:
            fBeforeInterceptors_.rwget ()->Prepend (i);
            break;
        case eAppend:
            fAfterInterceptors_.rwget ()->Append (i);
            break;
        case eAfterBeforeInterceptors:
            fBeforeInterceptors_.rwget ()->Append (i);
            break;
    }
    FixupInterceptorChain_ ();
}

void ConnectionManager::RemoveInterceptor (const Interceptor& i)
{
    bool found = false;
    {
        auto b4 = fBeforeInterceptors_.rwget ();
        if (optional<size_t> idx = b4->IndexOf (i)) {
            b4->Remove (*idx);
            found = true;
        }
    }
    if (not found) {
        auto after = fAfterInterceptors_.rwget ();
        if (optional<size_t> idx = after->IndexOf (i)) {
            after->Remove (*idx);
            found = true;
        }
    }
    Require (found);
    FixupInterceptorChain_ ();
}
<|endoftext|>


<commit_before>//============================================================================
// Name        : UDPReader.cpp
// Author      : Rodolfo Lam
// Version     :
// Copyright   : (C) 2016 Rodolfo Lam. All Rights Reserved.
// Description : Get one DATA-type package and display its contents.
//============================================================================

#include "Graphics/Screen.h"
#include "Network/MessageParser.h"
#include "Util/Math/Vec2D.h"

#include <array>
#include <iostream>
#include <istream>
#include <sstream>
#include <unordered_map>

#include <Poco/ByteOrder.h>
#include <Poco/Exception.h>
#include <Poco/Net/DatagramSocket.h>
#include <Poco/Net/SocketAddress.h>

using Poco::ByteOrder;

bool waitforEscKey();

int main(int argc, char** argv) {
    al_init();
    al_install_keyboard();

    RL::Graphics::Screen screen;

    Poco::Net::SocketAddress sa(Poco::Net::IPAddress(), 49003);
    Poco::Net::DatagramSocket dgs;
    try {
        dgs.bind(sa, false);
    } catch (const Poco::Exception &e) {
        std::cerr
                << "FATAL ERROR: Is the application already running? Port could not be opened. Details: "
                << e.message() << std::endl;
        return EXIT_FAILURE;
    }

    std::array<Poco::UInt8, RL::Network::NETMESSAGE_SIZE> buffer;
    buffer.fill(0);

    std::unordered_map<RL::Network::DataTypesEnum, std::array<float, 8>,
            RL::Network::DataTypesEnumHash> parsedData;
    parsedData.reserve(46);

    for (auto const &ref : RL::Network::Data_Index) {
        std::array<float, 8> ar;
        parsedData.emplace(std::make_pair(ref.second, std::move(ar)));
    }

    RL::Network::MessageParser parser;

    bool exit = false;

    while (exit != true) {
        Poco::Net::SocketAddress sender;
        int n = dgs.receiveFrom(buffer.data(), RL::Network::NETMESSAGE_SIZE,
                sender);

        parser.ParsePacket(parsedData, buffer, n);
        bool success = parser.LastPacketParseCorrect();

        std::stringstream debText;
        debText << "Last Packet time: " << al_get_time() << " | Status: "
                << success << " | Size: " << n;

        screen.processCommands(RL::Math::Vec2D<float>(), debText.str());

        screen.flipScreen(); // Actually, this might be better placed at the end.

        exit = waitforEscKey();
    }

    return EXIT_SUCCESS;

}

bool waitforEscKey() {
    ALLEGRO_KEYBOARD_STATE state;
    al_get_keyboard_state(&state);

    if(&state == nullptr) {
        return false;
    }

    return al_key_down(&state, ALLEGRO_KEY_ESCAPE);
}
<commit_msg>Set UDP socket Non-blocking<commit_after>//============================================================================
// Name        : UDPReader.cpp
// Author      : Rodolfo Lam
// Version     :
// Copyright   : (C) 2016 Rodolfo Lam. All Rights Reserved.
// Description : Get one DATA-type package and display its contents.
//============================================================================

#include "Graphics/Screen.h"
#include "Network/MessageParser.h"
#include "Util/Math/Vec2D.h"

#include <array>
#include <iostream>
#include <istream>
#include <sstream>
#include <unordered_map>

#include <Poco/ByteOrder.h>
#include <Poco/Exception.h>
#include <Poco/Net/DatagramSocket.h>
#include <Poco/Net/SocketAddress.h>

using Poco::ByteOrder;

bool waitforEscKey();

int main(int argc, char** argv) {
    al_init();
    al_install_keyboard();

    RL::Graphics::Screen screen;

    Poco::Net::SocketAddress sa(Poco::Net::IPAddress(), 49003);
    Poco::Net::DatagramSocket dgs;
    dgs.setBlocking(false);
    try {
        dgs.bind(sa, false);
    } catch (const Poco::Exception &e) {
        std::cerr
                << "FATAL ERROR: Is the application already running? Port could not be opened. Details: "
                << e.message() << std::endl;
        return EXIT_FAILURE;
    }

    std::array<Poco::UInt8, RL::Network::NETMESSAGE_SIZE> buffer;
    buffer.fill(0);

    std::unordered_map<RL::Network::DataTypesEnum, std::array<float, 8>,
            RL::Network::DataTypesEnumHash> parsedData;
    parsedData.reserve(46);

    for (auto const &ref : RL::Network::Data_Index) {
        std::array<float, 8> ar;
        parsedData.emplace(std::make_pair(ref.second, std::move(ar)));
    }

    RL::Network::MessageParser parser;

    bool exit = false;

    while (exit != true) {
        Poco::Net::SocketAddress sender;
        int n;

        try {
            n = dgs.receiveFrom(buffer.data(), RL::Network::NETMESSAGE_SIZE,
                    sender);
        } catch (Poco::TimeoutException &e) {
            std::cerr << "[" << al_get_time() << "] " << "Timeout, no data received" << std::endl;
        }

        parser.ParsePacket(parsedData, buffer, n);
        bool success = parser.LastPacketParseCorrect();

        std::stringstream debText;
        debText << "Last Packet time: " << al_get_time() << " | Status: "
                << success << " | Size: " << n;

        screen.processCommands(RL::Math::Vec2D<float>(), debText.str());

        screen.flipScreen(); // Actually, this might be better placed at the end.

        exit = waitforEscKey();
    }

    return EXIT_SUCCESS;

}

bool waitforEscKey() {
    ALLEGRO_KEYBOARD_STATE state;
    al_get_keyboard_state(&state);

    if(&state == nullptr) {
        return false;
    }

    return al_key_down(&state, ALLEGRO_KEY_ESCAPE);
}
<|endoftext|>