// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/api/api-inl.h"
#include "src/init/v8.h"

#include "src/wasm/wasm-code-manager.h"
#include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-module-builder.h"

#include "test/cctest/cctest.h"

#include "test/common/wasm/test-signatures.h"
#include "test/common/wasm/wasm-macro-gen.h"

namespace v8 {
namespace internal {
namespace wasm {

namespace {

class TestResolver : public CompilationResultResolver {
 public:
  explicit TestResolver(std::atomic<int>* pending)
      : native_module_(nullptr), pending_(pending) {}

  void OnCompilationSucceeded(i::Handle<i::WasmModuleObject> module) override {
    if (!module.is_null()) {
      native_module_ = module->shared_native_module();
      pending_->fetch_sub(1);
    }
  }

  void OnCompilationFailed(i::Handle<i::Object> error_reason) override {
    CHECK(false);
  }

  std::shared_ptr<NativeModule> native_module() { return native_module_; }

 private:
  std::shared_ptr<NativeModule> native_module_;
  std::atomic<int>* pending_;
};

// Create a valid module such that the bytes depend on {n}.
ZoneBuffer GetValidModuleBytes(Zone* zone, int n) {
  ZoneBuffer buffer(zone);
  TestSignatures sigs;
  WasmModuleBuilder builder(zone);
  {
    WasmFunctionBuilder* f = builder.AddFunction(sigs.v_v());
    uint8_t code[] = {kExprI32Const, n, kExprDrop, kExprEnd};
    f->EmitCode(code, arraysize(code));
  }
  builder.WriteTo(&buffer);
  return buffer;
}

}  // namespace

TEST(TestAsyncCache) {
  CcTest::InitializeVM();
  i::HandleScope internal_scope_(CcTest::i_isolate());
  AccountingAllocator allocator;
  Zone zone(&allocator, "CompilationCacheTester");

  auto bufferA = GetValidModuleBytes(&zone, 0);
  auto bufferB = GetValidModuleBytes(&zone, 1);

  std::atomic<int> pending(3);
  auto resolverA1 = std::make_shared<TestResolver>(&pending);
  auto resolverA2 = std::make_shared<TestResolver>(&pending);
  auto resolverB = std::make_shared<TestResolver>(&pending);

  CcTest::i_isolate()->wasm_engine()->AsyncCompile(
      CcTest::i_isolate(), WasmFeatures::All(), resolverA1,
      ModuleWireBytes(bufferA.begin(), bufferA.end()), true,
      "WebAssembly.compile");
  CcTest::i_isolate()->wasm_engine()->AsyncCompile(
      CcTest::i_isolate(), WasmFeatures::All(), resolverA2,
      ModuleWireBytes(bufferA.begin(), bufferA.end()), true,
      "WebAssembly.compile");
  CcTest::i_isolate()->wasm_engine()->AsyncCompile(
      CcTest::i_isolate(), WasmFeatures::All(), resolverB,
      ModuleWireBytes(bufferB.begin(), bufferB.end()), true,
      "WebAssembly.compile");

  while (pending > 0) {
    v8::platform::PumpMessageLoop(i::V8::GetCurrentPlatform(),
                                  CcTest::isolate());
  }

  CHECK_EQ(resolverA1->native_module(), resolverA2->native_module());
  CHECK_NE(resolverA1->native_module(), resolverB->native_module());
}

}  // namespace wasm
}  // namespace internal
}  // namespace v8