<commit_before>#include <check.h>
#include <stdint.h>
#include "queue.h"
#include <stdio.h>
#include <stdlib.h>

typedef struct test_t {
    int i;
    char bytes[8];
};

QUEUE_DECLARE(test_t, 10);
QUEUE_DEFINE(test_t);
QUEUE_DECLARE(int, 256);
QUEUE_DEFINE(int);

START_TEST (test_struct_element)
{
    QUEUE_TYPE(test_t) queue;
    QUEUE_INIT(test_t, &queue);
    int length = QUEUE_LENGTH(test_t, &queue);
    fail_unless(length == 0, "expected queue length of 0 but got %d", length);
    test_t original_value = {42, "abcdefg"};
    bool success = QUEUE_PUSH(test_t, &queue, original_value);
    fail_unless(success);
    fail_unless(QUEUE_LENGTH(test_t, &queue) == 1,
            "expected queue length of 1 but got %d", length);

    test_t value = QUEUE_POP(test_t, &queue);
    fail_unless(value.i == original_value.i);
    fail_unless(!strncmp(value.bytes, original_value.bytes, 8));
}
END_TEST

START_TEST (test_push)
{
    QUEUE_TYPE(uint8_t) queue;
    QUEUE_INIT(uint8_t, &queue);
    int length = QUEUE_LENGTH(uint8_t, &queue);
    fail_unless(length == 0, "expected queue length of 0 but got %d", length);
    bool success = QUEUE_PUSH(uint8_t, &queue, 0xEF);
    fail_unless(success);
    fail_unless(QUEUE_LENGTH(uint8_t, &queue) == 1,
            "expected queue length of 1 but got %d", length);
}
END_TEST

START_TEST (test_pop)
{
    QUEUE_TYPE(uint8_t) queue;
    QUEUE_INIT(uint8_t, &queue);
    uint8_t original_value = 0xEF;
    QUEUE_PUSH(uint8_t, &queue, original_value);
    uint8_t value = QUEUE_POP(uint8_t, &queue);
    fail_unless(value == original_value);
}
END_TEST

START_TEST (test_fill_er_up)
{
    QUEUE_TYPE(uint8_t) queue;
    QUEUE_INIT(uint8_t, &queue);
    for(int i = 0; i < QUEUE_MAX_LENGTH(uint8_t); i++) {
        bool success = QUEUE_PUSH(uint8_t, &queue, (uint8_t) (i % 255));
        fail_unless(success, "wasn't able to add the %dth element", i + 1);
    }

    for(int i = 0; i < QUEUE_MAX_LENGTH(uint8_t); i++) {
        uint8_t value = QUEUE_POP(uint8_t, &queue);
        if(i < QUEUE_MAX_LENGTH(uint8_t) - 1) {
            fail_unless(!QUEUE_EMPTY(uint8_t, &queue),
                    "didn't expect queue to be empty on %dth iteration", i + 1);
        }
        uint8_t expected = i % 255;
        fail_unless(value == expected,
                "expected %d but got %d out of the queue", expected, value);
    }
    fail_unless(QUEUE_EMPTY(uint8_t, &queue));
}
END_TEST

START_TEST (test_sliding_window)
{
    srand(42);
    QUEUE_TYPE(int) queue;
    QUEUE_INIT(int, &queue);
    for(int i = 0; i < 100; i++) {
        int elementsToAdd = 64 + rand() % 32;
        for(int i = 0; i < elementsToAdd; i++) {
            bool success = QUEUE_PUSH(int, &queue, i);
            fail_unless(success, "wasn't able to add the %dth element", i + 1);
        }
        printf("Length: %d\n", QUEUE_LENGTH(int, &queue));

        int expectedValue = 0;
        while(!QUEUE_EMPTY(int, &queue)) {
            int value = QUEUE_POP(int, &queue);
            fail_unless(value == expectedValue);
            ++expectedValue;
        }
    }
    fail_unless(QUEUE_EMPTY(int, &queue));
}
END_TEST

START_TEST (test_length)
{
    QUEUE_TYPE(uint8_t) queue;
    QUEUE_INIT(uint8_t, &queue);
    fail_unless(QUEUE_LENGTH(uint8_t, &queue) == 0);
    for(int i = 0; i < QUEUE_MAX_LENGTH(uint8_t); i++) {
        QUEUE_PUSH(uint8_t, &queue,  (uint8_t) (i % 255));
        if(i == QUEUE_MAX_LENGTH(uint8_t) - 1) {
            break;
        }
        fail_unless(QUEUE_LENGTH(uint8_t, &queue) == i + 1,
                "expected length of %d but found %d", i + 1,
                QUEUE_LENGTH(uint8_t, &queue));
    }

    for(int i = 0; i < QUEUE_MAX_LENGTH(uint8_t); i++) {
        QUEUE_POP(uint8_t, &queue);
        fail_unless(QUEUE_LENGTH(uint8_t, &queue) == QUEUE_MAX_LENGTH(uint8_t) - i - 1);
    }
    fail_unless(QUEUE_LENGTH(uint8_t, &queue) == 0);

    for(int i = 0; i < QUEUE_MAX_LENGTH(uint8_t); i++) {
        QUEUE_PUSH(uint8_t, &queue, (uint8_t) (i % 255));
        fail_unless(QUEUE_LENGTH(uint8_t, &queue) == i + 1,
                "expected length of %d but found %d", i + 1,
                QUEUE_LENGTH(uint8_t, &queue));
    }

    for(int i = 0; i < QUEUE_MAX_LENGTH(uint8_t) / 2; i++) {
        QUEUE_POP(uint8_t, &queue);
        fail_unless(QUEUE_LENGTH(uint8_t, &queue) == QUEUE_MAX_LENGTH(uint8_t) - i - 1);
    }

    for(int i = 0; i < QUEUE_MAX_LENGTH(uint8_t) / 2; i++) {
        QUEUE_PUSH(uint8_t, &queue, (uint8_t) (i % 255));
        int expectedLength =  i + (QUEUE_MAX_LENGTH(uint8_t) / 2) + 1;
        fail_unless(QUEUE_LENGTH(uint8_t, &queue) == expectedLength,
                "expected length of %d but found %d", expectedLength,
                QUEUE_LENGTH(uint8_t, &queue));
    }
}
END_TEST

START_TEST (test_available)
{
    QUEUE_TYPE(uint8_t) queue;
    QUEUE_INIT(uint8_t, &queue);
    fail_unless(QUEUE_AVAILABLE(uint8_t, &queue) == QUEUE_MAX_LENGTH(uint8_t));
    for(int i = 0; i < QUEUE_MAX_LENGTH(uint8_t); i++) {
        QUEUE_PUSH(uint8_t, &queue,  (uint8_t) (i % 255));
        if(i == QUEUE_MAX_LENGTH(uint8_t) - 1) {
            break;
        }
        fail_unless(QUEUE_AVAILABLE(uint8_t, &queue) ==
                    QUEUE_MAX_LENGTH(uint8_t) - i - 1,
                "expected %d available but found %d",
                QUEUE_MAX_LENGTH(uint8_t) - i - 1,
                QUEUE_AVAILABLE(uint8_t, &queue));
    }

    for(int i = 0; i < QUEUE_MAX_LENGTH(uint8_t); i++) {
        QUEUE_POP(uint8_t, &queue);
        fail_unless(QUEUE_AVAILABLE(uint8_t, &queue) == i + 1);
    }
    fail_unless(QUEUE_AVAILABLE(uint8_t, &queue) == QUEUE_MAX_LENGTH(uint8_t));
}
END_TEST

START_TEST (test_snapshot)
{
    QUEUE_TYPE(uint8_t) queue;
    QUEUE_INIT(uint8_t, &queue);
    uint8_t expected[QUEUE_MAX_LENGTH(uint8_t)];
    for(int i = 0; i < QUEUE_MAX_LENGTH(uint8_t); i++) {
        uint8_t value = i % 255;
        QUEUE_PUSH(uint8_t, &queue, value);
        expected[i] = value;
    }

    uint8_t snapshot[QUEUE_MAX_LENGTH(uint8_t)];
    QUEUE_SNAPSHOT(uint8_t, &queue, snapshot);
    for(int i = 0; i < QUEUE_MAX_LENGTH(uint8_t); i++) {
        fail_unless(snapshot[i] == expected[i],
                "expected %d but found %d", expected[i], snapshot[i]);
    }
}
END_TEST

Suite* suite(void) {
    Suite* s = suite_create("queue");
    TCase *tc_core = tcase_create("core");
    tcase_add_test(tc_core, test_push);
    tcase_add_test(tc_core, test_pop);
    tcase_add_test(tc_core, test_fill_er_up);
    tcase_add_test(tc_core, test_sliding_window);
    tcase_add_test(tc_core, test_length);
    tcase_add_test(tc_core, test_available);
    tcase_add_test(tc_core, test_snapshot);
    tcase_add_test(tc_core, test_struct_element);
    suite_add_tcase(s, tc_core);

    return s;
}

int main(void) {
    int numberFailed;
    Suite* s = suite();
    SRunner *sr = srunner_create(s);
    // Don't fork so we can actually use gdb
    srunner_set_fork_status(sr, CK_NOFORK);
    srunner_run_all(sr, CK_NORMAL);
    numberFailed = srunner_ntests_failed(sr);
    srunner_free(sr);
    return (numberFailed == 0) ? 0 : 1;
}
<commit_msg>Remove stray printf from a test case.<commit_after>#include <check.h>
#include <stdint.h>
#include "queue.h"
#include <stdio.h>
#include <stdlib.h>

typedef struct test_t {
    int i;
    char bytes[8];
};

QUEUE_DECLARE(test_t, 10);
QUEUE_DEFINE(test_t);
QUEUE_DECLARE(int, 256);
QUEUE_DEFINE(int);

START_TEST (test_struct_element)
{
    QUEUE_TYPE(test_t) queue;
    QUEUE_INIT(test_t, &queue);
    int length = QUEUE_LENGTH(test_t, &queue);
    fail_unless(length == 0, "expected queue length of 0 but got %d", length);
    test_t original_value = {42, "abcdefg"};
    bool success = QUEUE_PUSH(test_t, &queue, original_value);
    fail_unless(success);
    fail_unless(QUEUE_LENGTH(test_t, &queue) == 1,
            "expected queue length of 1 but got %d", length);

    test_t value = QUEUE_POP(test_t, &queue);
    fail_unless(value.i == original_value.i);
    fail_unless(!strncmp(value.bytes, original_value.bytes, 8));
}
END_TEST

START_TEST (test_push)
{
    QUEUE_TYPE(uint8_t) queue;
    QUEUE_INIT(uint8_t, &queue);
    int length = QUEUE_LENGTH(uint8_t, &queue);
    fail_unless(length == 0, "expected queue length of 0 but got %d", length);
    bool success = QUEUE_PUSH(uint8_t, &queue, 0xEF);
    fail_unless(success);
    fail_unless(QUEUE_LENGTH(uint8_t, &queue) == 1,
            "expected queue length of 1 but got %d", length);
}
END_TEST

START_TEST (test_pop)
{
    QUEUE_TYPE(uint8_t) queue;
    QUEUE_INIT(uint8_t, &queue);
    uint8_t original_value = 0xEF;
    QUEUE_PUSH(uint8_t, &queue, original_value);
    uint8_t value = QUEUE_POP(uint8_t, &queue);
    fail_unless(value == original_value);
}
END_TEST

START_TEST (test_fill_er_up)
{
    QUEUE_TYPE(uint8_t) queue;
    QUEUE_INIT(uint8_t, &queue);
    for(int i = 0; i < QUEUE_MAX_LENGTH(uint8_t); i++) {
        bool success = QUEUE_PUSH(uint8_t, &queue, (uint8_t) (i % 255));
        fail_unless(success, "wasn't able to add the %dth element", i + 1);
    }

    for(int i = 0; i < QUEUE_MAX_LENGTH(uint8_t); i++) {
        uint8_t value = QUEUE_POP(uint8_t, &queue);
        if(i < QUEUE_MAX_LENGTH(uint8_t) - 1) {
            fail_unless(!QUEUE_EMPTY(uint8_t, &queue),
                    "didn't expect queue to be empty on %dth iteration", i + 1);
        }
        uint8_t expected = i % 255;
        fail_unless(value == expected,
                "expected %d but got %d out of the queue", expected, value);
    }
    fail_unless(QUEUE_EMPTY(uint8_t, &queue));
}
END_TEST

