text: // Copyright 2015 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 <stdlib.h>
  #include <string.h>
  
  #include "src/api-inl.h"
  #include "src/objects-inl.h"
  #include "src/snapshot/code-serializer.h"
  #include "src/version.h"
  #include "src/wasm/module-decoder.h"
  #include "src/wasm/wasm-engine.h"
  #include "src/wasm/wasm-memory.h"
  #include "src/wasm/wasm-module-builder.h"
  #include "src/wasm/wasm-module.h"
  #include "src/wasm/wasm-objects-inl.h"
  #include "src/wasm/wasm-opcodes.h"
  
  #include "test/cctest/cctest.h"
  #include "test/common/wasm/flag-utils.h"
  #include "test/common/wasm/test-signatures.h"
  #include "test/common/wasm/wasm-macro-gen.h"
  #include "test/common/wasm/wasm-module-runner.h"
  
  namespace v8 {
  namespace internal {
  namespace wasm {
  namespace test_run_wasm_module {
  
  using testing::CompileAndInstantiateForTesting;
  
  namespace {
  void Cleanup(Isolate* isolate = CcTest::InitIsolateOnce()) {
    // By sending a low memory notifications, we will try hard to collect all
    // garbage and will therefore also invoke all weak callbacks of actually
    // unreachable persistent handles.
    reinterpret_cast<v8::Isolate*>(isolate)->LowMemoryNotification();
  }
  
  void TestModule(Zone* zone, WasmModuleBuilder* builder,
                  int32_t expected_result) {
    ZoneBuffer buffer(zone);
    builder->WriteTo(buffer);
  
    Isolate* isolate = CcTest::InitIsolateOnce();
    HandleScope scope(isolate);
    testing::SetupIsolateForWasmModule(isolate);
    int32_t result =
        testing::CompileAndRunWasmModule(isolate, buffer.begin(), buffer.end());
    CHECK_EQ(expected_result, result);
  }
  
  void TestModuleException(Zone* zone, WasmModuleBuilder* builder) {
    ZoneBuffer buffer(zone);
    builder->WriteTo(buffer);
  
    Isolate* isolate = CcTest::InitIsolateOnce();
    HandleScope scope(isolate);
    testing::SetupIsolateForWasmModule(isolate);
    v8::TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
    testing::CompileAndRunWasmModule(isolate, buffer.begin(), buffer.end());
    CHECK(try_catch.HasCaught());
    isolate->clear_pending_exception();
  }
  
  void ExportAsMain(WasmFunctionBuilder* f) {
    f->builder()->AddExport(CStrVector("main"), f);
  }
  
  #define EMIT_CODE_WITH_END(f, code)  \
    do {                               \
      f->EmitCode(code, sizeof(code)); \
      f->Emit(kExprEnd);               \
    } while (false)
  
  }  // namespace
  
  TEST(Run_WasmModule_Return114) {
    {
      static const int32_t kReturnValue = 114;
      TestSignatures sigs;
      v8::internal::AccountingAllocator allocator;
      Zone zone(&allocator, ZONE_NAME);
  
      WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
      WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
      ExportAsMain(f);
      byte code[] = {WASM_I32V_2(kReturnValue)};
      EMIT_CODE_WITH_END(f, code);
      TestModule(&zone, builder, kReturnValue);
    }
    Cleanup();
  }
  
  TEST(Run_WasmModule_CallAdd) {
    {
      v8::internal::AccountingAllocator allocator;
      Zone zone(&allocator, ZONE_NAME);
      TestSignatures sigs;
  
      WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
  
      WasmFunctionBuilder* f1 = builder->AddFunction(sigs.i_ii());
      uint16_t param1 = 0;
      uint16_t param2 = 1;
      byte code1[] = {
          WASM_I32_ADD(WASM_GET_LOCAL(param1), WASM_GET_LOCAL(param2))};
      EMIT_CODE_WITH_END(f1, code1);
  
      WasmFunctionBuilder* f2 = builder->AddFunction(sigs.i_v());
  
      ExportAsMain(f2);
      byte code2[] = {
          WASM_CALL_FUNCTION(f1->func_index(), WASM_I32V_2(77), WASM_I32V_1(22))};
      EMIT_CODE_WITH_END(f2, code2);
      TestModule(&zone, builder, 99);
    }
    Cleanup();
  }
  
  TEST(Run_WasmModule_ReadLoadedDataSegment) {
    {
      static const byte kDataSegmentDest0 = 12;
      v8::internal::AccountingAllocator allocator;
      Zone zone(&allocator, ZONE_NAME);
      TestSignatures sigs;
  
      WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
      WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
  
      ExportAsMain(f);
      byte code[] = {
          WASM_LOAD_MEM(MachineType::Int32(), WASM_I32V_1(kDataSegmentDest0))};
      EMIT_CODE_WITH_END(f, code);
      byte data[] = {0xAA, 0xBB, 0xCC, 0xDD};
      builder->AddDataSegment(data, sizeof(data), kDataSegmentDest0);
      TestModule(&zone, builder, 0xDDCCBBAA);
    }
    Cleanup();
  }
  
  TEST(Run_WasmModule_CheckMemoryIsZero) {
    {
      static const int kCheckSize = 16 * 1024;
      v8::internal::AccountingAllocator allocator;
      Zone zone(&allocator, ZONE_NAME);
      TestSignatures sigs;
  
      WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
      WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
  
      uint16_t localIndex = f->AddLocal(kWasmI32);
      ExportAsMain(f);
      byte code[] = {WASM_BLOCK_I(
          WASM_WHILE(
              WASM_I32_LTS(WASM_GET_LOCAL(localIndex), WASM_I32V_3(kCheckSize)),
              WASM_IF_ELSE(
                  WASM_LOAD_MEM(MachineType::Int32(), WASM_GET_LOCAL(localIndex)),
                  WASM_BRV(3, WASM_I32V_1(-1)),
                  WASM_INC_LOCAL_BY(localIndex, 4))),
          WASM_I32V_1(11))};
      EMIT_CODE_WITH_END(f, code);
      TestModule(&zone, builder, 11);
    }
    Cleanup();
  }
  
  TEST(Run_WasmModule_CallMain_recursive) {
    {
      v8::internal::AccountingAllocator allocator;
      Zone zone(&allocator, ZONE_NAME);
      TestSignatures sigs;
  
      WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
      WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
  
      uint16_t localIndex = f->AddLocal(kWasmI32);
      ExportAsMain(f);
      byte code[] = {
          WASM_SET_LOCAL(localIndex,
                         WASM_LOAD_MEM(MachineType::Int32(), WASM_ZERO)),
          WASM_IF_ELSE_I(WASM_I32_LTS(WASM_GET_LOCAL(localIndex), WASM_I32V_1(5)),
                         WASM_SEQ(WASM_STORE_MEM(MachineType::Int32(), WASM_ZERO,
                                                 WASM_INC_LOCAL(localIndex)),
                                  WASM_CALL_FUNCTION0(0)),
                         WASM_I32V_1(55))};
      EMIT_CODE_WITH_END(f, code);
      TestModule(&zone, builder, 55);
    }
    Cleanup();
  }
  
  TEST(Run_WasmModule_Global) {
    {
      v8::internal::AccountingAllocator allocator;
      Zone zone(&allocator, ZONE_NAME);
      TestSignatures sigs;
  
      WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
      uint32_t global1 = builder->AddGlobal(kWasmI32, false);
      uint32_t global2 = builder->AddGlobal(kWasmI32, false);
      WasmFunctionBuilder* f1 = builder->AddFunction(sigs.i_v());
      byte code1[] = {
          WASM_I32_ADD(WASM_GET_GLOBAL(global1), WASM_GET_GLOBAL(global2))};
      EMIT_CODE_WITH_END(f1, code1);
      WasmFunctionBuilder* f2 = builder->AddFunction(sigs.i_v());
      ExportAsMain(f2);
      byte code2[] = {WASM_SET_GLOBAL(global1, WASM_I32V_1(56)),
                      WASM_SET_GLOBAL(global2, WASM_I32V_1(41)),
                      WASM_RETURN1(WASM_CALL_FUNCTION0(f1->func_index()))};
      EMIT_CODE_WITH_END(f2, code2);
      TestModule(&zone, builder, 97);
    }
    Cleanup();
  }
  
  TEST(MemorySize) {
    {
      // Initial memory size is 16, see wasm-module-builder.cc
      static const int kExpectedValue = 16;
      TestSignatures sigs;
      v8::internal::AccountingAllocator allocator;
      Zone zone(&allocator, ZONE_NAME);
  
      WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
      WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
      ExportAsMain(f);
      byte code[] = {WASM_MEMORY_SIZE};
      EMIT_CODE_WITH_END(f, code);
      TestModule(&zone, builder, kExpectedValue);
    }
    Cleanup();
  }
  
  TEST(Run_WasmModule_MemSize_GrowMem) {
    {
      // Initial memory size = 16 + MemoryGrow(10)
      static const int kExpectedValue = 26;
      TestSignatures sigs;
      v8::internal::AccountingAllocator allocator;
      Zone zone(&allocator, ZONE_NAME);
  
      WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
      WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
      ExportAsMain(f);
      byte code[] = {WASM_GROW_MEMORY(WASM_I32V_1(10)), WASM_DROP,
                     WASM_MEMORY_SIZE};
      EMIT_CODE_WITH_END(f, code);
      TestModule(&zone, builder, kExpectedValue);
    }
    Cleanup();
  }
  
  TEST(MemoryGrowZero) {
    {
      // Initial memory size is 16, see wasm-module-builder.cc
      static const int kExpectedValue = 16;
      TestSignatures sigs;
      v8::internal::AccountingAllocator allocator;
      Zone zone(&allocator, ZONE_NAME);
  
      WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
      WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
      ExportAsMain(f);
      byte code[] = {WASM_GROW_MEMORY(WASM_I32V(0))};
      EMIT_CODE_WITH_END(f, code);
      TestModule(&zone, builder, kExpectedValue);
    }
    Cleanup();
  }
  
  class InterruptThread : public v8::base::Thread {
   public:
    explicit InterruptThread(Isolate* isolate, int32_t* memory)
        : Thread(Options("TestInterruptLoop")),
          isolate_(isolate),
          memory_(memory) {}
  
    static void OnInterrupt(v8::Isolate* isolate, void* data) {
      int32_t* m = reinterpret_cast<int32_t*>(data);
      // Set the interrupt location to 0 to break the loop in {TestInterruptLoop}.
      Address ptr = reinterpret_cast<Address>(&m[interrupt_location_]);
      WriteLittleEndianValue<int32_t>(ptr, interrupt_value_);
    }
  
    void Run() override {
      // Wait for the main thread to write the signal value.
      int32_t val = 0;
      do {
        val = memory_[0];
        val = ReadLittleEndianValue<int32_t>(reinterpret_cast<Address>(&val));
      } while (val != signal_value_);
      isolate_->RequestInterrupt(&OnInterrupt, const_cast<int32_t*>(memory_));
    }
  
    Isolate* isolate_;
    volatile int32_t* memory_;
    static const int32_t interrupt_location_ = 10;
    static const int32_t interrupt_value_ = 154;
    static const int32_t signal_value_ = 1221;
  };
  
  TEST(TestInterruptLoop) {
    {
      // Do not dump the module of this test because it contains an infinite loop.
      if (FLAG_dump_wasm_module) return;
  
      // This test tests that WebAssembly loops can be interrupted, i.e. that if
      // an
      // InterruptCallback is registered by {Isolate::RequestInterrupt}, then the
      // InterruptCallback is eventually called even if a loop in WebAssembly code
      // is executed.
      // Test setup:
      // The main thread executes a WebAssembly function with a loop. In the loop
      // {signal_value_} is written to memory to signal a helper thread that the
      // main thread reached the loop in the WebAssembly program. When the helper
      // thread reads {signal_value_} from memory, it registers the
      // InterruptCallback. Upon exeution, the InterruptCallback write into the
      // WebAssemblyMemory to end the loop in the WebAssembly program.
      TestSignatures sigs;
      Isolate* isolate = CcTest::InitIsolateOnce();
      v8::internal::AccountingAllocator allocator;
      Zone zone(&allocator, ZONE_NAME);
  
      WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
      WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
      ExportAsMain(f);
      byte code[] = {
          WASM_LOOP(
              WASM_IFB(WASM_NOT(WASM_LOAD_MEM(
                           MachineType::Int32(),
                           WASM_I32V(InterruptThread::interrupt_location_ * 4))),
                       WASM_STORE_MEM(MachineType::Int32(), WASM_ZERO,
                                      WASM_I32V(InterruptThread::signal_value_)),
                       WASM_BR(1))),
          WASM_I32V(121)};
      EMIT_CODE_WITH_END(f, code);
      ZoneBuffer buffer(&zone);
      builder->WriteTo(buffer);
  
      HandleScope scope(isolate);
      testing::SetupIsolateForWasmModule(isolate);
      ErrorThrower thrower(isolate, "Test");
      const Handle<WasmInstanceObject> instance =
          CompileAndInstantiateForTesting(
              isolate, &thrower, ModuleWireBytes(buffer.begin(), buffer.end()))
              .ToHandleChecked();
  
      Handle<JSArrayBuffer> memory(instance->memory_object()->array_buffer(),
                                   isolate);
      int32_t* memory_array = reinterpret_cast<int32_t*>(memory->backing_store());
  
      InterruptThread thread(isolate, memory_array);
      thread.Start();
      testing::RunWasmModuleForTesting(isolate, instance, 0, nullptr);
      Address address = reinterpret_cast<Address>(
          &memory_array[InterruptThread::interrupt_location_]);
      CHECK_EQ(InterruptThread::interrupt_value_,
               ReadLittleEndianValue<int32_t>(address));
    }
    Cleanup();
  }
  
  TEST(Run_WasmModule_MemoryGrowInIf) {
    {
      TestSignatures sigs;
      v8::internal::AccountingAllocator allocator;
      Zone zone(&allocator, ZONE_NAME);
      WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
      WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
      ExportAsMain(f);
      byte code[] = {WASM_IF_ELSE_I(WASM_I32V(0), WASM_GROW_MEMORY(WASM_I32V(1)),
                                    WASM_I32V(12))};
      EMIT_CODE_WITH_END(f, code);
      TestModule(&zone, builder, 12);
    }
    Cleanup();
  }
  
  TEST(Run_WasmModule_GrowMemOobOffset) {
    {
      static const int kPageSize = 0x10000;
      // Initial memory size = 16 + MemoryGrow(10)
      static const int index = kPageSize * 17 + 4;
      int value = 0xACED;
      TestSignatures sigs;
      v8::internal::AccountingAllocator allocator;
      Zone zone(&allocator, ZONE_NAME);
  
      WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
      WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
      ExportAsMain(f);
      byte code[] = {WASM_GROW_MEMORY(WASM_I32V_1(1)),
                     WASM_STORE_MEM(MachineType::Int32(), WASM_I32V(index),
                                    WASM_I32V(value))};
      EMIT_CODE_WITH_END(f, code);
      TestModuleException(&zone, builder);
    }
    Cleanup();
  }
  
  TEST(Run_WasmModule_GrowMemOobFixedIndex) {
    {
      static const int kPageSize = 0x10000;
      // Initial memory size = 16 + MemoryGrow(10)
      static const int index = kPageSize * 26 + 4;
      int value = 0xACED;
      TestSignatures sigs;
      Isolate* isolate = CcTest::InitIsolateOnce();
      Zone zone(isolate->allocator(), ZONE_NAME);
  
      WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
      WasmFunctionBuilder* f = builder->AddFunction(sigs.i_i());
      ExportAsMain(f);
      byte code[] = {WASM_GROW_MEMORY(WASM_GET_LOCAL(0)), WASM_DROP,
                     WASM_STORE_MEM(MachineType::Int32(), WASM_I32V(index),
                                    WASM_I32V(value)),
                     WASM_LOAD_MEM(MachineType::Int32(), WASM_I32V(index))};
      EMIT_CODE_WITH_END(f, code);
  
      HandleScope scope(isolate);
      ZoneBuffer buffer(&zone);
      builder->WriteTo(buffer);
      testing::SetupIsolateForWasmModule(isolate);
  
      ErrorThrower thrower(isolate, "Test");
      Handle<WasmInstanceObject> instance =
          CompileAndInstantiateForTesting(
              isolate, &thrower, ModuleWireBytes(buffer.begin(), buffer.end()))
              .ToHandleChecked();
  
      // Initial memory size is 16 pages, should trap till index > MemSize on
      // consecutive GrowMem calls
      for (uint32_t i = 1; i < 5; i++) {
        Handle<Object> params[1] = {Handle<Object>(Smi::FromInt(i), isolate)};
        v8::TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
        testing::RunWasmModuleForTesting(isolate, instance, 1, params);
        CHECK(try_catch.HasCaught());
        isolate->clear_pending_exception();
      }
  
      Handle<Object> params[1] = {Handle<Object>(Smi::FromInt(1), isolate)};
      int32_t result =
          testing::RunWasmModuleForTesting(isolate, instance, 1, params);
      CHECK_EQ(0xACED, result);
    }
    Cleanup();
  }
  
  TEST(Run_WasmModule_GrowMemOobVariableIndex) {
    {
      static const int kPageSize = 0x10000;
      int value = 0xACED;
      TestSignatures sigs;
      Isolate* isolate = CcTest::InitIsolateOnce();
      v8::internal::AccountingAllocator allocator;
      Zone zone(&allocator, ZONE_NAME);
  
      WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
      WasmFunctionBuilder* f = builder->AddFunction(sigs.i_i());
      ExportAsMain(f);
      byte code[] = {WASM_GROW_MEMORY(WASM_I32V_1(1)), WASM_DROP,
                     WASM_STORE_MEM(MachineType::Int32(), WASM_GET_LOCAL(0),
                                    WASM_I32V(value)),
                     WASM_LOAD_MEM(MachineType::Int32(), WASM_GET_LOCAL(0))};
      EMIT_CODE_WITH_END(f, code);
  
      HandleScope scope(isolate);
      ZoneBuffer buffer(&zone);
      builder->WriteTo(buffer);
      testing::SetupIsolateForWasmModule(isolate);
  
      ErrorThrower thrower(isolate, "Test");
      Handle<WasmInstanceObject> instance =
          CompileAndInstantiateForTesting(
              isolate, &thrower, ModuleWireBytes(buffer.begin(), buffer.end()))
              .ToHandleChecked();
  
      // Initial memory size is 16 pages, should trap till index > MemSize on
      // consecutive GrowMem calls
      for (int i = 1; i < 5; i++) {
        Handle<Object> params[1] = {
            Handle<Object>(Smi::FromInt((16 + i) * kPageSize - 3), isolate)};
        v8::TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
        testing::RunWasmModuleForTesting(isolate, instance, 1, params);
        CHECK(try_catch.HasCaught());
        isolate->clear_pending_exception();
      }
  
      for (int i = 1; i < 5; i++) {
        Handle<Object> params[1] = {
            Handle<Object>(Smi::FromInt((20 + i) * kPageSize - 4), isolate)};
        int32_t result =
            testing::RunWasmModuleForTesting(isolate, instance, 1, params);
        CHECK_EQ(0xACED, result);
      }
  
      v8::TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
      Handle<Object> params[1] = {
          Handle<Object>(Smi::FromInt(25 * kPageSize), isolate)};
      testing::RunWasmModuleForTesting(isolate, instance, 1, params);
      CHECK(try_catch.HasCaught());
      isolate->clear_pending_exception();
    }
    Cleanup();
  }
  
  TEST(Run_WasmModule_Global_init) {
    {
      v8::internal::AccountingAllocator allocator;
      Zone zone(&allocator, ZONE_NAME);
      TestSignatures sigs;
  
      WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
      uint32_t global1 =
          builder->AddGlobal(kWasmI32, false, false, WasmInitExpr(777777));
      uint32_t global2 =
          builder->AddGlobal(kWasmI32, false, false, WasmInitExpr(222222));
      WasmFunctionBuilder* f1 = builder->AddFunction(sigs.i_v());
      byte code[] = {
          WASM_I32_ADD(WASM_GET_GLOBAL(global1), WASM_GET_GLOBAL(global2))};
      EMIT_CODE_WITH_END(f1, code);
      ExportAsMain(f1);
      TestModule(&zone, builder, 999999);
    }
    Cleanup();
  }
  
  template <typename CType>
  static void RunWasmModuleGlobalInitTest(ValueType type, CType expected) {
    {
      v8::internal::AccountingAllocator allocator;
      Zone zone(&allocator, ZONE_NAME);
      TestSignatures sigs;
  
      ValueType types[] = {type};
      FunctionSig sig(1, 0, types);
  
      for (int padding = 0; padding < 5; padding++) {
        // Test with a simple initializer
        WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
  
        for (int i = 0; i < padding; i++) {  // pad global before
          builder->AddGlobal(kWasmI32, false, false, WasmInitExpr(i + 20000));
        }
        uint32_t global =
            builder->AddGlobal(type, false, false, WasmInitExpr(expected));
        for (int i = 0; i < padding; i++) {  // pad global after
          builder->AddGlobal(kWasmI32, false, false, WasmInitExpr(i + 30000));
        }
  
        WasmFunctionBuilder* f1 = builder->AddFunction(&sig);
        byte code[] = {WASM_GET_GLOBAL(global)};
        EMIT_CODE_WITH_END(f1, code);
        ExportAsMain(f1);
        TestModule(&zone, builder, expected);
      }
    }
    Cleanup();
  }
  
  TEST(Run_WasmModule_Global_i32) {
    RunWasmModuleGlobalInitTest<int32_t>(kWasmI32, -983489);
    RunWasmModuleGlobalInitTest<int32_t>(kWasmI32, 11223344);
  }
  
  TEST(Run_WasmModule_Global_f32) {
    RunWasmModuleGlobalInitTest<float>(kWasmF32, -983.9f);
    RunWasmModuleGlobalInitTest<float>(kWasmF32, 1122.99f);
  }
  
  TEST(Run_WasmModule_Global_f64) {
    RunWasmModuleGlobalInitTest<double>(kWasmF64, -833.9);
    RunWasmModuleGlobalInitTest<double>(kWasmF64, 86374.25);
  }
  
  TEST(InitDataAtTheUpperLimit) {
    {
      Isolate* isolate = CcTest::InitIsolateOnce();
      HandleScope scope(isolate);
      testing::SetupIsolateForWasmModule(isolate);
  
      ErrorThrower thrower(isolate, "Run_WasmModule_InitDataAtTheUpperLimit");
  
      const byte data[] = {
          WASM_MODULE_HEADER,   // --
          kMemorySectionCode,   // --
          U32V_1(4),            // section size
          ENTRY_COUNT(1),       // --
          kHasMaximumFlag,      // --
          1,                    // initial size
          2,                    // maximum size
          kDataSectionCode,     // --
          U32V_1(9),            // section size
          ENTRY_COUNT(1),       // --
          0,                    // linear memory index
          WASM_I32V_3(0xFFFF),  // destination offset
          kExprEnd,
          U32V_1(1),  // source size
          'c'         // data bytes
      };
  
      CompileAndInstantiateForTesting(
          isolate, &thrower, ModuleWireBytes(data, data + arraysize(data)));
      if (thrower.error()) {
        thrower.Reify()->Print();
        FATAL("compile or instantiate error");
      }
    }
    Cleanup();
  }
  
  TEST(EmptyMemoryNonEmptyDataSegment) {
    {
      Isolate* isolate = CcTest::InitIsolateOnce();
      HandleScope scope(isolate);
      testing::SetupIsolateForWasmModule(isolate);
  
      ErrorThrower thrower(isolate, "Run_WasmModule_InitDataAtTheUpperLimit");
  
      const byte data[] = {
          WASM_MODULE_HEADER,  // --
          kMemorySectionCode,  // --
          U32V_1(4),           // section size
          ENTRY_COUNT(1),      // --
          kHasMaximumFlag,     // --
          0,                   // initial size
          0,                   // maximum size
          kDataSectionCode,    // --
          U32V_1(7),           // section size
          ENTRY_COUNT(1),      // --
          0,                   // linear memory index
          WASM_I32V_1(8),      // destination offset
          kExprEnd,
          U32V_1(1),  // source size
          'c'         // data bytes
      };
  
      CompileAndInstantiateForTesting(
          isolate, &thrower, ModuleWireBytes(data, data + arraysize(data)));
      // It should not be possible to instantiate this module.
      CHECK(thrower.error());
    }
    Cleanup();
  }
  
  TEST(EmptyMemoryEmptyDataSegment) {
    {
      Isolate* isolate = CcTest::InitIsolateOnce();
      HandleScope scope(isolate);
      testing::SetupIsolateForWasmModule(isolate);
  
      ErrorThrower thrower(isolate, "Run_WasmModule_InitDataAtTheUpperLimit");
  
      const byte data[] = {
          WASM_MODULE_HEADER,  // --
          kMemorySectionCode,  // --
          U32V_1(4),           // section size
          ENTRY_COUNT(1),      // --
          kHasMaximumFlag,     // --
          0,                   // initial size
          0,                   // maximum size
          kDataSectionCode,    // --
          U32V_1(6),           // section size
          ENTRY_COUNT(1),      // --
          0,                   // linear memory index
          WASM_I32V_1(0),      // destination offset
          kExprEnd,
          U32V_1(0),  // source size
      };
  
      CompileAndInstantiateForTesting(
          isolate, &thrower, ModuleWireBytes(data, data + arraysize(data)));
      // It should be possible to instantiate this module.
      CHECK(!thrower.error());
    }
    Cleanup();
  }
  
  TEST(MemoryWithOOBEmptyDataSegment) {
    {
      Isolate* isolate = CcTest::InitIsolateOnce();
      HandleScope scope(isolate);
      testing::SetupIsolateForWasmModule(isolate);
  
      ErrorThrower thrower(isolate, "Run_WasmModule_InitDataAtTheUpperLimit");
  
      const byte data[] = {
          WASM_MODULE_HEADER,      // --
          kMemorySectionCode,      // --
          U32V_1(4),               // section size
          ENTRY_COUNT(1),          // --
          kHasMaximumFlag,         // --
          1,                       // initial size
          1,                       // maximum size
          kDataSectionCode,        // --
          U32V_1(9),               // section size
          ENTRY_COUNT(1),          // --
          0,                       // linear memory index
          WASM_I32V_4(0x2468ACE),  // destination offset
          kExprEnd,
          U32V_1(0),  // source size
      };
  
      CompileAndInstantiateForTesting(
          isolate, &thrower, ModuleWireBytes(data, data + arraysize(data)));
      // It should not be possible to instantiate this module.
      CHECK(thrower.error());
    }
    Cleanup();
  }
  
  // Utility to free the allocated memory for a buffer that is manually
  // externalized in a test.
  struct ManuallyExternalizedBuffer {
    Isolate* isolate_;
    Handle<JSArrayBuffer> buffer_;
    void* allocation_base_;
    size_t allocation_length_;
    bool const should_free_;
  
    ManuallyExternalizedBuffer(JSArrayBuffer buffer, Isolate* isolate)
        : isolate_(isolate),
          buffer_(buffer, isolate),
          allocation_base_(buffer->allocation_base()),
          allocation_length_(buffer->allocation_length()),
          should_free_(!isolate_->wasm_engine()->memory_tracker()->IsWasmMemory(
              buffer->backing_store())) {
      if (!isolate_->wasm_engine()->memory_tracker()->IsWasmMemory(
              buffer->backing_store())) {
        v8::Utils::ToLocal(buffer_)->Externalize();
      }
    }
    ~ManuallyExternalizedBuffer() {
      if (should_free_) {
        buffer_->FreeBackingStoreFromMainThread();
      }
    }
  };
  
  TEST(Run_WasmModule_Buffer_Externalized_GrowMem) {
    {
      Isolate* isolate = CcTest::InitIsolateOnce();
      HandleScope scope(isolate);
      TestSignatures sigs;
      v8::internal::AccountingAllocator allocator;
      Zone zone(&allocator, ZONE_NAME);
  
      WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
      WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
      ExportAsMain(f);
      byte code[] = {WASM_GROW_MEMORY(WASM_I32V_1(6)), WASM_DROP,
                     WASM_MEMORY_SIZE};
      EMIT_CODE_WITH_END(f, code);
  
      ZoneBuffer buffer(&zone);
      builder->WriteTo(buffer);
      testing::SetupIsolateForWasmModule(isolate);
      ErrorThrower thrower(isolate, "Test");
      const Handle<WasmInstanceObject> instance =
          CompileAndInstantiateForTesting(
              isolate, &thrower, ModuleWireBytes(buffer.begin(), buffer.end()))
              .ToHandleChecked();
      Handle<WasmMemoryObject> memory_object(instance->memory_object(), isolate);
  
      // Fake the Embedder flow by externalizing the array buffer.
      ManuallyExternalizedBuffer buffer1(memory_object->array_buffer(), isolate);
  
      // Grow using the API.
      uint32_t result = WasmMemoryObject::Grow(isolate, memory_object, 4);
      CHECK_EQ(16, result);
      CHECK(buffer1.buffer_->was_detached());  // growing always detaches
      CHECK_EQ(0, buffer1.buffer_->byte_length());
  
      CHECK_NE(*buffer1.buffer_, memory_object->array_buffer());
  
      // Fake the Embedder flow by externalizing the array buffer.
      ManuallyExternalizedBuffer buffer2(memory_object->array_buffer(), isolate);
  
      // Grow using an internal WASM bytecode.
      result = testing::RunWasmModuleForTesting(isolate, instance, 0, nullptr);
      CHECK_EQ(26, result);
      CHECK(buffer2.buffer_->was_detached());  // growing always detaches
      CHECK_EQ(0, buffer2.buffer_->byte_length());
      CHECK_NE(*buffer2.buffer_, memory_object->array_buffer());
    }
    Cleanup();
  }
  
  TEST(Run_WasmModule_Buffer_Externalized_GrowMemMemSize) {
    {
      Isolate* isolate = CcTest::InitIsolateOnce();
      HandleScope scope(isolate);
      Handle<JSArrayBuffer> buffer;
      CHECK(wasm::NewArrayBuffer(isolate, 16 * kWasmPageSize).ToHandle(&buffer));
      Handle<WasmMemoryObject> mem_obj =
          WasmMemoryObject::New(isolate, buffer, 100);
      auto const contents = v8::Utils::ToLocal(buffer)->Externalize();
      int32_t result = WasmMemoryObject::Grow(isolate, mem_obj, 0);
      CHECK_EQ(16, result);
      constexpr bool is_wasm_memory = true;
      const JSArrayBuffer::Allocation allocation{contents.AllocationBase(),
                                                 contents.AllocationLength(),
                                                 contents.Data(), is_wasm_memory};
      JSArrayBuffer::FreeBackingStore(isolate, allocation);
    }
    Cleanup();
  }
  
  TEST(Run_WasmModule_Buffer_Externalized_Detach) {
    {
      // Regression test for
      // https://bugs.chromium.org/p/chromium/issues/detail?id=731046
      Isolate* isolate = CcTest::InitIsolateOnce();
      HandleScope scope(isolate);
      Handle<JSArrayBuffer> buffer;
      CHECK(wasm::NewArrayBuffer(isolate, 16 * kWasmPageSize).ToHandle(&buffer));
      auto const contents = v8::Utils::ToLocal(buffer)->Externalize();
      wasm::DetachMemoryBuffer(isolate, buffer, true);
      constexpr bool is_wasm_memory = true;
      const JSArrayBuffer::Allocation allocation{contents.AllocationBase(),
                                                 contents.AllocationLength(),
                                                 contents.Data(), is_wasm_memory};
      JSArrayBuffer::FreeBackingStore(isolate, allocation);
    }
    Cleanup();
  }
  
  TEST(Run_WasmModule_Buffer_Externalized_Regression_UseAfterFree) {
    // Regresion test for https://crbug.com/813876
    Isolate* isolate = CcTest::InitIsolateOnce();
    HandleScope scope(isolate);
    Handle<JSArrayBuffer> buffer;
    CHECK(wasm::NewArrayBuffer(isolate, 16 * kWasmPageSize).ToHandle(&buffer));
    Handle<WasmMemoryObject> mem = WasmMemoryObject::New(isolate, buffer, 128);
    auto contents = v8::Utils::ToLocal(buffer)->Externalize();
    WasmMemoryObject::Grow(isolate, mem, 0);
    constexpr bool is_wasm_memory = true;
    JSArrayBuffer::FreeBackingStore(
        isolate, JSArrayBuffer::Allocation(contents.AllocationBase(),
                                           contents.AllocationLength(),
                                           contents.Data(), is_wasm_memory));
    // Make sure we can write to the buffer without crashing
    uint32_t* int_buffer =
        reinterpret_cast<uint32_t*>(mem->array_buffer()->backing_store());
    int_buffer[0] = 0;
  }
  
  #if V8_TARGET_ARCH_64_BIT
  TEST(Run_WasmModule_Reclaim_Memory) {
    // Make sure we can allocate memories without running out of address space.
    Isolate* isolate = CcTest::InitIsolateOnce();
    Handle<JSArrayBuffer> buffer;
    for (int i = 0; i < 256; ++i) {
      HandleScope scope(isolate);
      CHECK(NewArrayBuffer(isolate, kWasmPageSize).ToHandle(&buffer));
    }
  }
  #endif
  
  TEST(AtomicOpDisassembly) {
    {
      EXPERIMENTAL_FLAG_SCOPE(threads);
      TestSignatures sigs;
      Isolate* isolate = CcTest::InitIsolateOnce();
      v8::internal::AccountingAllocator allocator;
      Zone zone(&allocator, ZONE_NAME);
  
      WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
      builder->SetHasSharedMemory();
      builder->SetMaxMemorySize(16);
      WasmFunctionBuilder* f = builder->AddFunction(sigs.i_i());
      ExportAsMain(f);
      byte code[] = {
          WASM_ATOMICS_STORE_OP(kExprI32AtomicStore, WASM_ZERO, WASM_GET_LOCAL(0),
                                MachineRepresentation::kWord32),
          WASM_ATOMICS_LOAD_OP(kExprI32AtomicLoad, WASM_ZERO,
                               MachineRepresentation::kWord32)};
      EMIT_CODE_WITH_END(f, code);
  
      HandleScope scope(isolate);
      ZoneBuffer buffer(&zone);
      builder->WriteTo(buffer);
      testing::SetupIsolateForWasmModule(isolate);
  
      ErrorThrower thrower(isolate, "Test");
      auto enabled_features = WasmFeaturesFromIsolate(isolate);
      MaybeHandle<WasmModuleObject> module_object =
          isolate->wasm_engine()->SyncCompile(
              isolate, enabled_features, &thrower,
              ModuleWireBytes(buffer.begin(), buffer.end()));
  
      module_object.ToHandleChecked()->DisassembleFunction(0);
    }
    Cleanup();
  }
  
  #undef EMIT_CODE_WITH_END
  
  }  // namespace test_run_wasm_module
  }  // namespace wasm
  }  // namespace internal
  }  // namespace v8
  