<commit_before>#include <cmath>
#include <assert.h>
#include <utility>

#include "macros.h"
#include "libutil/exceptions.h"

#include "libinterpreter/execution_visitor.h"

DEFINE_CASM_UPDATESET_FORK_PAR
DEFINE_CASM_UPDATESET_FORK_SEQ

std::hash<Value> hasher;

void pack_values_in_array(const std::vector<Value> &value_list, uint64_t array[]) {
  for (size_t i=0; i < value_list.size(); i++) {
    array[i] = hasher(value_list[i]);
  }
}

ExecutionVisitor::ExecutionVisitor(ExecutionContext &ctxt, Driver& driver)
    : driver_(driver), context_(ctxt) {
  rule_bindings.push_back(&main_bindings);
}

void ExecutionVisitor::visit_assert(UnaryNode* assert, Value& val) {
  if (val.value.bval != true) {
    driver_.error(assert->location,
                  "Assertion failed");
    throw RuntimeException("Assertion failed");
  }
}

void ExecutionVisitor::visit_update(UpdateNode *update, Value &func_val, Value& expr_v) {
  UNUSED(func_val);

  casm_update* up = (casm_update*) pp_mem_alloc(&(context_.pp_stack), sizeof(casm_update));

  up->value = (void*) expr_v.to_uint64_t();
  up->defined = (expr_v.type == TypeType::UNDEF) ? 0 : 1;
  up->func = update->func->symbol->id;
  up->line = (uint64_t) update;
  pack_values_in_array(value_list, up->args);

  up->num_args = value_list.size();
  value_list.clear();

  auto& function_map = context_.functions[update->func->symbol->id];
  if (function_map.second.count({up->args, up->num_args}) == 0) {
    function_map.second.emplace(ArgumentsKey{up->args, up->num_args}, Value());
  }
  Value& ref = function_map.second[{up->args, up->num_args}];
  casm_update* v = (casm_update*)casm_updateset_add(&(context_.updateset),
                                                    (void*) &ref,
                                                    (void*) up);
  DEBUG("UPADTE "<<update->func->name<<" num args "<<up->num_args << " arg[0] "<<up->args[0]<< " val "<<expr_v.to_str());
  if (v != nullptr) {
    // Check if values match
    for (int i=0; i < up->num_args; i++) {
      if (up->args[i] != v->args[i]) {
        return;
      }
    }

    driver_.error(update->func->location,
                  "update conflict in parallel block for function `"+update->func->name+"`");
    throw RuntimeException("Conflict in updateset");
  }
}

void ExecutionVisitor::visit_call_pre(CallNode *call) { UNUSED(call); }

void ExecutionVisitor::visit_call_pre(CallNode *call, Value& expr) {
  if (expr.type != TypeType::UNDEF) {
    call->rule = expr.value.rule;
  } else {
    throw RuntimeException("Cannot call UNDEF");
  }
}

void ExecutionVisitor::visit_call(CallNode *call, std::vector<Value> &argument_results) {
  UNUSED(call);

  if (call->ruleref) {
    size_t args_defined = call->rule->arguments.size();
    size_t args_provided = argument_results.size();
    if (args_defined != args_provided) {
      driver_.error(call->location, "indirectly called rule `"+call->rule->name+
                    "` expects "+std::to_string(args_defined)+" arguments but "+
                    std::to_string(args_provided)+" where provided");
      throw RuntimeException("Invalid indirect call");
    } else {
      for (size_t i=0; i < args_defined; i++) {
        if (*call->rule->arguments[i] != argument_results[i].type) {
          driver_.error(call->arguments->at(i)->location,
                        "argument "+std::to_string(i+1)+" of indirectly called rule `"+
                        call->rule->name+"` must be `"+
                        call->rule->arguments[i]->to_str()+"` but was `"+
                        argument_results[i].type.to_str()+"`");
          throw RuntimeException("Invalid indirect call");
        }
      }
    }
  }

  rule_bindings.push_back(&argument_results);
}

void ExecutionVisitor::visit_call_post(CallNode *call) {
  UNUSED(call);
  rule_bindings.pop_back();
}

void ExecutionVisitor::visit_print(PrintNode *node, const std::vector<Value> &arguments) {
  for (const Value& v: arguments) {
    std::cout << v.to_str();
  }
  std::cout << std::endl;
}

void ExecutionVisitor::visit_let(LetNode *node, Value& v) {
  rule_bindings.back()->push_back(v);
}

void ExecutionVisitor::visit_let_post(LetNode *node) {
  rule_bindings.back()->pop_back();
}

Value ExecutionVisitor::visit_expression(Expression *expr, Value &left_val, Value &right_val) {
  DEBUG("left "<<left_val.to_str()<<" right "<<right_val.to_str());
  switch (expr->op) {
    case Expression::Operation::ADD: {
      left_val.add(right_val);
      DEBUG("ADD "<<left_val.to_str());
      return left_val;
    }
    case Expression::Operation::SUB: {
      left_val.sub(right_val);
      return left_val;
    }
    case Expression::Operation::MUL: {
      left_val.mul(right_val);
      return left_val;
    }
    case Expression::Operation::DIV: {
      left_val.div(right_val);
      return left_val;
    }
    case Expression::Operation::MOD: {
      left_val.mod(right_val);
      return left_val;
    }
    case Expression::Operation::RAT_DIV: {
      left_val.mod(right_val);
      return left_val;
    }

    case Expression::Operation::EQ: {
      Value tmp(value_eq(left_val, right_val));
      return tmp;
    }
    case Expression::Operation::NEQ: {
      Value tmp(!value_eq(left_val, right_val));
      return tmp;
    }

    case Expression::Operation::LESSER:
      left_val.lesser(right_val);
      return left_val;
    case Expression::Operation::GREATER:
      left_val.greater(right_val);
      return left_val;
    case Expression::Operation::LESSEREQ:
      left_val.lessereq(right_val);
      return left_val;
    case Expression::Operation::GREATEREQ:
      left_val.greatereq(right_val);
      return left_val;

    default: assert(0);
  }
}

Value ExecutionVisitor::visit_expression_single(Expression *expr, Value &val) {
  UNUSED(expr);
  return val;
}

Value casm_pow(std::vector<Value> &expr_results) {
  switch (expr_results[0].type.t) {
    case TypeType::INT:
      return Value((INT_T)std::pow(expr_results[0].value.ival,
                                             expr_results[1].value.ival));
    case TypeType::FLOAT:
      DEBUG("POW ARGS "<<expr_results[0].value.fval<<" foobar "<<expr_results[1].value.fval);
      return Value((FLOAT_T)std::pow(expr_results[0].value.fval,
                                             expr_results[1].value.fval));

    default: assert(0);

  }
}

Value casm_nth(std::vector<Value> &expr_results) {
  List *list = expr_results[0].value.list;
  List::const_iterator iter = list->begin();
  size_t i = 1;

  while (iter != list->end() && i < expr_results[1].value.ival) {
    i++;
    iter++;
  }
  if (i == expr_results[1].value.ival && iter != list->end()) {
    return Value(*iter);
  } else {
    return Value();
  }
}

Value casm_cons(std::vector<Value> &expr_results) {
  // TODO LEAK
  if (expr_results[1].is_undef()) {
    return Value();
  }

  TempList *consed_list = new TempList();
  consed_list->changes.push_back(expr_results[0]);
  consed_list->right = expr_results[1].value.list;
  return Value(expr_results[1].type, consed_list);
}

Value ExecutionVisitor::visit_function_atom(FunctionAtom *atom, std::vector<Value> &expr_results) {
  auto current_rule_bindings = rule_bindings.back();
  switch (atom->symbol_type) {
    case FunctionAtom::SymbolType::PARAMETER:
      return Value(current_rule_bindings->at(atom->offset));

    case FunctionAtom::SymbolType::FUNCTION: {
      size_t num_args = 1;
      if (expr_results.size() > 0) {
        num_args = expr_results.size();
      }

      uint64_t args[num_args];
      args[0] = 0;

      pack_values_in_array(expr_results, args);
      // TODO handle function access and function write differently
      value_list.swap(expr_results);

      Value v = Value(context_.get_function_value(atom->symbol, args));
      DEBUG("visit_atom "<<atom->symbol->name()<<" "<<v.to_str() <<" size "<<value_list.size());
      return v;
    }
    default:
      assert(0);
  }
}

Value ExecutionVisitor::visit_builtin_atom(BuiltinAtom *atom, std::vector<Value> &expr_results) {
  if (atom->name == "pow") {
    return casm_pow(expr_results);
  } else if (atom->name == "nth") {
    // TODO check if move happens here, why do we need a tmp var v here?
    Value v = casm_nth(expr_results);
    return v;
  } else if (atom->name == "cons"){
    return casm_cons(expr_results);
  } else {
    assert(0);
  }
}

Value ExecutionVisitor::visit_derived_function_atom(FunctionAtom *atom,
                                                      std::vector<Value> &expr_results,
                                                      Value& expr) {
  return expr;
}

Value ExecutionVisitor::visit_list_atom(ListAtom *atom, std::vector<Value> &vals) {
  atom->tmp_list.changes = std::move(vals);
  return Value(atom->type_, &atom->tmp_list);
}
std::string args_to_str(uint64_t args[], size_t size) {
  std::string res = "";
  size_t i = 0;

  if (size > 0) {
    for (; i < size-1; i++) {
      res += std::to_string(args[i]) + ",";
    }
    res += std::to_string(args[i]);
  }
  return res;
}

