/*
 * Copyright (c) 2016-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#include <atomic>
#include <chrono>
#include <string>

extern void* __infer_taint_source();
extern void __infer_taint_sink(void*);

namespace basics {

class Obj {
 public:
  void* method_source() { return (void*)0; }
  void method_sink(void*) {}
  static void* static_source() { return (void*)0; }
  static void static_sink(void*) {}
  std::string string_source(int i) { return ""; }
  static int taint_arg_source(int* arg) { return 1; }
  void string_sink(std::string) {}
  static std::string* sanitizer1(std::string* input) { return input; }
  static std::string sanitizer2(const std::string& input) { return input; }

  std::string field1;
  std::string field2;

  void endpoint(std::string source1, void* source2) {
    this->string_sink(source1);
    __infer_taint_sink(source2);
  }
};

void* returnSource() { return __infer_taint_source(); }

void callSink(void* param) { __infer_taint_sink(param); }

void* id(void* param) { return param; }

void sourceToSinkDirectBad() {
  void* source = __infer_taint_source();
  __infer_taint_sink(source);
}

void returnSourceToSinkBad() {
  void* source = returnSource();
  __infer_taint_sink(source);
}

void sourceThenCallSinkBad() {
  void* source = __infer_taint_source();
  callSink(source);
}

void propagateBad() {
  void* source = __infer_taint_source();
  void* launderedSource = id(source);
  callSink(launderedSource);
}

// make sure specifying external sources/sinks as instance methods works
void object_source_sink_bad(Obj obj) {
  void* source = obj.method_source();
  obj.method_sink(source);
}

// make sure specifying external sources/sinks as static methods works
void static_source_sink_bad(Obj obj) {
  void* source = Obj::static_source();
  Obj::static_sink(source);
}

template <class T>
T* template_source() {
  return nullptr;
}

template <class T>
void template_sink(T) {}

void template_source_bad() {
  void* source = template_source<void*>();
  __infer_taint_sink(source);
}

void string_source_bad(Obj obj) {
  std::string source = obj.string_source(5);
  obj.string_sink(source);
}

void via_field_bad1() {
  Obj* obj = new Obj();
  obj->field1 = *template_source<std::string>();
  template_sink<std::string>(obj->field1);
}

void via_field_bad2(Obj* obj) {
  obj->field1 = *template_source<std::string>();
  template_sink<std::string>(obj->field1);
}

void via_field_ok1() {
  Obj* obj = new Obj();
  obj->field1 = *template_source<std::string>();
  obj->field1 = nullptr;
  template_sink<std::string>(obj->field1);
}

void via_field_ok2() {
  Obj* obj = new Obj();
  obj->field1 = *template_source<std::string>();
  template_sink<std::string>(obj->field2);
}

template <class T>
T* id1(T* t) {
  return t;
}

template <class T>
T id2(T t) {
  return t;
}

void via_passthrough_bad1(Obj* obj) {
  std::string source = obj->string_source(0);
  std::string* source_ptr = &source;
  std::string* laundered_source = id1<std::string>(source_ptr);
  obj->string_sink(*laundered_source);
}

void via_passthrough_bad2(Obj* obj) {
  std::string source = obj->string_source(0);
  std::string laundered_source = id2<std::string>(source);
  obj->string_sink(laundered_source);
}

void taint_arg_source_bad() {
  int source;
  Obj::taint_arg_source(&source);
  __infer_taint_sink((void*)source);
}

void taint_arg_source_ok() {
  int source;
  int ret = Obj::taint_arg_source(&source);
  __infer_taint_sink((void*)ret); // return value is not a source
}

void via_sanitizer_ok1(Obj* obj) {
  std::string* source = &obj->string_source(0);
  std::string* sanitized = Obj::sanitizer1(source);
  obj->string_sink(*sanitized);
}

void via_sanitizer_ok2(Obj* obj) {
  std::string source = obj->string_source(0);
  std::string sanitized = obj->sanitizer2(source);
  obj->string_sink(sanitized);
}

std::string* unsanitized_bad(Obj* obj) {
  std::string* source = &obj->string_source(0);
  std::string* sanitized = Obj::sanitizer1(source);
  obj->string_sink(*source);
  return sanitized;
}

void funCall_bad2(int x, void* t) { __infer_taint_sink(t); }

void funCall_bad1() { funCall_bad2(0, __infer_taint_source()); }

void atomic_eq(std::atomic<std::chrono::duration<int, std::centi>> x,
               std::chrono::duration<int, std::centi> y) {
  // this gets translated as operator=(x, y, &tmp_return), which used to cause a
  // crash
  x = y;
}
struct node {
  struct node* prev;
  struct node* next;
};

// we used to hang on this example before the widening operator was fixed
void loop_ok() {
  struct node* init;
  struct node* tmp;

  while (1) {
    tmp->next = init;
    init = tmp;
    tmp->prev = init;
  }
}

void ret_void_ok() { return; }

void ret_void_transitive_ok() { return ret_void_ok(); }

} // namespace basics