START_TEST (test_sliding_window)
{
    srand(42);
    QUEUE_TYPE(int) queue;
    QUEUE_INIT(int, &queue);
    for(int i = 0; i < 100; i++) {
        int elementsToAdd = 64 + rand() % 32;
        for(int i = 0; i < elementsToAdd; i++) {
            bool success = QUEUE_PUSH(int, &queue, i);
            fail_unless(success, "wasn't able to add the %dth element", i + 1);
        }

        int expectedValue = 0;
        while(!QUEUE_EMPTY(int, &queue)) {
            int value = QUEUE_POP(int, &queue);
            fail_unless(value == expectedValue);
            ++expectedValue;
        }
    }
    fail_unless(QUEUE_EMPTY(int, &queue));
}
END_TEST

START_TEST (test_length)
{
    QUEUE_TYPE(uint8_t) queue;
    QUEUE_INIT(uint8_t, &queue);
    fail_unless(QUEUE_LENGTH(uint8_t, &queue) == 0);
    for(int i = 0; i < QUEUE_MAX_LENGTH(uint8_t); i++) {
        QUEUE_PUSH(uint8_t, &queue,  (uint8_t) (i % 255));
        if(i == QUEUE_MAX_LENGTH(uint8_t) - 1) {
            break;
        }
        fail_unless(QUEUE_LENGTH(uint8_t, &queue) == i + 1,
                "expected length of %d but found %d", i + 1,
                QUEUE_LENGTH(uint8_t, &queue));
    }

    for(int i = 0; i < QUEUE_MAX_LENGTH(uint8_t); i++) {
        QUEUE_POP(uint8_t, &queue);
        fail_unless(QUEUE_LENGTH(uint8_t, &queue) == QUEUE_MAX_LENGTH(uint8_t) - i - 1);
    }
    fail_unless(QUEUE_LENGTH(uint8_t, &queue) == 0);

    for(int i = 0; i < QUEUE_MAX_LENGTH(uint8_t); i++) {
        QUEUE_PUSH(uint8_t, &queue, (uint8_t) (i % 255));
        fail_unless(QUEUE_LENGTH(uint8_t, &queue) == i + 1,
                "expected length of %d but found %d", i + 1,
                QUEUE_LENGTH(uint8_t, &queue));
    }

    for(int i = 0; i < QUEUE_MAX_LENGTH(uint8_t) / 2; i++) {
        QUEUE_POP(uint8_t, &queue);
        fail_unless(QUEUE_LENGTH(uint8_t, &queue) == QUEUE_MAX_LENGTH(uint8_t) - i - 1);
    }

    for(int i = 0; i < QUEUE_MAX_LENGTH(uint8_t) / 2; i++) {
        QUEUE_PUSH(uint8_t, &queue, (uint8_t) (i % 255));
        int expectedLength =  i + (QUEUE_MAX_LENGTH(uint8_t) / 2) + 1;
        fail_unless(QUEUE_LENGTH(uint8_t, &queue) == expectedLength,
                "expected length of %d but found %d", expectedLength,
                QUEUE_LENGTH(uint8_t, &queue));
    }
}
END_TEST

START_TEST (test_available)
{
    QUEUE_TYPE(uint8_t) queue;
    QUEUE_INIT(uint8_t, &queue);
    fail_unless(QUEUE_AVAILABLE(uint8_t, &queue) == QUEUE_MAX_LENGTH(uint8_t));
    for(int i = 0; i < QUEUE_MAX_LENGTH(uint8_t); i++) {
        QUEUE_PUSH(uint8_t, &queue,  (uint8_t) (i % 255));
        if(i == QUEUE_MAX_LENGTH(uint8_t) - 1) {
            break;
        }
        fail_unless(QUEUE_AVAILABLE(uint8_t, &queue) ==
                    QUEUE_MAX_LENGTH(uint8_t) - i - 1,
                "expected %d available but found %d",
                QUEUE_MAX_LENGTH(uint8_t) - i - 1,
                QUEUE_AVAILABLE(uint8_t, &queue));
    }

    for(int i = 0; i < QUEUE_MAX_LENGTH(uint8_t); i++) {
        QUEUE_POP(uint8_t, &queue);
        fail_unless(QUEUE_AVAILABLE(uint8_t, &queue) == i + 1);
    }
    fail_unless(QUEUE_AVAILABLE(uint8_t, &queue) == QUEUE_MAX_LENGTH(uint8_t));
}
END_TEST

START_TEST (test_snapshot)
{
    QUEUE_TYPE(uint8_t) queue;
    QUEUE_INIT(uint8_t, &queue);
    uint8_t expected[QUEUE_MAX_LENGTH(uint8_t)];
    for(int i = 0; i < QUEUE_MAX_LENGTH(uint8_t); i++) {
        uint8_t value = i % 255;
        QUEUE_PUSH(uint8_t, &queue, value);
        expected[i] = value;
    }

    uint8_t snapshot[QUEUE_MAX_LENGTH(uint8_t)];
    QUEUE_SNAPSHOT(uint8_t, &queue, snapshot);
    for(int i = 0; i < QUEUE_MAX_LENGTH(uint8_t); i++) {
        fail_unless(snapshot[i] == expected[i],
                "expected %d but found %d", expected[i], snapshot[i]);
    }
}
END_TEST

Suite* suite(void) {
    Suite* s = suite_create("queue");
    TCase *tc_core = tcase_create("core");
    tcase_add_test(tc_core, test_push);
    tcase_add_test(tc_core, test_pop);
    tcase_add_test(tc_core, test_fill_er_up);
    tcase_add_test(tc_core, test_sliding_window);
    tcase_add_test(tc_core, test_length);
    tcase_add_test(tc_core, test_available);
    tcase_add_test(tc_core, test_snapshot);
    tcase_add_test(tc_core, test_struct_element);
    suite_add_tcase(s, tc_core);

    return s;
}

int main(void) {
    int numberFailed;
    Suite* s = suite();
    SRunner *sr = srunner_create(s);
    // Don't fork so we can actually use gdb
    srunner_set_fork_status(sr, CK_NOFORK);
    srunner_run_all(sr, CK_NORMAL);
    numberFailed = srunner_ntests_failed(sr);
    srunner_free(sr);
    return (numberFailed == 0) ? 0 : 1;
}
<|endoftext|>


<commit_before>#ifndef STAN_COMMAND_STANC_HELPER_HPP
#define STAN_COMMAND_STANC_HELPER_HPP

#include <stan/version.hpp>
#include <stan/lang/compiler.hpp>
#include <stan/io/cmd_line.hpp>
#include <exception>
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <string>

void print_version(std::ostream* out_stream) {
  if (!out_stream) return;
  *out_stream << "stanc version "
              << stan::MAJOR_VERSION
              << "."
              << stan::MINOR_VERSION
              << "."
              << stan::PATCH_VERSION
              << std::endl;
}

/**
 * Prints the Stan compiler (stanc) help.
 */
void print_stanc_help(std::ostream* out_stream) {
  using stan::io::print_help_option;

  if (!out_stream) return;

  *out_stream << std::endl;
  print_version(out_stream);
  *out_stream << std::endl;

  *out_stream << "USAGE:  " << "stanc [options] <model_file>" << std::endl;
  *out_stream << std::endl;

  *out_stream << "OPTIONS:" << std::endl;
  *out_stream << std::endl;

  print_help_option(out_stream, "help", "", "Display this information");

  print_help_option(out_stream, "version", "", "Display stanc version number");

  print_help_option(out_stream, "name", "string",
                    "Model name",
                    "default = \"$model_filename_model\"");

  print_help_option(out_stream, "o", "file",
                    "Output file for generated C++ code",
                    "default = \"$name.cpp\"");
}

void delete_file(std::ostream* err_stream,
                 const std::string& file_name) {
  int deleted = std::remove(file_name.c_str());
  if (deleted != 0 && file_name.size() > 0)
    if (err_stream)
      *err_stream << "Could not remove output file=" << file_name
                  << std::endl;
}

int stanc_helper(int argc, const char* argv[],
                 std::ostream* out_stream, std::ostream* err_stream) {
  static const int SUCCESS_RC = 0;
  static const int EXCEPTION_RC = -1;
  static const int PARSE_FAIL_RC = -2;
  static const int INVALID_ARGUMENT_RC = -3;

  std::string out_file_name;  // declare outside of try to delete in catch

  try {
    stan::io::cmd_line cmd(argc, argv);

    if (cmd.has_flag("help")) {
      print_stanc_help(out_stream);
      return SUCCESS_RC;
    }

    if (cmd.has_flag("version")) {
      print_version(out_stream);
      return SUCCESS_RC;
    }

    if (cmd.bare_size() != 1) {
      std::string msg("Require model file as argument. ");
      throw std::invalid_argument(msg);
    }
    std::string in_file_name;
    cmd.bare(0, in_file_name);
    std::fstream in(in_file_name.c_str());

    std::string model_name;
    if (cmd.has_key("name")) {
      cmd.val("name", model_name);
    } else {
      size_t slashInd = in_file_name.rfind('/');
      size_t ptInd = in_file_name.rfind('.');
      if (ptInd == std::string::npos)
        ptInd = in_file_name.length();
      if (slashInd == std::string::npos) {
        slashInd = in_file_name.rfind('\\');
      }
      if (slashInd == std::string::npos) {
        slashInd = 0;
      } else {
        slashInd++;
      }
      model_name = in_file_name.substr(slashInd, ptInd - slashInd) + "_model";
      for (std::string::iterator strIt = model_name.begin();
           strIt != model_name.end(); strIt++) {
        if (!isalnum(*strIt) && *strIt != '_') {
          *strIt = '_';
        }
      }
    }

    if (cmd.has_key("o")) {
      cmd.val("o", out_file_name);
    } else {
      out_file_name = model_name;
      out_file_name += ".cpp";
    }

    if (!isalpha(model_name[0]) && model_name[0] != '_') {
      std::string msg("model_name must not start with a "
                      "number or symbol other than _");
      throw std::invalid_argument(msg);
    }
    for (std::string::iterator strIt = model_name.begin();
         strIt != model_name.end(); strIt++) {
      if (!isalnum(*strIt) && *strIt != '_') {
        std::string msg("model_name must contain only letters, numbers and _");
        throw std::invalid_argument(msg);
      }
    }

    stan::lang::program prog;

    std::fstream out(out_file_name.c_str(),
                     std::fstream::out);
    if (out_stream) {
      *out_stream << "Model name=" << model_name << std::endl;
      *out_stream << "Input file=" << in_file_name << std::endl;
      *out_stream << "Output file=" << out_file_name << std::endl;
    }

    bool valid_model
      = stan::lang::compile(err_stream, in, out, model_name, in_file_name);

    out.close();
    if (!valid_model) {
      if (err_stream)
        *err_stream << "PARSING FAILED." << std::endl;
      // FIXME: how to remove triple cut-and-paste?
      delete_file(out_stream, out_file_name);
      return PARSE_FAIL_RC;
    }
  } catch (const std::invalid_argument& e) {
    if (err_stream) {
      *err_stream << std::endl
                  << e.what()
                  << std::endl;
      delete_file(out_stream, out_file_name);
    }
    return INVALID_ARGUMENT_RC;
  } catch (const std::exception& e) {
    if (err_stream) {
      *err_stream << std::endl
                  << e.what()
                  << std::endl;
    }
    delete_file(out_stream, out_file_name);
    return EXCEPTION_RC;
  }
  return SUCCESS_RC;
}

#endif
<commit_msg>Remove unused arg in_file_name in stanc_helper<commit_after>#ifndef STAN_COMMAND_STANC_HELPER_HPP
#define STAN_COMMAND_STANC_HELPER_HPP

#include <stan/version.hpp>
#include <stan/lang/compiler.hpp>
#include <stan/io/cmd_line.hpp>
#include <exception>
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <string>

void print_version(std::ostream* out_stream) {
  if (!out_stream) return;
  *out_stream << "stanc version "
              << stan::MAJOR_VERSION
              << "."
              << stan::MINOR_VERSION
              << "."
              << stan::PATCH_VERSION
              << std::endl;
}

/**
 * Prints the Stan compiler (stanc) help.
 */