template <>
void AstWalker<ExecutionVisitor, Value>::walk_ifthenelse(IfThenElseNode* node) {
  Value cond = walk_expression_base(node->condition_);

  if (cond.value.bval) {
    walk_statement(node->then_);
  } else if (node->else_) {
    walk_statement(node->else_);
  }
}

template <>
void AstWalker<ExecutionVisitor, Value>::walk_seqblock(UnaryNode* seqblock) {
  bool forked = false;
  if (visitor.context_.updateset.pseudostate % 2 == 0) {
    CASM_UPDATESET_FORK_SEQ(&visitor.context_.updateset);
    forked = true;
  }
  visitor.visit_seqblock(seqblock);
  walk_statements(reinterpret_cast<AstListNode*>(seqblock->child_));

  if (forked) {
    visitor.context_.merge_seq(visitor.driver_);
  }
}

template <>
void AstWalker<ExecutionVisitor, Value>::walk_parblock(UnaryNode* parblock) {
  bool forked = false;
  if (visitor.context_.updateset.pseudostate % 2 == 1) {
    CASM_UPDATESET_FORK_PAR(&visitor.context_.updateset);
    forked = true;
  }
  visitor.visit_seqblock(parblock);
  walk_statements(reinterpret_cast<AstListNode*>(parblock->child_));

  if (forked) {
    visitor.context_.merge_par();
  }
}

void ExecutionWalker::run() {
  std::vector<uint64_t*> initializer_args;

  for (auto pair: visitor.context_.symbol_table.table_) {
    auto function_map = std::unordered_map<ArgumentsKey, Value>();

    if (pair.second->symbol_type == Function::SType::FUNCTION && pair.second->intitializers_ != nullptr) {
      for (std::pair<ExpressionBase*, ExpressionBase*> init : *pair.second->intitializers_) {
        size_t num_args = 0;
        uint64_t *args = new uint64_t[10];
        if (init.first != nullptr) {
          std::vector<Value> ident;
          ident.push_back(walk_expression_base(init.first));
          pack_values_in_array(ident, &args[0]);
          num_args = ident.size();
          DEBUG("INTI FOO "<<pair.second->id << " arg: "<<args[0]);
        } else {
          args[0] = 0;
        }

        if (function_map.count({&args[0], num_args}) != 0) {
          yy::location loc = init.first ? init.first->location+init.second->location : init.second->location;
          visitor.driver_.error(loc, "function `"+pair.first+"("+args_to_str(args, num_args)+")` already initialized");
          throw RuntimeException("function already initialized");
        }
        function_map.emplace(std::pair<ArgumentsKey, Value>({&args[0], num_args}, walk_expression_base(init.second)));
        initializer_args.push_back(args);
      }
    }
    visitor.context_.functions[pair.second->id] = std::pair<Function*, std::unordered_map<ArgumentsKey, Value>>(pair.second, function_map);
  }

  Function *program_sym = visitor.context_.symbol_table.get("program");
  uint64_t args[10] = {0};
  size_t steps = 0;
  while(true) {
    Value& program_val = visitor.context_.get_function_value(program_sym, args);
    DEBUG(program_val.to_str());
    if (program_val.type == TypeType::UNDEF) {
      break;
    }
    walk_rule(program_val.value.rule);
    visitor.context_.apply_updates();
    steps += 1;
  }

  std::cout << steps <<" steps later..."<<std::endl;

  for (auto ptr : initializer_args) {
    delete[] ptr;
  }
}
<commit_msg>Implement len() builtin<commit_after>#include <cmath>
#include <assert.h>
#include <utility>

#include "macros.h"
#include "libutil/exceptions.h"

#include "libinterpreter/execution_visitor.h"

DEFINE_CASM_UPDATESET_FORK_PAR
DEFINE_CASM_UPDATESET_FORK_SEQ

std::hash<Value> hasher;

void pack_values_in_array(const std::vector<Value> &value_list, uint64_t array[]) {
  for (size_t i=0; i < value_list.size(); i++) {
    array[i] = hasher(value_list[i]);
  }
}

ExecutionVisitor::ExecutionVisitor(ExecutionContext &ctxt, Driver& driver)
    : driver_(driver), context_(ctxt) {
  rule_bindings.push_back(&main_bindings);
}

void ExecutionVisitor::visit_assert(UnaryNode* assert, Value& val) {
  if (val.value.bval != true) {
    driver_.error(assert->location,
                  "Assertion failed");
    throw RuntimeException("Assertion failed");
  }
}

void ExecutionVisitor::visit_update(UpdateNode *update, Value &func_val, Value& expr_v) {
  UNUSED(func_val);

  casm_update* up = (casm_update*) pp_mem_alloc(&(context_.pp_stack), sizeof(casm_update));

  up->value = (void*) expr_v.to_uint64_t();
  up->defined = (expr_v.type == TypeType::UNDEF) ? 0 : 1;
  up->func = update->func->symbol->id;
  up->line = (uint64_t) update;
  pack_values_in_array(value_list, up->args);

  up->num_args = value_list.size();
  value_list.clear();

  auto& function_map = context_.functions[update->func->symbol->id];
  if (function_map.second.count({up->args, up->num_args}) == 0) {
    function_map.second.emplace(ArgumentsKey{up->args, up->num_args}, Value());
  }
  Value& ref = function_map.second[{up->args, up->num_args}];
  casm_update* v = (casm_update*)casm_updateset_add(&(context_.updateset),
                                                    (void*) &ref,
                                                    (void*) up);
  DEBUG("UPADTE "<<update->func->name<<" num args "<<up->num_args << " arg[0] "<<up->args[0]<< " val "<<expr_v.to_str());
  if (v != nullptr) {
    // Check if values match
    for (int i=0; i < up->num_args; i++) {
      if (up->args[i] != v->args[i]) {
        return;
      }
    }

    driver_.error(update->func->location,
                  "update conflict in parallel block for function `"+update->func->name+"`");
    throw RuntimeException("Conflict in updateset");
  }
}

void ExecutionVisitor::visit_call_pre(CallNode *call) { UNUSED(call); }

void ExecutionVisitor::visit_call_pre(CallNode *call, Value& expr) {
  if (expr.type != TypeType::UNDEF) {
    call->rule = expr.value.rule;
  } else {
    throw RuntimeException("Cannot call UNDEF");
  }
}

void ExecutionVisitor::visit_call(CallNode *call, std::vector<Value> &argument_results) {
  UNUSED(call);

  if (call->ruleref) {
    size_t args_defined = call->rule->arguments.size();
    size_t args_provided = argument_results.size();
    if (args_defined != args_provided) {
      driver_.error(call->location, "indirectly called rule `"+call->rule->name+
                    "` expects "+std::to_string(args_defined)+" arguments but "+
                    std::to_string(args_provided)+" where provided");
      throw RuntimeException("Invalid indirect call");
    } else {
      for (size_t i=0; i < args_defined; i++) {
        if (*call->rule->arguments[i] != argument_results[i].type) {
          driver_.error(call->arguments->at(i)->location,
                        "argument "+std::to_string(i+1)+" of indirectly called rule `"+
                        call->rule->name+"` must be `"+
                        call->rule->arguments[i]->to_str()+"` but was `"+
                        argument_results[i].type.to_str()+"`");
          throw RuntimeException("Invalid indirect call");
        }
      }
    }
  }

  rule_bindings.push_back(&argument_results);
}

void ExecutionVisitor::visit_call_post(CallNode *call) {
  UNUSED(call);
  rule_bindings.pop_back();
}

void ExecutionVisitor::visit_print(PrintNode *node, const std::vector<Value> &arguments) {
  for (const Value& v: arguments) {
    std::cout << v.to_str();
  }
  std::cout << std::endl;
}

void ExecutionVisitor::visit_let(LetNode *node, Value& v) {
  rule_bindings.back()->push_back(v);
}

void ExecutionVisitor::visit_let_post(LetNode *node) {
  rule_bindings.back()->pop_back();
}

Value ExecutionVisitor::visit_expression(Expression *expr, Value &left_val, Value &right_val) {
  DEBUG("left "<<left_val.to_str()<<" right "<<right_val.to_str());
  switch (expr->op) {
    case Expression::Operation::ADD: {
      left_val.add(right_val);
      DEBUG("ADD "<<left_val.to_str());
      return left_val;
    }
    case Expression::Operation::SUB: {
      left_val.sub(right_val);
      return left_val;
    }
    case Expression::Operation::MUL: {
      left_val.mul(right_val);
      return left_val;
    }
    case Expression::Operation::DIV: {
      left_val.div(right_val);
      return left_val;
    }
    case Expression::Operation::MOD: {
      left_val.mod(right_val);
      return left_val;
    }
    case Expression::Operation::RAT_DIV: {
      left_val.mod(right_val);
      return left_val;
    }

    case Expression::Operation::EQ: {
      Value tmp(value_eq(left_val, right_val));
      return tmp;
    }
    case Expression::Operation::NEQ: {
      Value tmp(!value_eq(left_val, right_val));
      return tmp;
    }

    case Expression::Operation::LESSER:
      left_val.lesser(right_val);
      return left_val;
    case Expression::Operation::GREATER:
      left_val.greater(right_val);
      return left_val;
    case Expression::Operation::LESSEREQ:
      left_val.lessereq(right_val);
      return left_val;
    case Expression::Operation::GREATEREQ:
      left_val.greatereq(right_val);
      return left_val;

    default: assert(0);
  }
}

Value ExecutionVisitor::visit_expression_single(Expression *expr, Value &val) {
  UNUSED(expr);
  return val;
}

Value casm_pow(std::vector<Value> &expr_results) {
  switch (expr_results[0].type.t) {
    case TypeType::INT:
      return Value((INT_T)std::pow(expr_results[0].value.ival,
                                             expr_results[1].value.ival));
    case TypeType::FLOAT:
      DEBUG("POW ARGS "<<expr_results[0].value.fval<<" foobar "<<expr_results[1].value.fval);
      return Value((FLOAT_T)std::pow(expr_results[0].value.fval,
                                             expr_results[1].value.fval));

    default: assert(0);

  }
}

Value casm_nth(std::vector<Value> &expr_results) {
  List *list = expr_results[0].value.list;
  List::const_iterator iter = list->begin();
  size_t i = 1;

  while (iter != list->end() && i < expr_results[1].value.ival) {
    i++;
    iter++;
  }
  if (i == expr_results[1].value.ival && iter != list->end()) {
    return Value(*iter);
  } else {
    return Value();
  }
}

Value casm_cons(std::vector<Value> &expr_results) {
  // TODO LEAK
  if (expr_results[1].is_undef()) {
    return Value();
  }

  TempList *consed_list = new TempList();
  consed_list->changes.push_back(expr_results[0]);
  consed_list->right = expr_results[1].value.list;
  return Value(expr_results[1].type, consed_list);
}

Value casm_len(std::vector<Value> &expr_results) {
  // TODO len is really slow right now, it itertes over complete list
  if (expr_results[0].is_undef()) {
    return Value();
  }

  List *list = expr_results[0].value.list;
  List::const_iterator iter = list->begin();

  size_t count = 0;

  while (iter != list->end()) {
    count++;
    iter++;
  }

  return Value((INT_T) count);
}

Value ExecutionVisitor::visit_function_atom(FunctionAtom *atom, std::vector<Value> &expr_results) {
  auto current_rule_bindings = rule_bindings.back();
  switch (atom->symbol_type) {
    case FunctionAtom::SymbolType::PARAMETER:
      return Value(current_rule_bindings->at(atom->offset));

    case FunctionAtom::SymbolType::FUNCTION: {
      size_t num_args = 1;
      if (expr_results.size() > 0) {
        num_args = expr_results.size();
      }

      uint64_t args[num_args];
      args[0] = 0;

      pack_values_in_array(expr_results, args);
      // TODO handle function access and function write differently
      value_list.swap(expr_results);

      Value v = Value(context_.get_function_value(atom->symbol, args));
      DEBUG("visit_atom "<<atom->symbol->name()<<" "<<v.to_str() <<" size "<<value_list.size());
      return v;
    }
    default:
      assert(0);
  }
}

Value ExecutionVisitor::visit_builtin_atom(BuiltinAtom *atom, std::vector<Value> &expr_results) {
  if (atom->name == "pow") {
    return casm_pow(expr_results);
  } else if (atom->name == "nth") {
    // TODO check if move happens here, why do we need a tmp var v here?
    Value v = casm_nth(expr_results);
    return v;
  } else if (atom->name == "cons") {
    return casm_cons(expr_results);
  } else if (atom->name == "len") {
    return casm_len(expr_results);
  } else {
    assert(0);
  }
}

Value ExecutionVisitor::visit_derived_function_atom(FunctionAtom *atom,
                                                      std::vector<Value> &expr_results,
                                                      Value& expr) {
  return expr;
}

Value ExecutionVisitor::visit_list_atom(ListAtom *atom, std::vector<Value> &vals) {
  atom->tmp_list.changes = std::move(vals);
  return Value(atom->type_, &atom->tmp_list);
}
std::string args_to_str(uint64_t args[], size_t size) {
  std::string res = "";
  size_t i = 0;

  if (size > 0) {
    for (; i < size-1; i++) {
      res += std::to_string(args[i]) + ",";
    }
    res += std::to_string(args[i]);
  }
  return res;
}

template <>
void AstWalker<ExecutionVisitor, Value>::walk_ifthenelse(IfThenElseNode* node) {
  Value cond = walk_expression_base(node->condition_);

  if (cond.value.bval) {
    walk_statement(node->then_);
  } else if (node->else_) {
    walk_statement(node->else_);
  }
}

template <>
void AstWalker<ExecutionVisitor, Value>::walk_seqblock(UnaryNode* seqblock) {
  bool forked = false;
  if (visitor.context_.updateset.pseudostate % 2 == 0) {
    CASM_UPDATESET_FORK_SEQ(&visitor.context_.updateset);
    forked = true;
  }
  visitor.visit_seqblock(seqblock);
  walk_statements(reinterpret_cast<AstListNode*>(seqblock->child_));

  if (forked) {
    visitor.context_.merge_seq(visitor.driver_);
  }
}

template <>
void AstWalker<ExecutionVisitor, Value>::walk_parblock(UnaryNode* parblock) {
  bool forked = false;
  if (visitor.context_.updateset.pseudostate % 2 == 1) {
    CASM_UPDATESET_FORK_PAR(&visitor.context_.updateset);
    forked = true;
  }
  visitor.visit_seqblock(parblock);
  walk_statements(reinterpret_cast<AstListNode*>(parblock->child_));

  if (forked) {
    visitor.context_.merge_par();
  }
}

void ExecutionWalker::run() {
  std::vector<uint64_t*> initializer_args;

  for (auto pair: visitor.context_.symbol_table.table_) {
    auto function_map = std::unordered_map<ArgumentsKey, Value>();

    if (pair.second->symbol_type == Function::SType::FUNCTION && pair.second->intitializers_ != nullptr) {
      for (std::pair<ExpressionBase*, ExpressionBase*> init : *pair.second->intitializers_) {
        size_t num_args = 0;
        uint64_t *args = new uint64_t[10];
        if (init.first != nullptr) {
          std::vector<Value> ident;
          ident.push_back(walk_expression_base(init.first));
          pack_values_in_array(ident, &args[0]);
          num_args = ident.size();
          DEBUG("INTI FOO "<<pair.second->id << " arg: "<<args[0]);
        } else {
          args[0] = 0;
        }

        if (function_map.count({&args[0], num_args}) != 0) {
          yy::location loc = init.first ? init.first->location+init.second->location : init.second->location;
          visitor.driver_.error(loc, "function `"+pair.first+"("+args_to_str(args, num_args)+")` already initialized");
          throw RuntimeException("function already initialized");
        }
        function_map.emplace(std::pair<ArgumentsKey, Value>({&args[0], num_args}, walk_expression_base(init.second)));
        initializer_args.push_back(args);
      }
    }
    visitor.context_.functions[pair.second->id] = std::pair<Function*, std::unordered_map<ArgumentsKey, Value>>(pair.second, function_map);
  }

  Function *program_sym = visitor.context_.symbol_table.get("program");
  uint64_t args[10] = {0};
  size_t steps = 0;
  while(true) {
    Value& program_val = visitor.context_.get_function_value(program_sym, args);
    DEBUG(program_val.to_str());
    if (program_val.type == TypeType::UNDEF) {
      break;
    }
    walk_rule(program_val.value.rule);
    visitor.context_.apply_updates();
    steps += 1;
  }

  std::cout << steps <<" steps later..."<<std::endl;

  for (auto ptr : initializer_args) {
    delete[] ptr;
  }
}
<|endoftext|>


<commit_before>#include <cstdlib>
#include <cstdio>
#include <vector>
#include <ctime>

struct FenwickTree {
	std::vector<int> x;
	int n;

	FenwickTree(int t)
		: x(t, 0), n(t) {
	}

	int sum(int i, int j) {
		if (i == 0) {
			int S = 0;
			for (j; j >= 0; j = (j & (j + 1)) - 1) S += x[j];
			return S;
		}
		return sum(0, j) - sum(0, i - 1);
	}

	void add(int k, int a) {
		for (; k < n; k |= k+1) x[k] += a;
	}

	void clear() {
		for(int i = 0; i <= n; ++i) x[i] = 0;
	}
};

typedef std::vector<FenwickTree*> FenwickTrees;

struct Object {
	void* data;

	int n, pos;

	Object(void* data = 0, int n = 0, int pos = 0)
		: data(data), n(n), pos(pos) {}
};

class Allocator {
public:
	Allocator(int slice_size = 10000)
		: m_slice_size(slice_size) {}

	void init() {
		m_next_block.push_back(new FenwickTree(m_slice_size + 2));
		m_size_block.push_back(new FenwickTree(m_slice_size + 2));

		heap.push_back(malloc(m_slice_size));

		m_next_block[0]->add(0, m_slice_size);
	}