void print_stanc_help(std::ostream* out_stream) {
  using stan::io::print_help_option;

  if (!out_stream) return;

  *out_stream << std::endl;
  print_version(out_stream);
  *out_stream << std::endl;

  *out_stream << "USAGE:  " << "stanc [options] <model_file>" << std::endl;
  *out_stream << std::endl;

  *out_stream << "OPTIONS:" << std::endl;
  *out_stream << std::endl;

  print_help_option(out_stream, "help", "", "Display this information");

  print_help_option(out_stream, "version", "", "Display stanc version number");

  print_help_option(out_stream, "name", "string",
                    "Model name",
                    "default = \"$model_filename_model\"");

  print_help_option(out_stream, "o", "file",
                    "Output file for generated C++ code",
                    "default = \"$name.cpp\"");
}

void delete_file(std::ostream* err_stream,
                 const std::string& file_name) {
  int deleted = std::remove(file_name.c_str());
  if (deleted != 0 && file_name.size() > 0)
    if (err_stream)
      *err_stream << "Could not remove output file=" << file_name
                  << std::endl;
}

int stanc_helper(int argc, const char* argv[],
                 std::ostream* out_stream, std::ostream* err_stream) {
  static const int SUCCESS_RC = 0;
  static const int EXCEPTION_RC = -1;
  static const int PARSE_FAIL_RC = -2;
  static const int INVALID_ARGUMENT_RC = -3;

  std::string out_file_name;  // declare outside of try to delete in catch

  try {
    stan::io::cmd_line cmd(argc, argv);

    if (cmd.has_flag("help")) {
      print_stanc_help(out_stream);
      return SUCCESS_RC;
    }

    if (cmd.has_flag("version")) {
      print_version(out_stream);
      return SUCCESS_RC;
    }

    if (cmd.bare_size() != 1) {
      std::string msg("Require model file as argument. ");
      throw std::invalid_argument(msg);
    }
    std::string in_file_name;
    cmd.bare(0, in_file_name);
    std::fstream in(in_file_name.c_str());

    std::string model_name;
    if (cmd.has_key("name")) {
      cmd.val("name", model_name);
    } else {
      size_t slashInd = in_file_name.rfind('/');
      size_t ptInd = in_file_name.rfind('.');
      if (ptInd == std::string::npos)
        ptInd = in_file_name.length();
      if (slashInd == std::string::npos) {
        slashInd = in_file_name.rfind('\\');
      }
      if (slashInd == std::string::npos) {
        slashInd = 0;
      } else {
        slashInd++;
      }
      model_name = in_file_name.substr(slashInd, ptInd - slashInd) + "_model";
      for (std::string::iterator strIt = model_name.begin();
           strIt != model_name.end(); strIt++) {
        if (!isalnum(*strIt) && *strIt != '_') {
          *strIt = '_';
        }
      }
    }

    if (cmd.has_key("o")) {
      cmd.val("o", out_file_name);
    } else {
      out_file_name = model_name;
      out_file_name += ".cpp";
    }

    if (!isalpha(model_name[0]) && model_name[0] != '_') {
      std::string msg("model_name must not start with a "
                      "number or symbol other than _");
      throw std::invalid_argument(msg);
    }
    for (std::string::iterator strIt = model_name.begin();
         strIt != model_name.end(); strIt++) {
      if (!isalnum(*strIt) && *strIt != '_') {
        std::string msg("model_name must contain only letters, numbers and _");
        throw std::invalid_argument(msg);
      }
    }

    stan::lang::program prog;

    std::fstream out(out_file_name.c_str(),
                     std::fstream::out);
    if (out_stream) {
      *out_stream << "Model name=" << model_name << std::endl;
      *out_stream << "Input file=" << in_file_name << std::endl;
      *out_stream << "Output file=" << out_file_name << std::endl;
    }

    bool valid_model
      = stan::lang::compile(err_stream, in, out, model_name);

    out.close();
    if (!valid_model) {
      if (err_stream)
        *err_stream << "PARSING FAILED." << std::endl;
      // FIXME: how to remove triple cut-and-paste?
      delete_file(out_stream, out_file_name);
      return PARSE_FAIL_RC;
    }
  } catch (const std::invalid_argument& e) {
    if (err_stream) {
      *err_stream << std::endl
                  << e.what()
                  << std::endl;
      delete_file(out_stream, out_file_name);
    }
    return INVALID_ARGUMENT_RC;
  } catch (const std::exception& e) {
    if (err_stream) {
      *err_stream << std::endl
                  << e.what()
                  << std::endl;
    }
    delete_file(out_stream, out_file_name);
    return EXCEPTION_RC;
  }
  return SUCCESS_RC;
}

#endif
<|endoftext|>


<commit_before>/*
 * File:   utility_tests.cpp
 * Author: mhill
 *
 * Created on October 23, 2014, 10:16 PM
 */

#include <cstdlib>
#include "catch.hpp"
#include "config.h"

using namespace std;
using namespace alpr;

Config get_config(std::string countries)
{
  Config config(countries, "", "");
  return config;
}

TEST_CASE( "Loading Countries", "[Config]" ) {

  REQUIRE( get_config("us,eu").loaded_countries.size() == 2 );

  REQUIRE( get_config("us").loaded_countries.size() == 1 );

  REQUIRE( get_config("eu").loaded_countries.size() == 1 );

  REQUIRE( get_config("us, eu").loaded_countries.size() == 2 );

  REQUIRE( get_config("us, eu, au, kr").loaded_countries.size() == 4 );

  REQUIRE( get_config("us , eu").loaded_countries.size() == 2 );

  REQUIRE( get_config("us,eu,").loaded_countries.size() == 2 );

  REQUIRE( get_config(",,us,eu,").loaded_countries.size() == 2 );

}

//TEST_CASE( "Modifying Countries", "[Config]" )
//{
//  Config config("us,eu", "/etc/openalpr/openalpr.conf", "/usr/share/openalpr/runtime_data/");
//
//  REQUIRE(config.ocrLanguage == "lus");
//
//  config.setCountry("eu");
//  REQUIRE(config.ocrLanguage == "leu");
//
//  config.setCountry("us");
//  REQUIRE(config.ocrLanguage == "lus");
//}<commit_msg>Removed unit tests that required openalpr to be installed<commit_after>/*
 * File:   utility_tests.cpp
 * Author: mhill
 *
 * Created on October 23, 2014, 10:16 PM
 */

#include <cstdlib>
#include "catch.hpp"
#include "config.h"

using namespace std;
using namespace alpr;

Config get_config(std::string countries)
{
  Config config(countries, "", "");
  return config;
}

//TEST_CASE( "Loading Countries", "[Config]" ) {
//
//  REQUIRE( get_config("us,eu").loaded_countries.size() == 2 );
//
//  REQUIRE( get_config("us").loaded_countries.size() == 1 );
//
//  REQUIRE( get_config("eu").loaded_countries.size() == 1 );
//
//  REQUIRE( get_config("us, eu").loaded_countries.size() == 2 );
//
//  REQUIRE( get_config("us, eu, au, kr").loaded_countries.size() == 4 );
//
//  REQUIRE( get_config("us , eu").loaded_countries.size() == 2 );
//
//  REQUIRE( get_config("us,eu,").loaded_countries.size() == 2 );
//
//  REQUIRE( get_config(",,us,eu,").loaded_countries.size() == 2 );
//
//}

//TEST_CASE( "Modifying Countries", "[Config]" )
//{
//  Config config("us,eu", "/etc/openalpr/openalpr.conf", "/usr/share/openalpr/runtime_data/");
//
//  REQUIRE(config.ocrLanguage == "lus");
//
//  config.setCountry("eu");
//  REQUIRE(config.ocrLanguage == "leu");
//
//  config.setCountry("us");
//  REQUIRE(config.ocrLanguage == "lus");
//}<|endoftext|>


<commit_before>/*
* (C) 2017 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include "test_runner.h"
#include "tests.h"

#include <botan/version.h>
#include <botan/rotate.h>
#include <botan/loadstor.h>

namespace Botan_Tests {

Test_Runner::Test_Runner(std::ostream& out) : m_output(out) {}

namespace {

/*
* This is a fast, simple, deterministic PRNG that's used for running
* the tests. It is not intended to be cryptographically secure.
*/
class Testsuite_RNG final : public Botan::RandomNumberGenerator
   {
   public:
      std::string name() const override { return "Testsuite_RNG"; }

      void clear() override
         {
         m_a = m_b = m_c = m_d = 0;
         }

      void add_entropy(const uint8_t data[], size_t len) override
         {
         for(size_t i = 0; i != len; ++i)
            {
            m_a ^= data[i];
            m_b ^= i;
            mix();
            }
         }

      bool is_seeded() const override
         {
         return true;
         }

      void randomize(uint8_t out[], size_t len) override
         {
         for(size_t i = 0; i != len; ++i)
            {
            out[i] = static_cast<uint8_t>(m_a);
            mix();
            }
         }

      Testsuite_RNG(const std::string& drbg_seed, size_t test_counter = 0)
         {
         m_d = static_cast<uint32_t>(test_counter);

         add_entropy(reinterpret_cast<const uint8_t*>(drbg_seed.data()),
                     drbg_seed.size());
         }
   private:
      void mix()
         {
         const size_t ROUNDS = 3;

         for(size_t i = 0; i != ROUNDS; ++i)
            {
            m_a += i;

            m_a = Botan::rotl<9>(m_a);
            m_b ^= m_a;
            m_d ^= m_c;

            m_a += m_d;
            m_c += m_b;
            m_c = Botan::rotl<23>(m_c);
            }
         }

      uint32_t m_a = 0, m_b = 0, m_c = 0, m_d = 0;
   };

}

int Test_Runner::run(const std::vector<std::string>& requested_tests,
                     const std::string& data_dir,
                     const std::string& pkcs11_lib,
                     const std::string& provider,
                     bool log_success,
                     bool run_online_tests,
                     bool run_long_tests,
                     const std::string& drbg_seed,
                     size_t runs)
   {
   std::vector<std::string> req = requested_tests;

   if(req.empty())
      {
      /*
      If nothing was requested on the command line, run everything. First
      run the "essentials" to smoke test, then everything else in
      alphabetical order.
      */
      req = {"block", "stream", "hash", "mac", "modes", "aead"
             "kdf", "pbkdf", "hmac_drbg", "util"
      };

      std::set<std::string> all_others = Botan_Tests::Test::registered_tests();

      if(pkcs11_lib.empty())
         {
         // do not run pkcs11 tests by default unless pkcs11-lib set
         for(std::set<std::string>::iterator iter = all_others.begin(); iter != all_others.end();)
            {
            if((*iter).find("pkcs11") != std::string::npos)
               {
               iter = all_others.erase(iter);
               }
            else
               {
               ++iter;
               }
            }
         }

      for(auto f : req)
         {
         all_others.erase(f);
         }

      req.insert(req.end(), all_others.begin(), all_others.end());
      }
   else if(req.size() == 1 && req.at(0) == "pkcs11")
      {
      req = {"pkcs11-manage", "pkcs11-module", "pkcs11-slot", "pkcs11-session", "pkcs11-object", "pkcs11-rsa",
             "pkcs11-ecdsa", "pkcs11-ecdh", "pkcs11-rng", "pkcs11-x509"
      };
      }
   else
      {
      std::set<std::string> all = Botan_Tests::Test::registered_tests();
      for(auto const& r : req)
         {
         if(all.find(r) == all.end())
            {
            throw Botan_Tests::Test_Error("Unknown test suite: " + r);
            }
         }
      }

   output() << "Testing " << Botan::version_string() << "\n";
   output() << "Starting tests";

   if(!pkcs11_lib.empty())
      {
      output() << " pkcs11 library:" << pkcs11_lib;
      }

   Botan_Tests::Provider_Filter pf;
   if(!provider.empty())
      {
      output() << " provider:" << provider;
      pf.set(provider);
      }

   std::vector<uint8_t> seed = Botan::hex_decode(drbg_seed);
   if(seed.empty())
      {
      const uint64_t ts = Botan_Tests::Test::timestamp();
      seed.resize(8);
      Botan::store_be(ts, seed.data());
      }

   output() << " drbg_seed:" << Botan::hex_encode(seed) << "\n";

   Botan_Tests::Test::set_test_options(log_success,
                                       run_online_tests,
                                       run_long_tests,
                                       data_dir,
                                       pkcs11_lib,
                                       pf);

   for(size_t i = 0; i != runs; ++i)
      {
      std::unique_ptr<Botan::RandomNumberGenerator> rng =
         std::unique_ptr<Botan::RandomNumberGenerator>(new Testsuite_RNG(drbg_seed, i));

      Botan_Tests::Test::set_test_rng(std::move(rng));

      const size_t failed = run_tests(req, i, runs);
      if(failed > 0)
         return failed;
      }

   return 0;
   }