	void* alloc(int n) {
		int m = heap.size();

		if (n < m_slice_size) {
			for (int i = 0; i < m; ++i) {
				int ip = 0;
				void* slice = heap[i];

				while (ip < m_slice_size) {
					int next_block = m_next_block[i]->sum(0, ip);
					int last_size = m_size_block[i]->sum(0, ip);

					if (next_block - ip >= n) {
						int new_next_block = ip + n;

						m_next_block[i]->add(ip, -next_block + new_next_block);
						m_next_block[i]->add(new_next_block, -new_next_block + next_block);

						m_size_block[i]->add(ip, n - last_size);
						m_size_block[i]->add(ip + last_size, last_size);
						m_size_block[i]->add(new_next_block, -n);

						return new Object(static_cast<char*>(slice) + ip, i, ip);
					}

					if (next_block >= m_slice_size) break;

					ip = next_block;
				}
			}
		}

		if (n > m_slice_size) {
			m_next_block.push_back(new FenwickTree(n));
			m_size_block.push_back(new FenwickTree(n + 2));
			heap.push_back(malloc(n));
		} else {
			m_next_block.push_back(new FenwickTree(m_slice_size));
			m_size_block.push_back(new FenwickTree(m_slice_size + 2));
			heap.push_back(malloc(m_slice_size));
		}

		m_next_block[m]->add(0, n);
		m_next_block[m]->add(n, -n + m_slice_size);

		m_size_block[m]->add(0, n);
		m_size_block[m]->add(n, -n);

		return new Object(heap[m], m, 0);
	}

	void dealloc(void* _obj) {
		Object* obj = static_cast<Object*>(_obj);

		int n = obj->n;
		int ip = obj->pos;

		int last_size = m_size_block[n]->sum(0, ip);
		int last_next_block = m_next_block[n]->sum(0, ip);

		int next_block = m_next_block[n]->sum(0, last_next_block);

		m_size_block[n]->add(ip, -last_size);
		m_size_block[n]->add(ip + last_size, last_size);

		m_next_block[n]->add(ip, -last_next_block + next_block);
		m_next_block[n]->add(last_next_block,  last_next_block - next_block);
		delete obj;
	}

	~Allocator() {
		for (size_t i = 0, n = heap.size(); i < n; ++i) {
			free(heap[i]);
			delete m_next_block[i];
			delete m_size_block[i];
		}
	}

private:

	FenwickTrees m_next_block;
	FenwickTrees m_size_block;
	std::vector<void*> heap;

	int m_slice_size;
};

int main()
{
	int L = 100000; //100000;
	int M = 10000000; //1000000;
	int S = 100001; //1000000;

	time_t t0 = clock();
	Allocator* alloc = new Allocator(S);

	alloc->init();

	for (int i = 0; i < M; ++i) {
		char* a = static_cast<char*>(alloc->alloc(L));

		sprintf(a, "[default] teste teste\nteste teste\n");
		//printf("%s\n", a);
		alloc->dealloc(a);
	}

	delete alloc;
	time_t t1 = clock();

	printf("OK!\n");

	time_t t2 = clock();
	for (int i = 0; i < M; ++i) {
		char* a = static_cast<char*>(malloc(L));
		sprintf(a, "[default] teste teste\nteste teste\n");
		//printf("%s\n", a);
		free(a);
	}
	time_t t3 = clock();

	printf("Allocator time: %.6lf\n", static_cast<double>(t1-t0) / CLOCKS_PER_SEC);
	printf("Default allocator time: %.6lf\n", static_cast<double>(t3-t2) / CLOCKS_PER_SEC);

	return 0;
}

<commit_msg>Experimental allocator removed<commit_after><|endoftext|>


<commit_before>/*
 * $Id$
 *
 * Copyright (c) 2011 Surfnet
 * Copyright (c) 2011 .SE (The Internet Infrastructure Foundation).
 * Copyright (c) 2011 OpenDNSSEC AB (svb)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include "config.h"

/* On MacOSX arc4random is only available when we
   undef _ANSI_SOURCE and define _DARWIN_C_SOURCE. */
#ifdef __APPLE__
#undef _ANSI_SOURCE
#define _DARWIN_C_SOURCE 1
#endif
#include <stdlib.h>

#include "resalt.h"

#include "shared/duration.h"

int PolicyUpdateSalt(::ods::kasp::Policy &policy)
{
    /* This will be the salt that we create
     * salt never longer than 255 so 512 can
     * hold a hex encoded version of the salt.
    */
    char salt[512];
    /* for mapping random numbers in range 0..15 to hex characters */
    const char *hex_chars = "0123456789abcdef";
    /* Number of hex characters */
    unsigned int nhex_chars = strlen(hex_chars);

    if (!policy.denial().has_nsec3()) {
        // no nsec3 present so no need to resalt either.
        return 0;
    }

    const ::ods::kasp::NSEC3 &nsec3 = policy.denial().nsec3();
    if ( nsec3.has_salt() && nsec3.has_salt_last_change() ) {
        if (time_now() < nsec3.salt_last_change()+nsec3.resalt() ) {
            // Current time is within resalt period since last change.
            return 0;
        }
    }

    // Seems we have a nsec3 section that needs a salt generated

    // required uint32 resalt = 3 [(xml).path="Resalt", (xml).type=duration]; // re-salting interval
    // required uint32 saltlength = 6 [(xml).path="Hash/Salt/@length"];// nsec3 salt length 0..255
    // optional string salt = 7 [(xml).path="Hash/Salt"];// the actual salt is generated by the enforcer e.g. 0438eb9a93a6d6c5
    // optional uint32 salt_last_change = 8 [(xml).path="Hash/Salt/@lastchanged"]; // timestamp for when the last resalt took place

    unsigned int saltlength = nsec3.saltlength();
    if (saltlength <= 0 || saltlength > 255) {
        // error, saltlength out of range..
        return -3;
    }

#ifdef HAVE_ARC4RANDOM
    for (int i = 0; i < 2*saltlength; ++i) {
        salt[i] = hex_chars[ arc4random() % nhex_chars ];
    }
#else
   srand( time(NULL) );
    for (int i = 0; i < 2*saltlength; ++i) {
        salt[i] = hex_chars[rand()%nhex_chars];

    }
#endif

    policy.mutable_denial()->mutable_nsec3()->set_salt(&salt[0],2*saltlength);
    policy.mutable_denial()->mutable_nsec3()->set_salt_last_change(time_now());
    return 1;
}
<commit_msg>Test if arc4random now works on BSD<commit_after>/*
 * $Id$
 *
 * Copyright (c) 2011 Surfnet
 * Copyright (c) 2011 .SE (The Internet Infrastructure Foundation).
 * Copyright (c) 2011 OpenDNSSEC AB (svb)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include "config.h"

/* On MacOSX arc4random is only available when we
   undef _ANSI_SOURCE and define _DARWIN_C_SOURCE. */
#ifdef __APPLE__
#undef _ANSI_SOURCE
#define _DARWIN_C_SOURCE 1
#endif

/* Make arc4random visable on FreeBSD and NetBSD */
#define __BSD_VISIBLE 1

#include <stdlib.h>

#include "resalt.h"

#include "shared/duration.h"

int PolicyUpdateSalt(::ods::kasp::Policy &policy)
{
    /* This will be the salt that we create
     * salt never longer than 255 so 512 can
     * hold a hex encoded version of the salt.
    */
    char salt[512];
    /* for mapping random numbers in range 0..15 to hex characters */
    const char *hex_chars = "0123456789abcdef";
    /* Number of hex characters */
    unsigned int nhex_chars = strlen(hex_chars);

    if (!policy.denial().has_nsec3()) {
        // no nsec3 present so no need to resalt either.
        return 0;
    }

    const ::ods::kasp::NSEC3 &nsec3 = policy.denial().nsec3();
    if ( nsec3.has_salt() && nsec3.has_salt_last_change() ) {
        if (time_now() < nsec3.salt_last_change()+nsec3.resalt() ) {
            // Current time is within resalt period since last change.
            return 0;
        }
    }

    // Seems we have a nsec3 section that needs a salt generated

    // required uint32 resalt = 3 [(xml).path="Resalt", (xml).type=duration]; // re-salting interval
    // required uint32 saltlength = 6 [(xml).path="Hash/Salt/@length"];// nsec3 salt length 0..255
    // optional string salt = 7 [(xml).path="Hash/Salt"];// the actual salt is generated by the enforcer e.g. 0438eb9a93a6d6c5
    // optional uint32 salt_last_change = 8 [(xml).path="Hash/Salt/@lastchanged"]; // timestamp for when the last resalt took place

    unsigned int saltlength = nsec3.saltlength();
    if (saltlength <= 0 || saltlength > 255) {
        // error, saltlength out of range..
        return -3;
    }

#ifdef HAVE_ARC4RANDOM
    for (int i = 0; i < 2*saltlength; ++i) {
        salt[i] = hex_chars[ arc4random() % nhex_chars ];
    }
#else
   srand( time(NULL) );
    for (int i = 0; i < 2*saltlength; ++i) {
        salt[i] = hex_chars[rand()%nhex_chars];

    }
#endif

    policy.mutable_denial()->mutable_nsec3()->set_salt(&salt[0],2*saltlength);
    policy.mutable_denial()->mutable_nsec3()->set_salt_last_change(time_now());
    return 1;
}
<|endoftext|>


<commit_before>/*
 Copyright (c) 2015, The Cinder Project, All rights reserved.

 This code is intended for use with the Cinder C++ library: http://libcinder.org

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

 * Redistributions of source code must retain the above copyright notice, this list of conditions and
	the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
	the following disclaimer in the documentation and/or other materials provided with the distribution.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.
*/

#include "cinder/ShaderPreprocessor.h"
#include "cinder/app/App.h"
#include "cinder/Utilities.h"
#include "cinder/Log.h"

#include <regex>

using namespace std;

namespace cinder {

namespace {
	const regex sIncludeRegex = regex( "^[ ]*#[ ]*include[ ]+[\"<](.*)[\">].*" );
} // anonymous namespace

ShaderPreprocessor::ShaderPreprocessor()
	: mCachingEnabled( true )
{
	// TODO: we discussed keeping this ci assets agnostic, instead adding getAssetPath() from the outside
	// - also, we don't want GlslProg to rely on App at all.
	//    - can it do what it needs to with Platform?
	mSearchDirectories.push_back( app::getAssetPath( "" ) );
}

string ShaderPreprocessor::parse( const fs::path &path )
{
	set<fs::path> includeTree;

	return parseRecursive( path, fs::path(), includeTree );
}

string ShaderPreprocessor::parse( const std::string &source, const fs::path &sourceDirectory )
{
	return parseTopLevel( source, sourceDirectory );
}

string ShaderPreprocessor::parseTopLevel( const string &source, const fs::path &currentDirectory )
{
	set<fs::path> includeTree;

	stringstream output;
	istringstream input( source );

	// go through each line and process includes
	string line;
	smatch matches;

	size_t lineNumber = 1;

	while( getline( input, line ) ) {
		if( regex_search( line, matches, sIncludeRegex ) ) {
			output << parseRecursive( matches[1].str(), currentDirectory, includeTree );
			output << "#line " << lineNumber << endl;
		}
		else
			output << line;

		output << endl;
		lineNumber++;
	}

	return output.str();
}

string ShaderPreprocessor::parseRecursive( const fs::path &path, const fs::path &currentDirectory, set<fs::path> &includeTree )
{
	const fs::path fullPath = findFullPath( path, currentDirectory );

	if( includeTree.count( fullPath ) )
		throw ShaderPreprocessorExc( "circular include found, path: " + fullPath.string() );

	includeTree.insert( fullPath );

	time_t timeLastWrite = 0;
	if( mCachingEnabled ) {
		timeLastWrite = fs::last_write_time( fullPath );

		auto cachedIt = mCachedSources.find( fullPath );
		if( cachedIt != mCachedSources.end() ) {
			if( cachedIt->second.mTimeLastWrite >= timeLastWrite ) {
				return cachedIt->second.mString;
			}
		}
	}

	stringstream output;

	ifstream input( fullPath.c_str() );
	if( ! input.is_open() )
		throw ShaderPreprocessorExc( "Failed to open file at path: " + fullPath.string() );

	// go through each line and process includes
	string line;
	smatch matches;

	size_t lineNumber = 1;

	while( getline( input, line ) ) {
		if( regex_search( line, matches, sIncludeRegex ) ) {
			output << parseRecursive( matches[1].str(), fullPath.parent_path(), includeTree );
			output << "#line " << lineNumber << endl;
		}
		else
			output << line;

		output << endl;
		lineNumber++;
	}

	input.close();

	if( mCachingEnabled ) {
		Source &source = mCachedSources[fullPath];
		source.mTimeLastWrite = timeLastWrite;
		source.mString = output.str();

		return source.mString;
	}
	else
		return output.str();
}

void ShaderPreprocessor::addSearchDirectory( const fs::path &directory )
{
	if( ! fs::is_directory( directory ) ) {
		CI_LOG_E( "Not a directory: " << directory );
		return;
	}

	fs::path dirCanonical = fs::canonical( directory );
	auto it = find( mSearchDirectories.begin(), mSearchDirectories.end(), dirCanonical );
	if( it == mSearchDirectories.end() )
		mSearchDirectories.push_back( dirCanonical );
}

void ShaderPreprocessor::removeSearchDirectory( const fs::path &directory )
{
	fs::path dirCanonical = fs::canonical( directory );
	mSearchDirectories.erase( remove( mSearchDirectories.begin(), mSearchDirectories.end(), dirCanonical ), mSearchDirectories.end() );
}

void ShaderPreprocessor::clearCache()
{
	mCachedSources.clear();
}

fs::path ShaderPreprocessor::findFullPath( const fs::path &includePath, const fs::path &currentDirectory )
{
	auto fullPath = currentDirectory / includePath;
	if( fs::exists( fullPath ) )
		return fs::canonical( fullPath );

	for( const auto &searchDir : mSearchDirectories ) {
		fullPath = searchDir / includePath;
		if( fs::exists( fullPath ) )
			return fs::canonical( fullPath );
	}

	throw ShaderPreprocessorExc( "could not find shader with include path: " + includePath.string() );
}

} // namespace cinder
<commit_msg>iterate over search directories in reverse order that they were added<commit_after>/*
 Copyright (c) 2015, The Cinder Project, All rights reserved.

 This code is intended for use with the Cinder C++ library: http://libcinder.org

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

 * Redistributions of source code must retain the above copyright notice, this list of conditions and
	the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
	the following disclaimer in the documentation and/or other materials provided with the distribution.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.
*/

#include "cinder/ShaderPreprocessor.h"
#include "cinder/app/App.h"
#include "cinder/Utilities.h"
#include "cinder/Log.h"

#include <regex>

using namespace std;

namespace cinder {

namespace {
	const regex sIncludeRegex = regex( "^[ ]*#[ ]*include[ ]+[\"<](.*)[\">].*" );
} // anonymous namespace

ShaderPreprocessor::ShaderPreprocessor()
	: mCachingEnabled( true )
{
	// TODO: we discussed keeping this ci assets agnostic, instead adding getAssetPath() from the outside
	// - also, we don't want GlslProg to rely on App at all.
	//    - can it do what it needs to with Platform?
	mSearchDirectories.push_back( app::getAssetPath( "" ) );
}

string ShaderPreprocessor::parse( const fs::path &path )
{
	set<fs::path> includeTree;

	return parseRecursive( path, fs::path(), includeTree );
}

string ShaderPreprocessor::parse( const std::string &source, const fs::path &sourceDirectory )
{
	return parseTopLevel( source, sourceDirectory );
}

string ShaderPreprocessor::parseTopLevel( const string &source, const fs::path &currentDirectory )
{
	set<fs::path> includeTree;

	stringstream output;
	istringstream input( source );

	// go through each line and process includes
	string line;
	smatch matches;

	size_t lineNumber = 1;

	while( getline( input, line ) ) {
		if( regex_search( line, matches, sIncludeRegex ) ) {
			output << parseRecursive( matches[1].str(), currentDirectory, includeTree );
			output << "#line " << lineNumber << endl;
		}
		else
			output << line;

		output << endl;
		lineNumber++;
	}

	return output.str();
}

string ShaderPreprocessor::parseRecursive( const fs::path &path, const fs::path &currentDirectory, set<fs::path> &includeTree )
{
	const fs::path fullPath = findFullPath( path, currentDirectory );

	if( includeTree.count( fullPath ) )
		throw ShaderPreprocessorExc( "circular include found, path: " + fullPath.string() );

	includeTree.insert( fullPath );

	time_t timeLastWrite = 0;
	if( mCachingEnabled ) {
		timeLastWrite = fs::last_write_time( fullPath );

		auto cachedIt = mCachedSources.find( fullPath );
		if( cachedIt != mCachedSources.end() ) {
			if( cachedIt->second.mTimeLastWrite >= timeLastWrite ) {
				return cachedIt->second.mString;
			}
		}
	}

	stringstream output;

	ifstream input( fullPath.c_str() );
	if( ! input.is_open() )
		throw ShaderPreprocessorExc( "Failed to open file at path: " + fullPath.string() );

	// go through each line and process includes
	string line;
	smatch matches;

	size_t lineNumber = 1;

	while( getline( input, line ) ) {
		if( regex_search( line, matches, sIncludeRegex ) ) {
			output << parseRecursive( matches[1].str(), fullPath.parent_path(), includeTree );
			output << "#line " << lineNumber << endl;
		}
		else
			output << line;

		output << endl;
		lineNumber++;
	}

	input.close();

	if( mCachingEnabled ) {
		Source &source = mCachedSources[fullPath];
		source.mTimeLastWrite = timeLastWrite;
		source.mString = output.str();

		return source.mString;
	}
	else
		return output.str();
}

void ShaderPreprocessor::addSearchDirectory( const fs::path &directory )
{
	if( ! fs::is_directory( directory ) ) {
		CI_LOG_E( "Not a directory: " << directory );
		return;
	}

	fs::path dirCanonical = fs::canonical( directory );
	auto it = find( mSearchDirectories.begin(), mSearchDirectories.end(), dirCanonical );
	if( it == mSearchDirectories.end() )
		mSearchDirectories.push_back( dirCanonical );
}

void ShaderPreprocessor::removeSearchDirectory( const fs::path &directory )
{
	fs::path dirCanonical = fs::canonical( directory );
	mSearchDirectories.erase( remove( mSearchDirectories.begin(), mSearchDirectories.end(), dirCanonical ), mSearchDirectories.end() );
}

void ShaderPreprocessor::clearCache()
{
	mCachedSources.clear();
}

fs::path ShaderPreprocessor::findFullPath( const fs::path &includePath, const fs::path &currentDirectory )
{
	auto fullPath = currentDirectory / includePath;
	if( fs::exists( fullPath ) )
		return fs::canonical( fullPath );

	for( auto dirIt = mSearchDirectories.rbegin(); dirIt != mSearchDirectories.rend(); ++dirIt ) {
		fullPath = *dirIt / includePath;
		if( fs::exists( fullPath ) )
			return fs::canonical( fullPath );
	}

	throw ShaderPreprocessorExc( "could not find shader with include path: " + includePath.string() );
}

} // namespace cinder
<|endoftext|>