namespace {

std::string report_out(const std::vector<Botan_Tests::Test::Result>& results,
                       size_t& tests_failed,
                       size_t& tests_ran)
   {
   std::ostringstream out;

   std::map<std::string, Botan_Tests::Test::Result> combined;
   for(auto const& result : results)
      {
      const std::string who = result.who();
      auto i = combined.find(who);
      if(i == combined.end())
         {
         combined.insert(std::make_pair(who, Botan_Tests::Test::Result(who)));
         i = combined.find(who);
         }

      i->second.merge(result);
      }

   for(auto const& result : combined)
      {
      const bool verbose = false;
      out << result.second.result_string(verbose);
      tests_failed += result.second.tests_failed();
      tests_ran += result.second.tests_run();
      }

   return out.str();
   }

}

size_t Test_Runner::run_tests(const std::vector<std::string>& tests_to_run,
                              size_t test_run,
                              size_t tot_test_runs)
   {
   size_t tests_ran = 0, tests_failed = 0;

   const uint64_t start_time = Botan_Tests::Test::timestamp();

   for(auto const& test_name : tests_to_run)
      {
      output() << test_name << ':' << std::endl;

      std::vector<Test::Result> results;

      try
         {
         if(Test* test = Test::get_test(test_name))
            {
            std::vector<Test::Result> test_results = test->run();
            results.insert(results.end(), test_results.begin(), test_results.end());
            }
         else
            {
            results.push_back(Test::Result::Note(test_name, "Test missing or unavailable"));
            }
         }
      catch(std::exception& e)
         {
         results.push_back(Test::Result::Failure(test_name, e.what()));
         }
      catch(...)
         {
         results.push_back(Test::Result::Failure(test_name, "unknown exception"));
         }

      output() << report_out(results, tests_failed, tests_ran) << std::flush;
      }

   const uint64_t total_ns = Botan_Tests::Test::timestamp() - start_time;

   if(test_run == 0 && tot_test_runs == 1)
      output() << "Tests";
   else
      output() << "Test run " << (1+test_run) << "/" << tot_test_runs;

   output() << " complete ran " << tests_ran << " tests in "
            << Botan_Tests::Test::format_time(total_ns) << " ";

   if(tests_failed > 0)
      {
      output() << tests_failed << " tests failed";
      }
   else if(tests_ran > 0)
      {
      output() << "all tests ok";
      }

   output() << std::endl;

   return tests_failed;
   }

}

<commit_msg>Fix seeding of test RNG<commit_after>/*
* (C) 2017 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include "test_runner.h"
#include "tests.h"

#include <botan/version.h>
#include <botan/rotate.h>
#include <botan/loadstor.h>

namespace Botan_Tests {

Test_Runner::Test_Runner(std::ostream& out) : m_output(out) {}

namespace {

/*
* This is a fast, simple, deterministic PRNG that's used for running
* the tests. It is not intended to be cryptographically secure.
*/
class Testsuite_RNG final : public Botan::RandomNumberGenerator
   {
   public:
      std::string name() const override { return "Testsuite_RNG"; }

      void clear() override
         {
         m_a = m_b = m_c = m_d = 0;
         }

      void add_entropy(const uint8_t data[], size_t len) override
         {
         for(size_t i = 0; i != len; ++i)
            {
            m_a ^= data[i];
            m_b ^= i;
            mix();
            }
         }

      bool is_seeded() const override
         {
         return true;
         }

      void randomize(uint8_t out[], size_t len) override
         {
         for(size_t i = 0; i != len; ++i)
            {
            out[i] = static_cast<uint8_t>(m_a);
            mix();
            }
         }

      Testsuite_RNG(const std::vector<uint8_t>& seed, size_t test_counter = 0)
         {
         m_d = static_cast<uint32_t>(test_counter);

         add_entropy(seed.data(), seed.size());
         }
   private:
      void mix()
         {
         const size_t ROUNDS = 3;

         for(size_t i = 0; i != ROUNDS; ++i)
            {
            m_a += i;

            m_a = Botan::rotl<9>(m_a);
            m_b ^= m_a;
            m_d ^= m_c;

            m_a += m_d;
            m_c += m_b;
            m_c = Botan::rotl<23>(m_c);
            }
         }

      uint32_t m_a = 0, m_b = 0, m_c = 0, m_d = 0;
   };

}

int Test_Runner::run(const std::vector<std::string>& requested_tests,
                     const std::string& data_dir,
                     const std::string& pkcs11_lib,
                     const std::string& provider,
                     bool log_success,
                     bool run_online_tests,
                     bool run_long_tests,
                     const std::string& drbg_seed,
                     size_t runs)
   {
   std::vector<std::string> req = requested_tests;

   if(req.empty())
      {
      /*
      If nothing was requested on the command line, run everything. First
      run the "essentials" to smoke test, then everything else in
      alphabetical order.
      */
      req = {"block", "stream", "hash", "mac", "modes", "aead"
             "kdf", "pbkdf", "hmac_drbg", "util"
      };

      std::set<std::string> all_others = Botan_Tests::Test::registered_tests();

      if(pkcs11_lib.empty())
         {
         // do not run pkcs11 tests by default unless pkcs11-lib set
         for(std::set<std::string>::iterator iter = all_others.begin(); iter != all_others.end();)
            {
            if((*iter).find("pkcs11") != std::string::npos)
               {
               iter = all_others.erase(iter);
               }
            else
               {
               ++iter;
               }
            }
         }

      for(auto f : req)
         {
         all_others.erase(f);
         }

      req.insert(req.end(), all_others.begin(), all_others.end());
      }
   else if(req.size() == 1 && req.at(0) == "pkcs11")
      {
      req = {"pkcs11-manage", "pkcs11-module", "pkcs11-slot", "pkcs11-session", "pkcs11-object", "pkcs11-rsa",
             "pkcs11-ecdsa", "pkcs11-ecdh", "pkcs11-rng", "pkcs11-x509"
      };
      }
   else
      {
      std::set<std::string> all = Botan_Tests::Test::registered_tests();
      for(auto const& r : req)
         {
         if(all.find(r) == all.end())
            {
            throw Botan_Tests::Test_Error("Unknown test suite: " + r);
            }
         }
      }

   output() << "Testing " << Botan::version_string() << "\n";
   output() << "Starting tests";

   if(!pkcs11_lib.empty())
      {
      output() << " pkcs11 library:" << pkcs11_lib;
      }

   Botan_Tests::Provider_Filter pf;
   if(!provider.empty())
      {
      output() << " provider:" << provider;
      pf.set(provider);
      }

   std::vector<uint8_t> seed = Botan::hex_decode(drbg_seed);
   if(seed.empty())
      {
      const uint64_t ts = Botan_Tests::Test::timestamp();
      seed.resize(8);
      Botan::store_be(ts, seed.data());
      }

   output() << " drbg_seed:" << Botan::hex_encode(seed) << "\n";

   Botan_Tests::Test::set_test_options(log_success,
                                       run_online_tests,
                                       run_long_tests,
                                       data_dir,
                                       pkcs11_lib,
                                       pf);

   for(size_t i = 0; i != runs; ++i)
      {
      std::unique_ptr<Botan::RandomNumberGenerator> rng =
         std::unique_ptr<Botan::RandomNumberGenerator>(new Testsuite_RNG(seed, i));

      Botan_Tests::Test::set_test_rng(std::move(rng));

      const size_t failed = run_tests(req, i, runs);
      if(failed > 0)
         return failed;
      }

   return 0;
   }

namespace {

std::string report_out(const std::vector<Botan_Tests::Test::Result>& results,
                       size_t& tests_failed,
                       size_t& tests_ran)
   {
   std::ostringstream out;

   std::map<std::string, Botan_Tests::Test::Result> combined;
   for(auto const& result : results)
      {
      const std::string who = result.who();
      auto i = combined.find(who);
      if(i == combined.end())
         {
         combined.insert(std::make_pair(who, Botan_Tests::Test::Result(who)));
         i = combined.find(who);
         }

      i->second.merge(result);
      }

   for(auto const& result : combined)
      {
      const bool verbose = false;
      out << result.second.result_string(verbose);
      tests_failed += result.second.tests_failed();
      tests_ran += result.second.tests_run();
      }

   return out.str();
   }

}

size_t Test_Runner::run_tests(const std::vector<std::string>& tests_to_run,
                              size_t test_run,
                              size_t tot_test_runs)
   {
   size_t tests_ran = 0, tests_failed = 0;

   const uint64_t start_time = Botan_Tests::Test::timestamp();

   for(auto const& test_name : tests_to_run)
      {
      output() << test_name << ':' << std::endl;

      std::vector<Test::Result> results;

      try
         {
         if(Test* test = Test::get_test(test_name))
            {
            std::vector<Test::Result> test_results = test->run();
            results.insert(results.end(), test_results.begin(), test_results.end());
            }
         else
            {
            results.push_back(Test::Result::Note(test_name, "Test missing or unavailable"));
            }
         }
      catch(std::exception& e)
         {
         results.push_back(Test::Result::Failure(test_name, e.what()));
         }
      catch(...)
         {
         results.push_back(Test::Result::Failure(test_name, "unknown exception"));
         }

      output() << report_out(results, tests_failed, tests_ran) << std::flush;
      }

   const uint64_t total_ns = Botan_Tests::Test::timestamp() - start_time;

   if(test_run == 0 && tot_test_runs == 1)
      output() << "Tests";
   else
      output() << "Test run " << (1+test_run) << "/" << tot_test_runs;

   output() << " complete ran " << tests_ran << " tests in "
            << Botan_Tests::Test::format_time(total_ns) << " ";

   if(tests_failed > 0)
      {
      output() << tests_failed << " tests failed";
      }
   else if(tests_ran > 0)
      {
      output() << "all tests ok";
      }

   output() << std::endl;

   return tests_failed;
   }

}

<|endoftext|>


<commit_before>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// author:  Baozeng Ding <sploving1@gmail.com>
// author:  Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

#include "NullDerefProtectionTransformer.h"

#include "cling/Interpreter/Interpreter.h"
#include "cling/Utils/AST.h"

#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Sema/Lookup.h"

#include <bitset>
#include <map>

using namespace clang;

namespace cling {
  NullDerefProtectionTransformer::NullDerefProtectionTransformer(Interpreter* I)
    : ASTTransformer(&I->getCI()->getSema()), m_Interp(I) {
  }

  NullDerefProtectionTransformer::~NullDerefProtectionTransformer()
  { }

  class PointerCheckInjector : public RecursiveASTVisitor<PointerCheckInjector> {
  private:
    Interpreter& m_Interp;
    Sema& m_Sema;
    typedef llvm::DenseMap<clang::FunctionDecl*, std::bitset<32> > decl_map_t;
    llvm::DenseMap<clang::FunctionDecl*, std::bitset<32> > m_NonNullArgIndexs;

    ///\brief Needed for the AST transformations, owned by Sema.
    ///
    ASTContext& m_Context;

    ///\brief cling_runtime_internal_throwIfInvalidPointer cache.
    ///
    LookupResult* m_clingthrowIfInvalidPointerCache;

  public:
    PointerCheckInjector(Interpreter& I)
      : m_Interp(I), m_Sema(I.getCI()->getSema()),
        m_Context(I.getCI()->getASTContext()),
        m_clingthrowIfInvalidPointerCache(0) {}

    ~PointerCheckInjector() {
      delete m_clingthrowIfInvalidPointerCache;
    }

    bool VisitUnaryOperator(UnaryOperator* UnOp) {
      Expr* SubExpr = UnOp->getSubExpr();
      VisitStmt(SubExpr);
      if (UnOp->getOpcode() == UO_Deref
          && !llvm::isa<clang::CXXThisExpr>(SubExpr)
          && SubExpr->getType().getTypePtr()->isPointerType())
          UnOp->setSubExpr(SynthesizeCheck(SubExpr));
      return true;
    }

    bool VisitMemberExpr(MemberExpr* ME) {
      Expr* Base = ME->getBase();
      VisitStmt(Base);
      if (ME->isArrow()
          && !llvm::isa<clang::CXXThisExpr>(Base)
          && ME->getMemberDecl()->isCXXInstanceMember())
        ME->setBase(SynthesizeCheck(Base));
      return true;
    }

    bool VisitCallExpr(CallExpr* CE) {
      VisitStmt(CE->getCallee());
      FunctionDecl* FDecl = CE->getDirectCallee();
      if (FDecl && isDeclCandidate(FDecl)) {
        decl_map_t::const_iterator it = m_NonNullArgIndexs.find(FDecl);
        const std::bitset<32>& ArgIndexs = it->second;
        Sema::ContextRAII pushedDC(m_Sema, FDecl);
        for (int index = 0; index < 32; ++index) {
          if (ArgIndexs.test(index)) {
            // Get the argument with the nonnull attribute.
            Expr* Arg = CE->getArg(index);
            if (Arg->getType().getTypePtr()->isPointerType()
                && !llvm::isa<clang::CXXThisExpr>(Arg))
              CE->setArg(index, SynthesizeCheck(Arg));
          }
        }
      }
      return true;
    }

    bool TraverseFunctionDecl(FunctionDecl* FD) {
      // We cannot synthesize when there is a const expr.
      if (!FD->isConstexpr())
        RecursiveASTVisitor::TraverseFunctionDecl(FD);
      return true;
    }

    bool TraverseCXXMethodDecl(CXXMethodDecl* CXXMD) {
      // We cannot synthesize when there is a const expr.
      if (!CXXMD->isConstexpr())
        RecursiveASTVisitor::TraverseCXXMethodDecl(CXXMD);
      return true;
    }

  private:
    Expr* SynthesizeCheck(Expr* Arg) {
      assert(Arg && "Cannot call with Arg=0");

      if(!m_clingthrowIfInvalidPointerCache)
        FindAndCacheRuntimeLookupResult();

      SourceLocation Loc = Arg->getLocStart();
      Expr* VoidSemaArg = utils::Synthesize::CStyleCastPtrExpr(&m_Sema,
                                                            m_Context.VoidPtrTy,
                                                            (uint64_t)&m_Interp);
      Expr* VoidExprArg = utils::Synthesize::CStyleCastPtrExpr(&m_Sema,
                                                          m_Context.VoidPtrTy,
                                                          (uint64_t)Arg);
      Scope* S = m_Sema.getScopeForContext(m_Sema.CurContext);
      CXXScopeSpec CSS;

      Expr* checkCall
        = m_Sema.BuildDeclarationNameExpr(CSS,
                                          *m_clingthrowIfInvalidPointerCache,
                                         /*ADL*/ false).get();
      const clang::FunctionProtoType* checkCallType
        = llvm::dyn_cast<const clang::FunctionProtoType>(
            checkCall->getType().getTypePtr());

      TypeSourceInfo* constVoidPtrTSI = m_Context.getTrivialTypeSourceInfo(
        checkCallType->getParamType(2), Loc);

      Expr* voidPtrArg
        = m_Sema.BuildCStyleCastExpr(Loc, constVoidPtrTSI, Loc,
                                     Arg).get();

      Expr *args[] = {VoidSemaArg, VoidExprArg, voidPtrArg};

      if (Expr* call = m_Sema.ActOnCallExpr(S, checkCall,
                                         Loc, args, Loc).get()) {
        clang::TypeSourceInfo* argTSI = m_Context.getTrivialTypeSourceInfo(
                                        Arg->getType(), Loc);
        Expr* castExpr = m_Sema.BuildCStyleCastExpr(Loc, argTSI, Loc, call).get();
        return castExpr;
      }
      return voidPtrArg;
    }

    bool isDeclCandidate(FunctionDecl * FDecl) {
      if (m_NonNullArgIndexs.count(FDecl))
        return true;

      if (llvm::isa<CXXRecordDecl>(FDecl))
        return true;

      std::bitset<32> ArgIndexs;
      for (specific_attr_iterator<NonNullAttr>
             I = FDecl->specific_attr_begin<NonNullAttr>(),
             E = FDecl->specific_attr_end<NonNullAttr>(); I != E; ++I) {

        NonNullAttr *NonNull = *I;
        for (NonNullAttr::args_iterator i = NonNull->args_begin(),
               e = NonNull->args_end(); i != e; ++i) {
          ArgIndexs.set(*i);
        }
      }

      if (ArgIndexs.any()) {
        m_NonNullArgIndexs.insert(std::make_pair(FDecl, ArgIndexs));
        return true;
      }
      return false;
    }

    void FindAndCacheRuntimeLookupResult() {
      assert(!m_clingthrowIfInvalidPointerCache && "Called multiple times!?");

      DeclarationName Name
        = &m_Context.Idents.get("cling_runtime_internal_throwIfInvalidPointer");
      SourceLocation noLoc;
      m_clingthrowIfInvalidPointerCache = new LookupResult(m_Sema, Name, noLoc,
                                        Sema::LookupOrdinaryName,
                                        Sema::ForRedeclaration);
      m_Sema.LookupQualifiedName(*m_clingthrowIfInvalidPointerCache,
                                 m_Context.getTranslationUnitDecl());
      assert(!m_clingthrowIfInvalidPointerCache->empty() &&
              "Lookup of cling_runtime_internal_throwIfInvalidPointer failed!");
    }
  };

  ASTTransformer::Result
  NullDerefProtectionTransformer::Transform(clang::Decl* D) {

    PointerCheckInjector injector(*m_Interp);
    injector.TraverseDecl(D);
    return Result(D, true);
  }
} // end namespace cling
<commit_msg>Fix the behavior of PointerCheckInjector for templated functions.<commit_after>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// author:  Baozeng Ding <sploving1@gmail.com>
// author:  Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

#include "NullDerefProtectionTransformer.h"

#include "cling/Interpreter/Interpreter.h"
#include "cling/Utils/AST.h"

#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Sema/Lookup.h"

#include <bitset>
#include <map>

using namespace clang;

namespace cling {
  NullDerefProtectionTransformer::NullDerefProtectionTransformer(Interpreter* I)
    : ASTTransformer(&I->getCI()->getSema()), m_Interp(I) {
  }

  NullDerefProtectionTransformer::~NullDerefProtectionTransformer()
  { }

  class PointerCheckInjector : public RecursiveASTVisitor<PointerCheckInjector> {
  private:
    Interpreter& m_Interp;
    Sema& m_Sema;
    typedef llvm::DenseMap<clang::FunctionDecl*, std::bitset<32> > decl_map_t;
    llvm::DenseMap<clang::FunctionDecl*, std::bitset<32> > m_NonNullArgIndexs;

    ///\brief Needed for the AST transformations, owned by Sema.
    ///
    ASTContext& m_Context;

    ///\brief cling_runtime_internal_throwIfInvalidPointer cache.
    ///
    LookupResult* m_clingthrowIfInvalidPointerCache;

  public:
    PointerCheckInjector(Interpreter& I)
      : m_Interp(I), m_Sema(I.getCI()->getSema()),
        m_Context(I.getCI()->getASTContext()),
        m_clingthrowIfInvalidPointerCache(0) {}

    ~PointerCheckInjector() {
      delete m_clingthrowIfInvalidPointerCache;
    }

    bool VisitUnaryOperator(UnaryOperator* UnOp) {
      Expr* SubExpr = UnOp->getSubExpr();
      VisitStmt(SubExpr);
      if (UnOp->getOpcode() == UO_Deref
          && !llvm::isa<clang::CXXThisExpr>(SubExpr)
          && SubExpr->getType().getTypePtr()->isPointerType())
          UnOp->setSubExpr(SynthesizeCheck(SubExpr));
      return true;
    }

    bool VisitMemberExpr(MemberExpr* ME) {
      Expr* Base = ME->getBase();
      VisitStmt(Base);
      if (ME->isArrow()
          && !llvm::isa<clang::CXXThisExpr>(Base)
          && ME->getMemberDecl()->isCXXInstanceMember())
        ME->setBase(SynthesizeCheck(Base));
      return true;
    }

    bool VisitCallExpr(CallExpr* CE) {
      VisitStmt(CE->getCallee());
      FunctionDecl* FDecl = CE->getDirectCallee();
      if (FDecl && isDeclCandidate(FDecl)) {
        decl_map_t::const_iterator it = m_NonNullArgIndexs.find(FDecl);
        const std::bitset<32>& ArgIndexs = it->second;
        Sema::ContextRAII pushedDC(m_Sema, FDecl);
        for (int index = 0; index < 32; ++index) {
          if (ArgIndexs.test(index)) {
            // Get the argument with the nonnull attribute.
            Expr* Arg = CE->getArg(index);
            if (Arg->getType().getTypePtr()->isPointerType()
                && !llvm::isa<clang::CXXThisExpr>(Arg))
              CE->setArg(index, SynthesizeCheck(Arg));
          }
        }
      }
      return true;
    }

    bool TraverseFunctionDecl(FunctionDecl* FD) {
      // We cannot synthesize when there is a const expr
      // and if it is a function template (we will do the transformation on
      // the instance).
      if (!FD->isConstexpr() && !FD->getDescribedFunctionTemplate())
         RecursiveASTVisitor::TraverseFunctionDecl(FD);
      return true;
    }

    bool TraverseCXXMethodDecl(CXXMethodDecl* CXXMD) {
      // We cannot synthesize when there is a const expr.
      if (!CXXMD->isConstexpr())
        RecursiveASTVisitor::TraverseCXXMethodDecl(CXXMD);
      return true;
    }

  private:
    Expr* SynthesizeCheck(Expr* Arg) {
      assert(Arg && "Cannot call with Arg=0");

      if(!m_clingthrowIfInvalidPointerCache)
        FindAndCacheRuntimeLookupResult();

      SourceLocation Loc = Arg->getLocStart();
      Expr* VoidSemaArg = utils::Synthesize::CStyleCastPtrExpr(&m_Sema,
                                                            m_Context.VoidPtrTy,
                                                            (uint64_t)&m_Interp);
      Expr* VoidExprArg = utils::Synthesize::CStyleCastPtrExpr(&m_Sema,
                                                          m_Context.VoidPtrTy,
                                                          (uint64_t)Arg);
      Scope* S = m_Sema.getScopeForContext(m_Sema.CurContext);
      CXXScopeSpec CSS;

      Expr* checkCall
        = m_Sema.BuildDeclarationNameExpr(CSS,
                                          *m_clingthrowIfInvalidPointerCache,
                                         /*ADL*/ false).get();
      const clang::FunctionProtoType* checkCallType
        = llvm::dyn_cast<const clang::FunctionProtoType>(
            checkCall->getType().getTypePtr());

      TypeSourceInfo* constVoidPtrTSI = m_Context.getTrivialTypeSourceInfo(
        checkCallType->getParamType(2), Loc);

      // It is unclear whether this is the correct cast if the type
      // is dependent.  Hence, For now, we do not expect SynthesizeCheck to
      // be run on a function template.  It should be run only on function
      // instances.
      // When this is actually insert in a function template, it seems that
      // clang r272382 when instantiating the templates drops one of the part
      // of the implicit cast chain.
      // Namely in:
/*
`-ImplicitCastExpr 0x1010cea90 <col:4> 'const void *' <BitCast>
 `-ImplicitCastExpr 0x1026e0bc0 <col:4> 'const class TAttMarker *'
                    <UncheckedDerivedToBase (TAttMarker)>
  `-ImplicitCastExpr 0x1026e0b48 <col:4> 'class TGraph *' <LValueToRValue>
   `-DeclRefExpr 0x1026e0b20 <col:4> 'class TGraph *' lvalue Var 0x1026e09c0
                   'g5' 'class TGraph *'
*/
      // It drops the 2nd lines (ImplicitCastExpr UncheckedDerivedToBase)
      // clang r227800 seems to actually keep that lines during instantiation.
      Expr* voidPtrArg
        = m_Sema.BuildCStyleCastExpr(Loc, constVoidPtrTSI, Loc, Arg).get();

      Expr *args[] = {VoidSemaArg, VoidExprArg, voidPtrArg};

      if (Expr* call = m_Sema.ActOnCallExpr(S, checkCall,
                                            Loc, args, Loc).get())
      {
        // It is unclear whether this is the correct cast if the type
        // is dependent.  Hence, For now, we do not expect SynthesizeCheck to
        // be run on a function template.  It should be run only on function
        // instances.
        clang::TypeSourceInfo* argTSI = m_Context.getTrivialTypeSourceInfo(
                                          Arg->getType(), Loc);
        Expr* castExpr = m_Sema.BuildCStyleCastExpr(Loc, argTSI,
                                                    Loc, call).get();
        return castExpr;
      }
      return voidPtrArg;
    }