<commit_before>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright (C) 2011 - 2014                                                  *
 * Dominik Charousset <dominik.charousset (at) haw-hamburg.de>                *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENCE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#ifndef CAF_POLICY_ABSTRACT_EVENT_BASED_ACTOR_HPP
#define CAF_POLICY_ABSTRACT_EVENT_BASED_ACTOR_HPP

#include <tuple>
#include <stack>
#include <memory>
#include <vector>
#include <type_traits>

#include "caf/config.hpp"
#include "caf/extend.hpp"
#include "caf/behavior.hpp"
#include "caf/scheduler.hpp"

#include "caf/policy/resume_policy.hpp"

#include "caf/detail/logging.hpp"

namespace caf {
namespace policy {

class event_based_resume {
 public:

  // Base must be a mailbox-based actor
  template <class Base, class Derived>
  struct mixin : Base, resumable {
    template <class... Ts>
    mixin(Ts&&... args) : Base(std::forward<Ts>(args)...) {
      // nop
    }

    void attach_to_scheduler() override {
      this->ref();
    }

    void detach_from_scheduler() override {
      this->deref();
    }

    resumable::resume_result resume(execution_unit* new_host,
                                    size_t max_throughput) override {
      auto d = static_cast<Derived*>(this);
      d->host(new_host);
      CAF_LOG_TRACE("id = " << d->id());
      auto done_cb = [&]() -> bool {
        CAF_LOG_TRACE("");
        d->bhvr_stack().clear();
        d->bhvr_stack().cleanup();
        d->on_exit();
        if (!d->bhvr_stack().empty()) {
          CAF_LOG_DEBUG("on_exit did set a new behavior in on_exit");
          d->planned_exit_reason(exit_reason::not_exited);
          return false; // on_exit did set a new behavior
        }
        auto rsn = d->planned_exit_reason();
        if (rsn == exit_reason::not_exited) {
          rsn = exit_reason::normal;
          d->planned_exit_reason(rsn);
        }
        d->cleanup(rsn);
        return true;

      };
      auto actor_done = [&] {
        return d->bhvr_stack().empty()
               || d->planned_exit_reason() != exit_reason::not_exited;
      };
      // actors without behavior or that have already defined
      // an exit reason must not be resumed
      CAF_REQUIRE(!d->m_initialized || !actor_done());
      try {
        if (!d->m_initialized) {
          d->m_initialized = true;
          auto bhvr = d->make_behavior();
          if (bhvr) d->become(std::move(bhvr));
          // else: make_behavior() might have just called become()
          if (actor_done() && done_cb()) return resume_result::done;
          // else: enter resume loop
        }
        // max_throughput = 0 means infinite
        size_t increment = max_throughput == 0 ? 0 : 1;
        if (max_throughput == 0) {
          max_throughput = 1;
        }
        for (size_t i = 0; i < max_throughput; i += increment) {
          auto ptr = d->next_message();
          if (ptr) {
            if (d->invoke_message(ptr)) {
              if (actor_done() && done_cb()) {
                CAF_LOG_DEBUG("actor exited");
                return resume_result::done;
              }
              // continue from cache if current message was
              // handled, because the actor might have changed
              // its behavior to match 'old' messages now
              while (d->invoke_message_from_cache()) {
                if (actor_done() && done_cb()) {
                  CAF_LOG_DEBUG("actor exited");
                  return resume_result::done;
                }
              }
            }
            // add ptr to cache if invoke_message
            // did not reset it (i.e. skipped, but not dropped)
            if (ptr) {
              CAF_LOG_DEBUG("add message to cache");
              d->push_to_cache(std::move(ptr));
            }
          } else {
            CAF_LOG_DEBUG("no more element in mailbox; going to block");
            if (d->mailbox().try_block()) {
              return resumable::awaiting_message;
            }
            CAF_LOG_DEBUG("try_block() interrupted by new message");
          }
        }
        if (!d->has_next_message() && d->mailbox().try_block()) {
          return resumable::awaiting_message;
        }
        // time's up
        return resumable::resume_later;
      }
      catch (actor_exited& what) {
        CAF_LOG_INFO("actor died because of exception: actor_exited, "
                     "reason = " << what.reason());
        if (d->exit_reason() == exit_reason::not_exited) {
          d->quit(what.reason());
        }
      }
      catch (std::exception& e) {
        CAF_LOG_WARNING("actor died because of exception: "
                 << detail::demangle(typeid(e))
                 << ", what() = " << e.what());
        if (d->exit_reason() == exit_reason::not_exited) {
          d->quit(exit_reason::unhandled_exception);
        }
      }
      catch (...) {
        CAF_LOG_WARNING("actor died because of an unknown exception");
        if (d->exit_reason() == exit_reason::not_exited) {
          d->quit(exit_reason::unhandled_exception);
        }
      }
      done_cb();
      return resumable::done;
    }
  };

  template <class Actor>
  void await_data(Actor*) {
    static_assert(std::is_same<Actor, Actor>::value == false,
            "The event-based resume policy cannot be used "
            "to implement blocking actors");
  }

  template <class Actor>
  bool await_data(Actor*, const duration&) {
    static_assert(std::is_same<Actor, Actor>::value == false,
            "The event-based resume policy cannot be used "
            "to implement blocking actors");
    return false;
  }
};

} // namespace policy
} // namespace caf

#endif // CAF_POLICY_ABSTRACT_EVENT_BASED_ACTOR_HPP
<commit_msg>Fix handling of on_exit() in event-based actors<commit_after>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright (C) 2011 - 2014                                                  *
 * Dominik Charousset <dominik.charousset (at) haw-hamburg.de>                *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENCE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#ifndef CAF_POLICY_ABSTRACT_EVENT_BASED_ACTOR_HPP
#define CAF_POLICY_ABSTRACT_EVENT_BASED_ACTOR_HPP

#include <tuple>
#include <stack>
#include <memory>
#include <vector>
#include <type_traits>

#include "caf/config.hpp"
#include "caf/extend.hpp"
#include "caf/behavior.hpp"
#include "caf/scheduler.hpp"

#include "caf/policy/resume_policy.hpp"

#include "caf/detail/logging.hpp"

namespace caf {
namespace policy {

class event_based_resume {
 public:

  // Base must be a mailbox-based actor
  template <class Base, class Derived>
  struct mixin : Base, resumable {
    template <class... Ts>
    mixin(Ts&&... args) : Base(std::forward<Ts>(args)...) {
      // nop
    }

    void attach_to_scheduler() override {
      this->ref();
    }

    void detach_from_scheduler() override {
      this->deref();
    }

    resumable::resume_result resume(execution_unit* new_host,
                                    size_t max_throughput) override {
      auto d = static_cast<Derived*>(this);
      d->host(new_host);
      CAF_LOG_TRACE("id = " << d->id());
      auto done_cb = [&]() -> bool {
        CAF_LOG_TRACE("");
        d->bhvr_stack().clear();
        d->bhvr_stack().cleanup();
        d->on_exit();
        if (!d->bhvr_stack().empty()) {
          CAF_LOG_DEBUG("on_exit did set a new behavior");
          d->planned_exit_reason(exit_reason::not_exited);
          return false; // on_exit did set a new behavior
        }
        auto rsn = d->planned_exit_reason();
        if (rsn == exit_reason::not_exited) {
          rsn = exit_reason::normal;
          d->planned_exit_reason(rsn);
        }
        d->cleanup(rsn);
        return true;
      };
      auto actor_done = [&]() -> bool {
        if (d->bhvr_stack().empty()
            || d->planned_exit_reason() != exit_reason::not_exited) {
          return done_cb();
        }
        return false;
      };
      // actors without behavior or that have already defined
      // an exit reason must not be resumed
      CAF_REQUIRE(!d->m_initialized
                  || (!d->bhvr_stack().empty()
                      && !d->planned_exit_reason() != exit_reason::not_exited));
      try {
        if (!d->m_initialized) {
          d->m_initialized = true;
          auto bhvr = d->make_behavior();
          if (bhvr) {
            // make_behavior() did return a behavior instead of using become()
            d->become(std::move(bhvr));
          }
          if (actor_done()) {
            return resume_result::done;
          }
        }
        // max_throughput = 0 means infinite
        size_t increment = max_throughput == 0 ? 0 : 1;
        if (max_throughput == 0) {
          max_throughput = 1;
        }
        for (size_t i = 0; i < max_throughput; i += increment) {
          auto ptr = d->next_message();
          if (ptr) {
            if (d->invoke_message(ptr)) {
              if (actor_done()) {
                CAF_LOG_DEBUG("actor exited");
                return resume_result::done;
              }
              // continue from cache if current message was
              // handled, because the actor might have changed
              // its behavior to match 'old' messages now
              while (d->invoke_message_from_cache()) {
                if (actor_done()) {
                  CAF_LOG_DEBUG("actor exited");
                  return resume_result::done;
                }
              }
            }
            // add ptr to cache if invoke_message
            // did not reset it (i.e. skipped, but not dropped)
            if (ptr) {
              CAF_LOG_DEBUG("add message to cache");
              d->push_to_cache(std::move(ptr));
            }
          } else {
            CAF_LOG_DEBUG("no more element in mailbox; going to block");
            if (d->mailbox().try_block()) {
              return resumable::awaiting_message;
            }
            CAF_LOG_DEBUG("try_block() interrupted by new message");
          }
        }
        if (!d->has_next_message() && d->mailbox().try_block()) {
          return resumable::awaiting_message;
        }
        // time's up
        return resumable::resume_later;
      }
      catch (actor_exited& what) {
        CAF_LOG_INFO("actor died because of exception: actor_exited, "
                     "reason = " << what.reason());
        if (d->exit_reason() == exit_reason::not_exited) {
          d->quit(what.reason());
        }
      }
      catch (std::exception& e) {
        CAF_LOG_WARNING("actor died because of exception: "
                 << detail::demangle(typeid(e))
                 << ", what() = " << e.what());
        if (d->exit_reason() == exit_reason::not_exited) {
          d->quit(exit_reason::unhandled_exception);
        }
      }
      catch (...) {
        CAF_LOG_WARNING("actor died because of an unknown exception");
        if (d->exit_reason() == exit_reason::not_exited) {
          d->quit(exit_reason::unhandled_exception);
        }
      }
      if (!done_cb()) {
        // actor has been "revived", try running it again later
        return resumable::resume_later;
      }
      return resumable::done;
    }
  };