    bool isDeclCandidate(FunctionDecl * FDecl) {
      if (m_NonNullArgIndexs.count(FDecl))
        return true;

      if (llvm::isa<CXXRecordDecl>(FDecl))
        return true;

      std::bitset<32> ArgIndexs;
      for (specific_attr_iterator<NonNullAttr>
             I = FDecl->specific_attr_begin<NonNullAttr>(),
             E = FDecl->specific_attr_end<NonNullAttr>(); I != E; ++I) {

        NonNullAttr *NonNull = *I;
        for (NonNullAttr::args_iterator i = NonNull->args_begin(),
               e = NonNull->args_end(); i != e; ++i) {
          ArgIndexs.set(*i);
        }
      }

      if (ArgIndexs.any()) {
        m_NonNullArgIndexs.insert(std::make_pair(FDecl, ArgIndexs));
        return true;
      }
      return false;
    }

    void FindAndCacheRuntimeLookupResult() {
      assert(!m_clingthrowIfInvalidPointerCache && "Called multiple times!?");

      DeclarationName Name
        = &m_Context.Idents.get("cling_runtime_internal_throwIfInvalidPointer");
      SourceLocation noLoc;
      m_clingthrowIfInvalidPointerCache = new LookupResult(m_Sema, Name, noLoc,
                                        Sema::LookupOrdinaryName,
                                        Sema::ForRedeclaration);
      m_Sema.LookupQualifiedName(*m_clingthrowIfInvalidPointerCache,
                                 m_Context.getTranslationUnitDecl());
      assert(!m_clingthrowIfInvalidPointerCache->empty() &&
              "Lookup of cling_runtime_internal_throwIfInvalidPointer failed!");
    }
  };

  ASTTransformer::Result
  NullDerefProtectionTransformer::Transform(clang::Decl* D) {

    PointerCheckInjector injector(*m_Interp);
    injector.TraverseDecl(D);
    return Result(D, true);
  }
} // end namespace cling
<|endoftext|>


<commit_before>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author:  Baozeng Ding <sploving1@gmail.com>
// author:  Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//------------------------------------------------------------------------------

#include "NullDerefProtectionTransformer.h"

#include "cling/Interpreter/Transaction.h"
#include "cling/Utils/AST.h"

#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Mangle.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Sema/Lookup.h"

#include <bitset>
#include <map>

using namespace clang;

namespace cling {
  NullDerefProtectionTransformer::NullDerefProtectionTransformer(clang::Sema* S)
    : TransactionTransformer(S) {
  }

  NullDerefProtectionTransformer::~NullDerefProtectionTransformer()
  { }

  // Copied from clad - the clang/opencl autodiff project
  class NodeContext {
  public:
  private:
    typedef llvm::SmallVector<clang::Stmt*, 2> Statements;
    Statements m_Stmts;
  private:
    NodeContext() {};
  public:
    NodeContext(clang::Stmt* s) { m_Stmts.push_back(s); }
    NodeContext(clang::Stmt* s0, clang::Stmt* s1) {
      m_Stmts.push_back(s0);
      m_Stmts.push_back(s1);
    }

    bool isSingleStmt() const { return m_Stmts.size() == 1; }

    clang::Stmt* getStmt() {
      assert(isSingleStmt() && "Cannot get multiple stmts.");
      return m_Stmts.front();
    }
    const clang::Stmt* getStmt() const { return getStmt(); }
    const Statements& getStmts() const {
      return m_Stmts;
    }

    CompoundStmt* wrapInCompoundStmt(clang::ASTContext& C) const {
      assert(!isSingleStmt() && "Must be more than 1");
      llvm::ArrayRef<Stmt*> stmts
        = llvm::makeArrayRef(m_Stmts.data(), m_Stmts.size());
      clang::SourceLocation noLoc;
      return new (C) clang::CompoundStmt(C, stmts, noLoc, noLoc);
    }

    clang::Expr* getExpr() {
      assert(llvm::isa<clang::Expr>(getStmt()) && "Must be an expression.");
      return llvm::cast<clang::Expr>(getStmt());
    }
    const clang::Expr* getExpr() const {
      return getExpr();
    }

    void prepend(clang::Stmt* S) {
      m_Stmts.insert(m_Stmts.begin(), S);
    }

    void append(clang::Stmt* S) {
      m_Stmts.push_back(S);
    }
  };

  class IfStmtInjector : public StmtVisitor<IfStmtInjector, NodeContext> {
  private:
    Sema& m_Sema;
    typedef std::map<clang::FunctionDecl*, std::bitset<32> > decl_map_t;
    std::map<clang::FunctionDecl*, std::bitset<32> > m_NonNullArgIndexs;

  public:
    IfStmtInjector(Sema& S) : m_Sema(S) {}
    CompoundStmt* Inject(CompoundStmt* CS) {
      NodeContext result = VisitCompoundStmt(CS);
      return cast<CompoundStmt>(result.getStmt());
    }

    NodeContext VisitStmt(Stmt* S) {
      return NodeContext(S);
    }

    NodeContext VisitCompoundStmt(CompoundStmt* CS) {
      ASTContext& C = m_Sema.getASTContext();
      llvm::SmallVector<Stmt*, 16> stmts;
      for (CompoundStmt::body_iterator I = CS->body_begin(), E = CS->body_end();
           I != E; ++I) {
        NodeContext nc = Visit(*I);
        if (nc.isSingleStmt())
          stmts.push_back(nc.getStmt());
        else
          stmts.append(nc.getStmts().begin(), nc.getStmts().end());
      }

      llvm::ArrayRef<Stmt*> stmtsRef(stmts.data(), stmts.size());
      CompoundStmt* newCS = new (C) CompoundStmt(C, stmtsRef,
                                                 CS->getLBracLoc(),
                                                 CS->getRBracLoc());
      return NodeContext(newCS);
    }

    NodeContext VisitIfStmt(IfStmt* If) {
      NodeContext result(If);
      // check the condition
      NodeContext cond = Visit(If->getCond());
      if (!cond.isSingleStmt())
        result.prepend(cond.getStmts()[0]);
      return result;
    }

    NodeContext VisitCastExpr(CastExpr* CE) {
      NodeContext result = Visit(CE->getSubExpr());
      return result;
    }

    NodeContext VisitBinaryOperator(BinaryOperator* BinOp) {
      NodeContext result(BinOp);

      // Here we might get if(check) throw; binop rhs.
      NodeContext rhs = Visit(BinOp->getRHS());
      // Here we might get if(check) throw; binop lhs.
      NodeContext lhs = Visit(BinOp->getLHS());

      // Prepend those checks. It will become:
      // if(check_rhs) throw; if (check_lhs) throw; BinOp;
      if (!rhs.isSingleStmt()) {
        // FIXME:we need to loop from 0 to n-1
        result.prepend(rhs.getStmts()[0]);
      }
      if (!lhs.isSingleStmt()) {
        // FIXME:we need to loop from 0 to n-1
        result.prepend(lhs.getStmts()[0]);
      }
      return result;
    }

    NodeContext VisitUnaryOperator(UnaryOperator* UnOp) {
      NodeContext result(UnOp);
      if (UnOp->getOpcode() == UO_Deref) {
        result.prepend(SynthesizeCheck(UnOp->getLocStart(),
                                       UnOp->getSubExpr()));
      }
      return result;
    }

    NodeContext VisitMemberExpr(MemberExpr* ME) {
      NodeContext result(ME);
      if (ME->isArrow()) {
        result.prepend(SynthesizeCheck(ME->getLocStart(),
                                       ME->getBase()->IgnoreImplicit()));
      }
      return result;
    }

    NodeContext VisitCallExpr(CallExpr* CE) {
      FunctionDecl* FDecl = CE->getDirectCallee();
      NodeContext result(CE);
      if (FDecl && isDeclCandidate(FDecl)) {
        SourceLocation CallLoc = CE->getLocStart();
        decl_map_t::const_iterator it = m_NonNullArgIndexs.find(FDecl);
        const std::bitset<32>& ArgIndexs = it->second;
        Sema::ContextRAII pushedDC(m_Sema, FDecl);
        for (int index = 0; index < 32; ++index) {
          if (ArgIndexs.test(index)) {
            // Get the argument with the nonnull attribute.
            Expr* Arg = CE->getArg(index);
            result.prepend(SynthesizeCheck(Arg->getLocStart(), Arg));
          }
        }
      }
      return result;
    }

  private:
    Stmt* SynthesizeCheck(SourceLocation Loc, Expr* Arg ) {
      assert(Arg && "Cannot call with Arg=0");
      ASTContext& Context = m_Sema.getASTContext();
      //copied from DynamicLookup.cpp
      NamespaceDecl* NSD = utils::Lookup::Namespace(&m_Sema, "cling");
      NamespaceDecl* clingRuntimeNSD
        = utils::Lookup::Namespace(&m_Sema, "runtime", NSD);

      // Find and set up "NullDerefException"
      DeclarationName Name
        = &Context.Idents.get("NullDerefException");

      LookupResult R(m_Sema, Name, SourceLocation(),
                     Sema::LookupOrdinaryName, Sema::ForRedeclaration);

      m_Sema.LookupQualifiedName(R, clingRuntimeNSD);
      CXXRecordDecl* NullDerefDecl = R.getAsSingle<CXXRecordDecl>();

      // Lookup Sema type
      CXXRecordDecl* SemaRD
        = dyn_cast<CXXRecordDecl>(utils::Lookup::Named(&m_Sema, "Sema",
                                   utils::Lookup::Namespace(&m_Sema, "clang")));

      QualType SemaRDTy = Context.getTypeDeclType(SemaRD);
      Expr* VoidSemaArg = utils::Synthesize::CStyleCastPtrExpr(&m_Sema,SemaRDTy,
                                                             (uint64_t)&m_Sema);

      // Lookup Expr type
      CXXRecordDecl* ExprRD
        = dyn_cast<CXXRecordDecl>(utils::Lookup::Named(&m_Sema, "Expr",
                                   utils::Lookup::Namespace(&m_Sema, "clang")));

      QualType ExprRDTy = Context.getTypeDeclType(ExprRD);
      Expr* VoidExprArg = utils::Synthesize::CStyleCastPtrExpr(&m_Sema,ExprRDTy,
                                                               (uint64_t)Arg);

      Expr *args[] = {VoidSemaArg, VoidExprArg};
      QualType NullDerefDeclTy = Context.getTypeDeclType(NullDerefDecl);
      TypeSourceInfo* TSI = Context.getTrivialTypeSourceInfo(NullDerefDeclTy,
                                                             SourceLocation());
      ExprResult ConstructorCall
        = m_Sema.BuildCXXTypeConstructExpr(TSI,Loc, MultiExprArg(args, 2),Loc);

      Scope* S = m_Sema.getScopeForContext(m_Sema.CurContext);
      Expr* Throw = m_Sema.ActOnCXXThrow(S, Loc, ConstructorCall.take()).take();

      // Check whether we can get the argument'value. If the argument is
      // null, throw an exception direclty. If the argument is not null
      // then ignore this argument and continue to deal with the next
      // argument with the nonnull attribute.
      bool Result = false;
      if (Arg->EvaluateAsBooleanCondition(Result, Context)) {
        if(!Result) {
          return Throw;
        }
        return Arg;
      }
      // The argument's value cannot be decided, so we add a UnaryOp
      // operation to check its value at runtime.
      ExprResult ER = m_Sema.ActOnUnaryOp(S, Loc, tok::exclaim, Arg);

      Decl* varDecl = 0;
      Stmt* varStmt = 0;
      Sema::FullExprArg FullCond(m_Sema.MakeFullExpr(ER.take()));
      StmtResult IfStmt = m_Sema.ActOnIfStmt(Loc, FullCond, varDecl,
                                             Throw, Loc, varStmt);
      return IfStmt.take();
    }