  template <class Actor>
  void await_data(Actor*) {
    static_assert(std::is_same<Actor, Actor>::value == false,
            "The event-based resume policy cannot be used "
            "to implement blocking actors");
  }

  template <class Actor>
  bool await_data(Actor*, const duration&) {
    static_assert(std::is_same<Actor, Actor>::value == false,
            "The event-based resume policy cannot be used "
            "to implement blocking actors");
    return false;
  }
};

} // namespace policy
} // namespace caf

#endif // CAF_POLICY_ABSTRACT_EVENT_BASED_ACTOR_HPP
<|endoftext|>


<commit_before>// Copyright 2013 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//     * Neither the name of Google Inc. nor the names of its
//       contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include <stdlib.h>

#include "v8.h"

#include "cctest.h"
#include "code-stubs.h"
#include "factory.h"
#include "macro-assembler.h"
#include "platform.h"

#if __GNUC__
#define STDCALL  __attribute__((stdcall))
#else
#define STDCALL  __stdcall
#endif

using namespace v8::internal;

typedef int32_t STDCALL (*ConvertDToIFunc)(double input);

int STDCALL ConvertDToICVersion(double d) {
  Address double_ptr = reinterpret_cast<Address>(&d);
  uint32_t exponent_bits = Memory::uint32_at(double_ptr + kDoubleSize / 2);
  int32_t shifted_mask = static_cast<int32_t>(Double::kExponentMask >> 32);
  int32_t exponent = (((exponent_bits & shifted_mask) >>
                       (Double::kPhysicalSignificandSize - 32)) -
                      HeapNumber::kExponentBias);
  uint32_t unsigned_exponent = static_cast<uint32_t>(exponent);
  int result = 0;
  uint32_t max_exponent =
    static_cast<uint32_t>(Double::kPhysicalSignificandSize);
  if (unsigned_exponent >= max_exponent) {
    if ((exponent - Double::kPhysicalSignificandSize) < 32) {
      result = Memory::uint32_at(double_ptr) <<
        (exponent - Double::kPhysicalSignificandSize);
    }
  } else {
    uint64_t big_result =
        (BitCast<uint64_t>(d) & Double::kSignificandMask) | Double::kHiddenBit;
    big_result = big_result >> (Double::kPhysicalSignificandSize - exponent);
    result = static_cast<uint32_t>(big_result);
  }
  if (static_cast<int32_t>(exponent_bits) < 0) {
    return (0 - result);
  } else {
    return result;
  }
}

void RunOneTruncationTestWithTest(ConvertDToIFunc func,
                                  double from,
                                  int64_t to) {
  int result = (*func)(from);
  CHECK_EQ(static_cast<int>(to), result);
}

// #define NaN and Infinity so that it's possible to cut-and-paste these tests
// directly to a .js file and run them.
#define NaN NAN
#define Infinity INFINITY
#define RunOneTruncationTest(p1, p2) RunOneTruncationTestWithTest(func, p1, p2)

void RunAllTruncationTests(ConvertDToIFunc func) {
  RunOneTruncationTest(0, 0);
  RunOneTruncationTest(0.5, 0);
  RunOneTruncationTest(-0.5, 0);
  RunOneTruncationTest(1.5, 1);
  RunOneTruncationTest(-1.5, -1);
  RunOneTruncationTest(5.5, 5);
  RunOneTruncationTest(-5.0, -5);
  RunOneTruncationTest(NaN, 0);
  RunOneTruncationTest(Infinity, 0);
  RunOneTruncationTest(-NaN, 0);
  RunOneTruncationTest(-Infinity, 0);

  RunOneTruncationTest(4.5036e+15, 0x1635E000);
  RunOneTruncationTest(-4.5036e+15, -372629504);

  RunOneTruncationTest(4503603922337791.0, -1);
  RunOneTruncationTest(-4503603922337791.0, 1);
  RunOneTruncationTest(4503601774854143.0, 2147483647);
  RunOneTruncationTest(-4503601774854143.0, -2147483647);
  RunOneTruncationTest(9007207844675582.0, -2);
  RunOneTruncationTest(-9007207844675582.0, 2);
  RunOneTruncationTest(2.4178527921507624e+24, -536870912);
  RunOneTruncationTest(-2.4178527921507624e+24, 536870912);
  RunOneTruncationTest(2.417853945072267e+24, -536870912);
  RunOneTruncationTest(-2.417853945072267e+24, 536870912);

  RunOneTruncationTest(4.8357055843015248e+24, -1073741824);
  RunOneTruncationTest(-4.8357055843015248e+24, 1073741824);
  RunOneTruncationTest(4.8357078901445341e+24, -1073741824);
  RunOneTruncationTest(-4.8357078901445341e+24, 1073741824);

  RunOneTruncationTest(9.6714111686030497e+24, -2147483648.0);
  RunOneTruncationTest(-9.6714111686030497e+24, -2147483648.0);
  RunOneTruncationTest(9.6714157802890681e+24, -2147483648.0);
  RunOneTruncationTest(-9.6714157802890681e+24, -2147483648.0);
}

#undef NaN
#undef Infinity
#undef RunOneTruncationTest

#define __ assm.

ConvertDToIFunc MakeConvertDToIFuncTrampoline(Isolate* isolate,
                                              Register source_reg,
                                              Register destination_reg) {
  // Allocate an executable page of memory.
  size_t actual_size;
  byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
                                                   &actual_size,
                                                   true));
  CHECK(buffer);
  HandleScope handles(isolate);
  MacroAssembler assm(isolate, buffer, static_cast<int>(actual_size));
  assm.set_allow_stub_calls(false);
  int offset =
    source_reg.is(esp) ? 0 : (HeapNumber::kValueOffset - kSmiTagSize);
  DoubleToIStub stub(source_reg, destination_reg, offset, true);
  byte* start = stub.GetCode(isolate)->instruction_start();

  __ push(ebx);
  __ push(ecx);
  __ push(edx);
  __ push(esi);
  __ push(edi);

  if (!source_reg.is(esp)) {
    __ lea(source_reg, MemOperand(esp, 6 * kPointerSize - offset));
  }

  int param_offset = 7 * kPointerSize;
  // Save registers make sure they don't get clobbered.
  int reg_num = 0;
  for (;reg_num < Register::NumAllocatableRegisters(); ++reg_num) {
    Register reg = Register::from_code(reg_num);
    if (!reg.is(esp) && !reg.is(ebp) && !reg.is(destination_reg)) {
      __ push(reg);
      param_offset += kPointerSize;
    }
  }

  // Re-push the double argument
  __ push(MemOperand(esp, param_offset));
  __ push(MemOperand(esp, param_offset));

  // Call through to the actual stub
  __ call(start, RelocInfo::EXTERNAL_REFERENCE);

  __ add(esp, Immediate(kDoubleSize));

  // Make sure no registers have been unexpectedly clobbered
  for (--reg_num; reg_num >= 0; --reg_num) {
    Register reg = Register::from_code(reg_num);
    if (!reg.is(esp) && !reg.is(ebp) && !reg.is(destination_reg)) {
      __ cmp(reg, MemOperand(esp, 0));
      __ Assert(equal, "register was clobbered");
      __ add(esp, Immediate(kPointerSize));
    }
  }

  __ mov(eax, destination_reg);

  __ pop(edi);
  __ pop(esi);
  __ pop(edx);
  __ pop(ecx);
  __ pop(ebx);

  __ ret(kDoubleSize);

  CodeDesc desc;
  assm.GetCode(&desc);
  return reinterpret_cast<ConvertDToIFunc>(
      reinterpret_cast<intptr_t>(buffer));
}

#undef __

static Isolate* GetIsolateFrom(LocalContext* context) {
  return reinterpret_cast<Isolate*>((*context)->GetIsolate());
}