    bool isDeclCandidate(FunctionDecl * FDecl) {
      if (m_NonNullArgIndexs.count(FDecl))
        return true;

      std::bitset<32> ArgIndexs;
      for (specific_attr_iterator<NonNullAttr>
             I = FDecl->specific_attr_begin<NonNullAttr>(),
             E = FDecl->specific_attr_end<NonNullAttr>(); I != E; ++I) {

        NonNullAttr *NonNull = *I;
        for (NonNullAttr::args_iterator i = NonNull->args_begin(),
               e = NonNull->args_end(); i != e; ++i) {
          ArgIndexs.set(*i);
        }
      }

      if (ArgIndexs.any()) {
        m_NonNullArgIndexs.insert(std::make_pair(FDecl, ArgIndexs));
        return true;
      }
      return false;
    }
  };

  void NullDerefProtectionTransformer::Transform() {
    FunctionDecl* FD = getTransaction()->getWrapperFD();
    if (!FD)
      return;

    CompoundStmt* CS = dyn_cast<CompoundStmt>(FD->getBody());
    assert(CS && "Function body not a CompundStmt?");
    IfStmtInjector injector((*m_Sema));
    CompoundStmt* newCS = injector.Inject(CS);
    FD->setBody(newCS);
  }
} // end namespace cling
<commit_msg>Silence a warning.<commit_after>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author:  Baozeng Ding <sploving1@gmail.com>
// author:  Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//------------------------------------------------------------------------------

#include "NullDerefProtectionTransformer.h"

#include "cling/Interpreter/Transaction.h"
#include "cling/Utils/AST.h"

#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Mangle.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Sema/Lookup.h"

#include <bitset>
#include <map>

using namespace clang;

namespace cling {
  NullDerefProtectionTransformer::NullDerefProtectionTransformer(clang::Sema* S)
    : TransactionTransformer(S) {
  }

  NullDerefProtectionTransformer::~NullDerefProtectionTransformer()
  { }

  // Copied from clad - the clang/opencl autodiff project
  class NodeContext {
  public:
  private:
    typedef llvm::SmallVector<clang::Stmt*, 2> Statements;
    Statements m_Stmts;
  private:
    NodeContext() {};
  public:
    NodeContext(clang::Stmt* s) { m_Stmts.push_back(s); }
    NodeContext(clang::Stmt* s0, clang::Stmt* s1) {
      m_Stmts.push_back(s0);
      m_Stmts.push_back(s1);
    }

    bool isSingleStmt() const { return m_Stmts.size() == 1; }

    clang::Stmt* getStmt() {
      assert(isSingleStmt() && "Cannot get multiple stmts.");
      return m_Stmts.front();
    }
    const clang::Stmt* getStmt() const { return getStmt(); }
    const Statements& getStmts() const {
      return m_Stmts;
    }

    CompoundStmt* wrapInCompoundStmt(clang::ASTContext& C) const {
      assert(!isSingleStmt() && "Must be more than 1");
      llvm::ArrayRef<Stmt*> stmts
        = llvm::makeArrayRef(m_Stmts.data(), m_Stmts.size());
      clang::SourceLocation noLoc;
      return new (C) clang::CompoundStmt(C, stmts, noLoc, noLoc);
    }

    clang::Expr* getExpr() {
      assert(llvm::isa<clang::Expr>(getStmt()) && "Must be an expression.");
      return llvm::cast<clang::Expr>(getStmt());
    }
    const clang::Expr* getExpr() const {
      return getExpr();
    }

    void prepend(clang::Stmt* S) {
      m_Stmts.insert(m_Stmts.begin(), S);
    }

    void append(clang::Stmt* S) {
      m_Stmts.push_back(S);
    }
  };

  class IfStmtInjector : public StmtVisitor<IfStmtInjector, NodeContext> {
  private:
    Sema& m_Sema;
    typedef std::map<clang::FunctionDecl*, std::bitset<32> > decl_map_t;
    std::map<clang::FunctionDecl*, std::bitset<32> > m_NonNullArgIndexs;

  public:
    IfStmtInjector(Sema& S) : m_Sema(S) {}
    CompoundStmt* Inject(CompoundStmt* CS) {
      NodeContext result = VisitCompoundStmt(CS);
      return cast<CompoundStmt>(result.getStmt());
    }

    NodeContext VisitStmt(Stmt* S) {
      return NodeContext(S);
    }

    NodeContext VisitCompoundStmt(CompoundStmt* CS) {
      ASTContext& C = m_Sema.getASTContext();
      llvm::SmallVector<Stmt*, 16> stmts;
      for (CompoundStmt::body_iterator I = CS->body_begin(), E = CS->body_end();
           I != E; ++I) {
        NodeContext nc = Visit(*I);
        if (nc.isSingleStmt())
          stmts.push_back(nc.getStmt());
        else
          stmts.append(nc.getStmts().begin(), nc.getStmts().end());
      }

      llvm::ArrayRef<Stmt*> stmtsRef(stmts.data(), stmts.size());
      CompoundStmt* newCS = new (C) CompoundStmt(C, stmtsRef,
                                                 CS->getLBracLoc(),
                                                 CS->getRBracLoc());
      return NodeContext(newCS);
    }

    NodeContext VisitIfStmt(IfStmt* If) {
      NodeContext result(If);
      // check the condition
      NodeContext cond = Visit(If->getCond());
      if (!cond.isSingleStmt())
        result.prepend(cond.getStmts()[0]);
      return result;
    }

    NodeContext VisitCastExpr(CastExpr* CE) {
      NodeContext result = Visit(CE->getSubExpr());
      return result;
    }

    NodeContext VisitBinaryOperator(BinaryOperator* BinOp) {
      NodeContext result(BinOp);

      // Here we might get if(check) throw; binop rhs.
      NodeContext rhs = Visit(BinOp->getRHS());
      // Here we might get if(check) throw; binop lhs.
      NodeContext lhs = Visit(BinOp->getLHS());

      // Prepend those checks. It will become:
      // if(check_rhs) throw; if (check_lhs) throw; BinOp;
      if (!rhs.isSingleStmt()) {
        // FIXME:we need to loop from 0 to n-1
        result.prepend(rhs.getStmts()[0]);
      }
      if (!lhs.isSingleStmt()) {
        // FIXME:we need to loop from 0 to n-1
        result.prepend(lhs.getStmts()[0]);
      }
      return result;
    }

    NodeContext VisitUnaryOperator(UnaryOperator* UnOp) {
      NodeContext result(UnOp);
      if (UnOp->getOpcode() == UO_Deref) {
        result.prepend(SynthesizeCheck(UnOp->getLocStart(),
                                       UnOp->getSubExpr()));
      }
      return result;
    }

    NodeContext VisitMemberExpr(MemberExpr* ME) {
      NodeContext result(ME);
      if (ME->isArrow()) {
        result.prepend(SynthesizeCheck(ME->getLocStart(),
                                       ME->getBase()->IgnoreImplicit()));
      }
      return result;
    }

    NodeContext VisitCallExpr(CallExpr* CE) {
      FunctionDecl* FDecl = CE->getDirectCallee();
      NodeContext result(CE);
      if (FDecl && isDeclCandidate(FDecl)) {
        decl_map_t::const_iterator it = m_NonNullArgIndexs.find(FDecl);
        const std::bitset<32>& ArgIndexs = it->second;
        Sema::ContextRAII pushedDC(m_Sema, FDecl);
        for (int index = 0; index < 32; ++index) {
          if (ArgIndexs.test(index)) {
            // Get the argument with the nonnull attribute.
            Expr* Arg = CE->getArg(index);
            result.prepend(SynthesizeCheck(Arg->getLocStart(), Arg));
          }
        }
      }
      return result;
    }

  private:
    Stmt* SynthesizeCheck(SourceLocation Loc, Expr* Arg ) {
      assert(Arg && "Cannot call with Arg=0");
      ASTContext& Context = m_Sema.getASTContext();
      //copied from DynamicLookup.cpp
      NamespaceDecl* NSD = utils::Lookup::Namespace(&m_Sema, "cling");
      NamespaceDecl* clingRuntimeNSD
        = utils::Lookup::Namespace(&m_Sema, "runtime", NSD);

      // Find and set up "NullDerefException"
      DeclarationName Name
        = &Context.Idents.get("NullDerefException");

      LookupResult R(m_Sema, Name, SourceLocation(),
                     Sema::LookupOrdinaryName, Sema::ForRedeclaration);

      m_Sema.LookupQualifiedName(R, clingRuntimeNSD);
      CXXRecordDecl* NullDerefDecl = R.getAsSingle<CXXRecordDecl>();

      // Lookup Sema type
      CXXRecordDecl* SemaRD
        = dyn_cast<CXXRecordDecl>(utils::Lookup::Named(&m_Sema, "Sema",
                                   utils::Lookup::Namespace(&m_Sema, "clang")));

      QualType SemaRDTy = Context.getTypeDeclType(SemaRD);
      Expr* VoidSemaArg = utils::Synthesize::CStyleCastPtrExpr(&m_Sema,SemaRDTy,
                                                             (uint64_t)&m_Sema);

      // Lookup Expr type
      CXXRecordDecl* ExprRD
        = dyn_cast<CXXRecordDecl>(utils::Lookup::Named(&m_Sema, "Expr",
                                   utils::Lookup::Namespace(&m_Sema, "clang")));

      QualType ExprRDTy = Context.getTypeDeclType(ExprRD);
      Expr* VoidExprArg = utils::Synthesize::CStyleCastPtrExpr(&m_Sema,ExprRDTy,
                                                               (uint64_t)Arg);

      Expr *args[] = {VoidSemaArg, VoidExprArg};
      QualType NullDerefDeclTy = Context.getTypeDeclType(NullDerefDecl);
      TypeSourceInfo* TSI = Context.getTrivialTypeSourceInfo(NullDerefDeclTy,
                                                             SourceLocation());
      ExprResult ConstructorCall
        = m_Sema.BuildCXXTypeConstructExpr(TSI,Loc, MultiExprArg(args, 2),Loc);

      Scope* S = m_Sema.getScopeForContext(m_Sema.CurContext);
      Expr* Throw = m_Sema.ActOnCXXThrow(S, Loc, ConstructorCall.take()).take();

      // Check whether we can get the argument'value. If the argument is
      // null, throw an exception direclty. If the argument is not null
      // then ignore this argument and continue to deal with the next
      // argument with the nonnull attribute.
      bool Result = false;
      if (Arg->EvaluateAsBooleanCondition(Result, Context)) {
        if(!Result) {
          return Throw;
        }
        return Arg;
      }
      // The argument's value cannot be decided, so we add a UnaryOp
      // operation to check its value at runtime.
      ExprResult ER = m_Sema.ActOnUnaryOp(S, Loc, tok::exclaim, Arg);

      Decl* varDecl = 0;
      Stmt* varStmt = 0;
      Sema::FullExprArg FullCond(m_Sema.MakeFullExpr(ER.take()));
      StmtResult IfStmt = m_Sema.ActOnIfStmt(Loc, FullCond, varDecl,
                                             Throw, Loc, varStmt);
      return IfStmt.take();
    }

    bool isDeclCandidate(FunctionDecl * FDecl) {
      if (m_NonNullArgIndexs.count(FDecl))
        return true;

      std::bitset<32> ArgIndexs;
      for (specific_attr_iterator<NonNullAttr>
             I = FDecl->specific_attr_begin<NonNullAttr>(),
             E = FDecl->specific_attr_end<NonNullAttr>(); I != E; ++I) {

        NonNullAttr *NonNull = *I;
        for (NonNullAttr::args_iterator i = NonNull->args_begin(),
               e = NonNull->args_end(); i != e; ++i) {
          ArgIndexs.set(*i);
        }
      }

      if (ArgIndexs.any()) {
        m_NonNullArgIndexs.insert(std::make_pair(FDecl, ArgIndexs));
        return true;
      }
      return false;
    }
  };

  void NullDerefProtectionTransformer::Transform() {
    FunctionDecl* FD = getTransaction()->getWrapperFD();
    if (!FD)
      return;

    CompoundStmt* CS = dyn_cast<CompoundStmt>(FD->getBody());
    assert(CS && "Function body not a CompundStmt?");
    IfStmtInjector injector((*m_Sema));
    CompoundStmt* newCS = injector.Inject(CS);
    FD->setBody(newCS);
  }
} // end namespace cling
<|endoftext|>