TEST(ConvertDToI) {
  CcTest::InitializeVM();
  LocalContext context;
  Isolate* isolate = GetIsolateFrom(&context);
  HandleScope scope(isolate);

#if DEBUG
  // Verify that the tests actually work with the C version. In the release
  // code, the compiler optimizes it away because it's all constant, but does it
  // wrong, triggering an assert on gcc.
  RunAllTruncationTests(&ConvertDToICVersion);
#endif

  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esp, eax));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esp, ebx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esp, ecx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esp, edx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esp, edi));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esp, esi));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, eax, eax));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, eax, ebx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, eax, ecx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, eax, edx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, eax, edi));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, eax, esi));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ebx, eax));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ebx, ebx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ebx, ecx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ebx, edx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ebx, edi));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ebx, esi));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ecx, eax));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ecx, ebx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ecx, ecx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ecx, edx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ecx, edi));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ecx, esi));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edx, eax));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edx, ebx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edx, ecx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edx, edx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edx, edi));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edx, esi));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esi, eax));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esi, ebx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esi, ecx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esi, edx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esi, edi));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esi, esi));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edi, eax));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edi, ebx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edi, ecx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edi, edx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edi, edi));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edi, esi));
}
<commit_msg>Fix windows build breakage due to 15645<commit_after>// Copyright 2013 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//     * Neither the name of Google Inc. nor the names of its
//       contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include <stdlib.h>

#include <limits>

#include "v8.h"

#include "cctest.h"
#include "code-stubs.h"
#include "factory.h"
#include "macro-assembler.h"
#include "platform.h"

#if __GNUC__
#define STDCALL  __attribute__((stdcall))
#else
#define STDCALL  __stdcall
#endif

using namespace v8::internal;

typedef int32_t STDCALL ConvertDToIFuncType(double input);
typedef ConvertDToIFuncType* ConvertDToIFunc;

int STDCALL ConvertDToICVersion(double d) {
  Address double_ptr = reinterpret_cast<Address>(&d);
  uint32_t exponent_bits = Memory::uint32_at(double_ptr + kDoubleSize / 2);
  int32_t shifted_mask = static_cast<int32_t>(Double::kExponentMask >> 32);
  int32_t exponent = (((exponent_bits & shifted_mask) >>
                       (Double::kPhysicalSignificandSize - 32)) -
                      HeapNumber::kExponentBias);
  uint32_t unsigned_exponent = static_cast<uint32_t>(exponent);
  int result = 0;
  uint32_t max_exponent =
    static_cast<uint32_t>(Double::kPhysicalSignificandSize);
  if (unsigned_exponent >= max_exponent) {
    if ((exponent - Double::kPhysicalSignificandSize) < 32) {
      result = Memory::uint32_at(double_ptr) <<
        (exponent - Double::kPhysicalSignificandSize);
    }
  } else {
    uint64_t big_result =
        (BitCast<uint64_t>(d) & Double::kSignificandMask) | Double::kHiddenBit;
    big_result = big_result >> (Double::kPhysicalSignificandSize - exponent);
    result = static_cast<uint32_t>(big_result);
  }
  if (static_cast<int32_t>(exponent_bits) < 0) {
    return (0 - result);
  } else {
    return result;
  }
}

void RunOneTruncationTestWithTest(ConvertDToIFunc func,
                                  double from,
                                  double raw) {
  uint64_t to = static_cast<int64_t>(raw);
  int result = (*func)(from);
  CHECK_EQ(static_cast<int>(to), result);
}

// #define NaN and Infinity so that it's possible to cut-and-paste these tests
// directly to a .js file and run them.
#define NaN (OS::nan_value())
#define Infinity (std::numeric_limits<double>::infinity())
#define RunOneTruncationTest(p1, p2) RunOneTruncationTestWithTest(func, p1, p2)

void RunAllTruncationTests(ConvertDToIFunc func) {
  RunOneTruncationTest(0, 0);
  RunOneTruncationTest(0.5, 0);
  RunOneTruncationTest(-0.5, 0);
  RunOneTruncationTest(1.5, 1);
  RunOneTruncationTest(-1.5, -1);
  RunOneTruncationTest(5.5, 5);
  RunOneTruncationTest(-5.0, -5);
  RunOneTruncationTest(NaN, 0);
  RunOneTruncationTest(Infinity, 0);
  RunOneTruncationTest(-NaN, 0);
  RunOneTruncationTest(-Infinity, 0);

  RunOneTruncationTest(4.5036e+15, 0x1635E000);
  RunOneTruncationTest(-4.5036e+15, -372629504);

  RunOneTruncationTest(4503603922337791.0, -1);
  RunOneTruncationTest(-4503603922337791.0, 1);
  RunOneTruncationTest(4503601774854143.0, 2147483647);
  RunOneTruncationTest(-4503601774854143.0, -2147483647);
  RunOneTruncationTest(9007207844675582.0, -2);
  RunOneTruncationTest(-9007207844675582.0, 2);
  RunOneTruncationTest(2.4178527921507624e+24, -536870912);
  RunOneTruncationTest(-2.4178527921507624e+24, 536870912);
  RunOneTruncationTest(2.417853945072267e+24, -536870912);
  RunOneTruncationTest(-2.417853945072267e+24, 536870912);

  RunOneTruncationTest(4.8357055843015248e+24, -1073741824);
  RunOneTruncationTest(-4.8357055843015248e+24, 1073741824);
  RunOneTruncationTest(4.8357078901445341e+24, -1073741824);
  RunOneTruncationTest(-4.8357078901445341e+24, 1073741824);

  RunOneTruncationTest(9.6714111686030497e+24, -2147483648.0);
  RunOneTruncationTest(-9.6714111686030497e+24, -2147483648.0);
  RunOneTruncationTest(9.6714157802890681e+24, -2147483648.0);
  RunOneTruncationTest(-9.6714157802890681e+24, -2147483648.0);
}

#undef NaN
#undef Infinity
#undef RunOneTruncationTest

#define __ assm.

ConvertDToIFunc MakeConvertDToIFuncTrampoline(Isolate* isolate,
                                              Register source_reg,
                                              Register destination_reg) {
  // Allocate an executable page of memory.
  size_t actual_size;
  byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
                                                   &actual_size,
                                                   true));
  CHECK(buffer);
  HandleScope handles(isolate);
  MacroAssembler assm(isolate, buffer, static_cast<int>(actual_size));
  assm.set_allow_stub_calls(false);
  int offset =
    source_reg.is(esp) ? 0 : (HeapNumber::kValueOffset - kSmiTagSize);
  DoubleToIStub stub(source_reg, destination_reg, offset, true);
  byte* start = stub.GetCode(isolate)->instruction_start();

  __ push(ebx);
  __ push(ecx);
  __ push(edx);
  __ push(esi);
  __ push(edi);

  if (!source_reg.is(esp)) {
    __ lea(source_reg, MemOperand(esp, 6 * kPointerSize - offset));
  }

  int param_offset = 7 * kPointerSize;
  // Save registers make sure they don't get clobbered.
  int reg_num = 0;
  for (;reg_num < Register::NumAllocatableRegisters(); ++reg_num) {
    Register reg = Register::from_code(reg_num);
    if (!reg.is(esp) && !reg.is(ebp) && !reg.is(destination_reg)) {
      __ push(reg);
      param_offset += kPointerSize;
    }
  }

  // Re-push the double argument
  __ push(MemOperand(esp, param_offset));
  __ push(MemOperand(esp, param_offset));

  // Call through to the actual stub
  __ call(start, RelocInfo::EXTERNAL_REFERENCE);

  __ add(esp, Immediate(kDoubleSize));

  // Make sure no registers have been unexpectedly clobbered
  for (--reg_num; reg_num >= 0; --reg_num) {
    Register reg = Register::from_code(reg_num);
    if (!reg.is(esp) && !reg.is(ebp) && !reg.is(destination_reg)) {
      __ cmp(reg, MemOperand(esp, 0));
      __ Assert(equal, "register was clobbered");
      __ add(esp, Immediate(kPointerSize));
    }
  }

  __ mov(eax, destination_reg);

  __ pop(edi);
  __ pop(esi);
  __ pop(edx);
  __ pop(ecx);
  __ pop(ebx);

  __ ret(kDoubleSize);

  CodeDesc desc;
  assm.GetCode(&desc);
  return reinterpret_cast<ConvertDToIFunc>(
      reinterpret_cast<intptr_t>(buffer));
}

#undef __

static Isolate* GetIsolateFrom(LocalContext* context) {
  return reinterpret_cast<Isolate*>((*context)->GetIsolate());
}

TEST(ConvertDToI) {
  CcTest::InitializeVM();
  LocalContext context;
  Isolate* isolate = GetIsolateFrom(&context);
  HandleScope scope(isolate);

#if DEBUG
  // Verify that the tests actually work with the C version. In the release
  // code, the compiler optimizes it away because it's all constant, but does it
  // wrong, triggering an assert on gcc.
  RunAllTruncationTests(&ConvertDToICVersion);
#endif

  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esp, eax));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esp, ebx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esp, ecx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esp, edx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esp, edi));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esp, esi));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, eax, eax));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, eax, ebx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, eax, ecx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, eax, edx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, eax, edi));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, eax, esi));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ebx, eax));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ebx, ebx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ebx, ecx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ebx, edx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ebx, edi));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ebx, esi));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ecx, eax));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ecx, ebx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ecx, ecx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ecx, edx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ecx, edi));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, ecx, esi));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edx, eax));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edx, ebx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edx, ecx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edx, edx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edx, edi));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edx, esi));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esi, eax));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esi, ebx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esi, ecx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esi, edx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esi, edi));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, esi, esi));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edi, eax));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edi, ebx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edi, ecx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edi, edx));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edi, edi));
  RunAllTruncationTests(MakeConvertDToIFuncTrampoline(isolate, edi, esi));
}
<|endoftext|>