<commit_before>//  Copyright © 2015 George Georgiev. All rights reserved.
//

#pragma once

#include "task/cxx/cxx_file_crc_task.h"
#include "task/db/db_put_task.h"
#include "task/sys/ensure_directory_task.h"
#include "task/sys/execute_command_task.h"
#include "dom/cxx/cxx_program.hpp"
#include "doim/cxx/cxx_file.hpp"
#include "doim/db/db_key.hpp"
#include "doim/db/db_value.hpp"
#include "doim/sys/argument.hpp"
#include "doim/sys/command.hpp"
#include "db/database.h"
#include <memory>
#include <set>
#include <sstream>
#include <string>

namespace tool
{
class CxxCompiler;
typedef std::shared_ptr<CxxCompiler> CxxCompilerSPtr;

class CxxCompiler
{
public:
    CxxCompiler(const doim::SysExecutableSPtr& compiler)
        : mCompiler(compiler)
    {
    }

    doim::SysArgumentSetSPtr includeArguments(
        const doim::FsDirectorySPtr& directory,
        const doim::CxxIncludeDirectorySetSPtr& includeDirectories)
    {
        auto arguments = std::make_shared<doim::SysArgumentSet>();

        for (const auto& includeDirectory : *includeDirectories)
        {
            std::string value;
            switch (includeDirectory->type())
            {
                case doim::CxxIncludeDirectory::Type::kUser:
                    value = "-I ";
                    break;
                case doim::CxxIncludeDirectory::Type::kSystem:
                    value = "-isystem ";
                    break;
            }

            value += includeDirectory->directory()->path(directory);

            arguments->insert(doim::gManager->obtainArgument(value));
        }

        return arguments;
    }

    ECode tasks(const doim::FsDirectorySPtr& directory,
                const doim::CxxObjectFileSPtr& objectFile,
                std::vector<tpool::TaskSPtr>& tasks)
    {
        auto task = std::make_shared<task::CxxFileCrcTask>(objectFile->cxxFile());
        EHTest((*task)(), objectFile->file()->path());

        auto key = std::make_shared<doim::DbKey>("file:" +
                                                 objectFile->cxxFile()->file()->path());
        key = doim::gManager->unique(key);

        uint64_t crc;
        db::gDatabase->get(key->bytes(), crc);

        if (task->crc() == crc)
            EHEnd;

        std::cout << "set crc:" << crc << " expected: " << task->crc() << std::endl;

        auto mkdirTask =
            std::make_shared<task::EnsureDirectoryTask>(objectFile->file()->directory());
        tasks.push_back(mkdirTask);

        auto compileArguments =
            includeArguments(directory, objectFile->cxxFile()->cxxIncludeDirectories());

        auto argument_cxxflags = doim::gManager->obtainArgument(
            "-std=c++11 -stdlib=libc++ -O0 -g "
            "-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/"
            "MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk");
        compileArguments->insert(argument_cxxflags);

        auto argument_c = doim::gManager->obtainArgument(
            "-c " + objectFile->cxxFile()->file()->path(directory));
        compileArguments->insert(argument_c);

        auto argument_o =
            doim::gManager->obtainArgument("-o " + objectFile->file()->path(directory));
        compileArguments->insert(argument_o);

        auto compileCommand =
            std::make_shared<doim::SysCommand>(mCompiler, compileArguments);
        auto compileTask = std::make_shared<task::ExecuteCommandTask>(compileCommand);
        tasks.push_back(compileTask);

        auto value = std::make_shared<doim::DbValue>(task->crc());
        auto updateTask = std::make_shared<task::DbPutTask>(key, value);
        tasks.push_back(updateTask);

        EHEnd;
    }

    ECode commands(const doim::FsDirectorySPtr& directory,
                   const dom::CxxProgramSPtr& program,
                   std::string& cmd)
    {
        const auto& intermediate = doim::gManager->obtainDirectory(directory, "build");

        std::stringstream stream;

        std::set<std::string> files;

        std::set<std::string> lib_directories;
        std::set<std::string> lib_binaries;
        for (const auto& cxxLibrary : program->recursiveCxxLibraries())
        {
            if (cxxLibrary->binary() != nullptr)
            {
                lib_directories.insert(
                    cxxLibrary->binary()->directory()->path(directory));
                std::string name = cxxLibrary->binary()->name();
                lib_binaries.insert(name.substr(3, name.size() - 5));
            }
            else
            {
                const auto& objectFiles =
                    cxxLibrary->cxxObjectFiles(directory, intermediate);

                for (const auto& objectFile : objectFiles)
                {
                    files.insert(objectFile->cxxFile()->file()->path(directory));
                    std::vector<tpool::TaskSPtr> tsks;
                    EHTest(tasks(directory, objectFile, tsks));
                    for (auto task : tsks)
                    {
                        ILOG("RUN: " + task->description());
                        EHTest((*task)());
                        ILOG("DONE: " + task->description());
                    }
                }
            }
        }

        const auto& objectFiles = program->cxxObjectFiles(directory, intermediate);
        for (const auto& objectFile : objectFiles)
        {
            files.insert(objectFile->cxxFile()->file()->path(directory));

            std::vector<tpool::TaskSPtr> tsks;
            EHTest(tasks(directory, objectFile, tsks));
            for (auto task : tsks)
            {
                ILOG("RUN: " + task->description());
                EHTest((*task)());
                ILOG("DONE: " + task->description());
            }
        }

        std::stringstream objFilesStream;
        for (const auto& file : files)
            objFilesStream << " build/" << file << ".o";

        for (const auto& lib_directory : lib_directories)
            objFilesStream << " -L" << lib_directory;

        for (const auto& lib_binary : lib_binaries)
            objFilesStream << " -l" << lib_binary;

        stream << mCompiler->path(directory) << "\\\n"
               << "    " << objFilesStream.str() << "\\\n"
               << "    -o build/" << program->name() << " || exit 1\n"
               << "echo done.\n";

        cmd = stream.str();
        EHEnd;
    }

private:
    doim::SysExecutableSPtr mCompiler;
};
}
<commit_msg>link execute task<commit_after>//  Copyright © 2015 George Georgiev. All rights reserved.
//

#pragma once

#include "task/cxx/cxx_file_crc_task.h"
#include "task/db/db_put_task.h"
#include "task/sys/ensure_directory_task.h"
#include "task/sys/execute_command_task.h"
#include "dom/cxx/cxx_program.hpp"
#include "doim/cxx/cxx_file.hpp"
#include "doim/db/db_key.hpp"
#include "doim/db/db_value.hpp"
#include "doim/sys/argument.hpp"
#include "doim/sys/command.hpp"
#include "db/database.h"
#include <memory>
#include <set>
#include <sstream>
#include <string>

namespace tool
{
class CxxCompiler;
typedef std::shared_ptr<CxxCompiler> CxxCompilerSPtr;

class CxxCompiler
{
public:
    CxxCompiler(const doim::SysExecutableSPtr& compiler)
        : mCompiler(compiler)
    {
    }

    doim::SysArgumentSetSPtr includeArguments(
        const doim::FsDirectorySPtr& directory,
        const doim::CxxIncludeDirectorySetSPtr& includeDirectories)
    {
        auto arguments = std::make_shared<doim::SysArgumentSet>();

        for (const auto& includeDirectory : *includeDirectories)
        {
            std::string value;
            switch (includeDirectory->type())
            {
                case doim::CxxIncludeDirectory::Type::kUser:
                    value = "-I ";
                    break;
                case doim::CxxIncludeDirectory::Type::kSystem:
                    value = "-isystem ";
                    break;
            }

            value += includeDirectory->directory()->path(directory);

            arguments->insert(doim::gManager->obtainArgument(value));
        }

        return arguments;
    }

    ECode tasks(const doim::FsDirectorySPtr& directory,
                const doim::CxxObjectFileSPtr& objectFile,
                std::vector<tpool::TaskSPtr>& tasks)
    {
        auto task = std::make_shared<task::CxxFileCrcTask>(objectFile->cxxFile());
        EHTest((*task)(), objectFile->file()->path());

        auto key = std::make_shared<doim::DbKey>("file:" +
                                                 objectFile->cxxFile()->file()->path());
        key = doim::gManager->unique(key);

        uint64_t crc;
        db::gDatabase->get(key->bytes(), crc);

        if (task->crc() == crc)
            EHEnd;

        std::cout << "set crc:" << crc << " expected: " << task->crc() << std::endl;

        auto mkdirTask =
            std::make_shared<task::EnsureDirectoryTask>(objectFile->file()->directory());
        tasks.push_back(mkdirTask);

        auto compileArguments =
            includeArguments(directory, objectFile->cxxFile()->cxxIncludeDirectories());

        auto argument_cxxflags = doim::gManager->obtainArgument(
            "-std=c++11 -stdlib=libc++ -O0 -g "
            "-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/"
            "MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk");
        compileArguments->insert(argument_cxxflags);

        auto argument_c = doim::gManager->obtainArgument(
            "-c " + objectFile->cxxFile()->file()->path(directory));
        compileArguments->insert(argument_c);

        auto argument_o =
            doim::gManager->obtainArgument("-o " + objectFile->file()->path(directory));
        compileArguments->insert(argument_o);

        auto compileCommand =
            std::make_shared<doim::SysCommand>(mCompiler, compileArguments);
        auto compileTask = std::make_shared<task::ExecuteCommandTask>(compileCommand);
        tasks.push_back(compileTask);

        auto value = std::make_shared<doim::DbValue>(task->crc());
        auto updateTask = std::make_shared<task::DbPutTask>(key, value);
        tasks.push_back(updateTask);

        EHEnd;
    }

    ECode commands(const doim::FsDirectorySPtr& directory,
                   const dom::CxxProgramSPtr& program,
                   std::string& cmd)
    {
        const auto& intermediate = doim::gManager->obtainDirectory(directory, "build");

        auto arguments = std::make_shared<doim::SysArgumentSet>();
        for (const auto& cxxLibrary : program->recursiveCxxLibraries())
        {
            if (cxxLibrary->binary() != nullptr)
            {
                auto argument_L = doim::gManager->obtainArgument(
                    "-L " + cxxLibrary->binary()->directory()->path(directory));
                arguments->insert(argument_L);

                std::string name = cxxLibrary->binary()->name();
                auto argument_l = doim::gManager->obtainArgument(
                    "-l" + name.substr(3, name.size() - 5));
                arguments->insert(argument_l);
            }
            else
            {
                const auto& objectFiles =
                    cxxLibrary->cxxObjectFiles(directory, intermediate);

                for (const auto& objectFile : objectFiles)
                {
                    auto argument_obj = doim::gManager->obtainArgument(
                        objectFile->file()->path(directory));
                    arguments->insert(argument_obj);

                    std::vector<tpool::TaskSPtr> tsks;
                    EHTest(tasks(directory, objectFile, tsks));
                    for (auto task : tsks)
                    {
                        ILOG("RUN: " + task->description());
                        EHTest((*task)());
                        ILOG("DONE: " + task->description());
                    }
                }
            }
        }

        const auto& objectFiles = program->cxxObjectFiles(directory, intermediate);
        for (const auto& objectFile : objectFiles)
        {
            auto argument_obj =
                doim::gManager->obtainArgument(objectFile->file()->path(directory));
            arguments->insert(argument_obj);

            std::vector<tpool::TaskSPtr> tsks;
            EHTest(tasks(directory, objectFile, tsks));
            for (auto task : tsks)
            {
                ILOG("RUN: " + task->description());
                EHTest((*task)());
                ILOG("DONE: " + task->description());
            }
        }

        auto argument_o = doim::gManager->obtainArgument("-o build/" + program->name());
        arguments->insert(argument_o);

        auto linkCommand = std::make_shared<doim::SysCommand>(mCompiler, arguments);

        auto linkTask = std::make_shared<task::ExecuteCommandTask>(linkCommand);

        ILOG("RUN: " + linkTask->description());
        EHTest((*linkTask)());
        ILOG("DONE: " + linkTask->description());
        EHEnd;
    }

private:
    doim::SysExecutableSPtr mCompiler;
};
}
<|endoftext|>