普通文本  |  958行  |  27.91 KB

// 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 "test/unittests/test-utils.h"

#include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-opcodes.h"

namespace v8 {
namespace internal {
namespace wasm {

class WasmModuleVerifyTest : public TestWithZone {
 public:
  ModuleResult DecodeModule(const byte* module_start, const byte* module_end) {
    return DecodeWasmModule(nullptr, zone(), module_start, module_end, false,
                            false);
  }
};


#define EXPECT_VERIFIES(data)                                         \
  do {                                                                \
    ModuleResult result = DecodeModule(data, data + arraysize(data)); \
    EXPECT_TRUE(result.ok());                                         \
    if (result.val) delete result.val;                                \
  } while (false)


#define EXPECT_FAILURE(data)                                          \
  do {                                                                \
    ModuleResult result = DecodeModule(data, data + arraysize(data)); \
    EXPECT_FALSE(result.ok());                                        \
    if (result.val) delete result.val;                                \
  } while (false)


struct LocalTypePair {
  uint8_t code;
  LocalType type;
} kLocalTypes[] = {{kLocalI32, kAstI32},
                   {kLocalI64, kAstI64},
                   {kLocalF32, kAstF32},
                   {kLocalF64, kAstF64}};


TEST_F(WasmModuleVerifyTest, DecodeEmpty) {
  static const byte data[1]{kDeclEnd};
  {
    ModuleResult result = DecodeModule(data, data);
    EXPECT_TRUE(result.ok());
    if (result.val) delete result.val;
  }
  {
    ModuleResult result = DecodeModule(data, data + 1);
    EXPECT_TRUE(result.ok());
    if (result.val) delete result.val;
  }
}


TEST_F(WasmModuleVerifyTest, OneGlobal) {
  const byte data[] = {
      kDeclGlobals,
      1,
      0,
      0,
      0,
      0,        // name offset
      kMemI32,  // memory type
      0,        // exported
  };

  {
    // Should decode to exactly one global.
    ModuleResult result = DecodeModule(data, data + arraysize(data));
    EXPECT_TRUE(result.ok());
    EXPECT_EQ(1, result.val->globals->size());
    EXPECT_EQ(0, result.val->functions->size());
    EXPECT_EQ(0, result.val->data_segments->size());

    WasmGlobal* global = &result.val->globals->back();

    EXPECT_EQ(0, global->name_offset);
    EXPECT_EQ(MachineType::Int32(), global->type);
    EXPECT_EQ(0, global->offset);
    EXPECT_FALSE(global->exported);

    if (result.val) delete result.val;
  }

  for (size_t size = 1; size < arraysize(data); size++) {
    // Should fall off end of module bytes.
    ModuleResult result = DecodeModule(data, data + size);
    EXPECT_FALSE(result.ok());
    if (result.val) delete result.val;
  }
}


TEST_F(WasmModuleVerifyTest, ZeroGlobals) {
  const byte data[] = {
      kDeclGlobals, 0,  // declare 0 globals
  };
  ModuleResult result = DecodeModule(data, data + arraysize(data));
  EXPECT_TRUE(result.ok());
  if (result.val) delete result.val;
}


static void AppendUint32v(std::vector<byte>& buffer, uint32_t val) {
  while (true) {
    uint32_t next = val >> 7;
    uint32_t out = val & 0x7f;
    if (next) {
      buffer.push_back(static_cast<byte>(0x80 | out));
      val = next;
    } else {
      buffer.push_back(static_cast<byte>(out));
      break;
    }
  }
}


TEST_F(WasmModuleVerifyTest, NGlobals) {
  const byte data[] = {
      0,       0, 0, 0,  // name offset
      kMemI32,           // memory type
      0,                 // exported
  };
  for (uint32_t i = 0; i < 1000000; i = i * 7 + 1) {
    std::vector<byte> buffer;
    buffer.push_back(kDeclGlobals);
    AppendUint32v(buffer, i);
    for (uint32_t j = 0; j < i; j++) {
      buffer.insert(buffer.end(), data, data + arraysize(data));
    }

    ModuleResult result = DecodeModule(&buffer[0], &buffer[0] + buffer.size());
    EXPECT_TRUE(result.ok());
    if (result.val) delete result.val;
  }
}


TEST_F(WasmModuleVerifyTest, GlobalWithInvalidNameOffset) {
  const byte data[] = {
      kDeclGlobals,
      1,  // declare one global
      0,
      3,
      0,
      0,        // name offset
      kMemI32,  // memory type
      0,        // exported
  };

  EXPECT_FAILURE(data);
}


TEST_F(WasmModuleVerifyTest, GlobalWithInvalidMemoryType) {
  const byte data[] = {
      kDeclGlobals,
      1,  // declare one global
      0,
      0,
      0,
      0,   // name offset
      33,  // memory type
      0,   // exported
  };

  EXPECT_FAILURE(data);
}


TEST_F(WasmModuleVerifyTest, TwoGlobals) {
  const byte data[] = {
      kDeclGlobals,
      2,
      0,
      0,
      0,
      0,        // #0: name offset
      kMemF32,  // memory type
      0,        // exported
      0,
      0,
      0,
      0,        // #1: name offset
      kMemF64,  // memory type
      1,        // exported
  };

  {
    // Should decode to exactly two globals.
    ModuleResult result = DecodeModule(data, data + arraysize(data));
    EXPECT_TRUE(result.ok());
    EXPECT_EQ(2, result.val->globals->size());
    EXPECT_EQ(0, result.val->functions->size());
    EXPECT_EQ(0, result.val->data_segments->size());

    WasmGlobal* g0 = &result.val->globals->at(0);
    WasmGlobal* g1 = &result.val->globals->at(1);

    EXPECT_EQ(0, g0->name_offset);
    EXPECT_EQ(MachineType::Float32(), g0->type);
    EXPECT_EQ(0, g0->offset);
    EXPECT_FALSE(g0->exported);

    EXPECT_EQ(0, g1->name_offset);
    EXPECT_EQ(MachineType::Float64(), g1->type);
    EXPECT_EQ(0, g1->offset);
    EXPECT_TRUE(g1->exported);

    if (result.val) delete result.val;
  }

  for (size_t size = 1; size < arraysize(data); size++) {
    // Should fall off end of module bytes.
    ModuleResult result = DecodeModule(data, data + size);
    EXPECT_FALSE(result.ok());
    if (result.val) delete result.val;
  }
}


TEST_F(WasmModuleVerifyTest, OneSignature) {
  static const byte data[] = {
      kDeclSignatures, 1, 0, kLocalVoid  // void -> void
  };
  EXPECT_VERIFIES(data);
}


TEST_F(WasmModuleVerifyTest, MultipleSignatures) {
  static const byte data[] = {
      kDeclSignatures,
      3,
      0,
      kLocalVoid,  // void -> void
      1,
      kLocalI32,
      kLocalF32,  // f32 -> i32
      2,
      kLocalI32,
      kLocalF64,
      kLocalF64,  // (f64,f64) -> i32
  };

  ModuleResult result = DecodeModule(data, data + arraysize(data));
  EXPECT_TRUE(result.ok());
  EXPECT_EQ(3, result.val->signatures->size());
  if (result.val->signatures->size() == 3) {
    EXPECT_EQ(0, result.val->signatures->at(0)->return_count());
    EXPECT_EQ(1, result.val->signatures->at(1)->return_count());
    EXPECT_EQ(1, result.val->signatures->at(2)->return_count());

    EXPECT_EQ(0, result.val->signatures->at(0)->parameter_count());
    EXPECT_EQ(1, result.val->signatures->at(1)->parameter_count());
    EXPECT_EQ(2, result.val->signatures->at(2)->parameter_count());
  }
  if (result.val) delete result.val;

  for (size_t size = 1; size < arraysize(data); size++) {
    ModuleResult result = DecodeModule(data, data + size);
    // Should fall off the end of module bytes.
    EXPECT_FALSE(result.ok());
    if (result.val) delete result.val;
  }
}


TEST_F(WasmModuleVerifyTest, FunctionWithoutSig) {
  static const byte data[] = {
      kDeclFunctions, 1,
      // func#0 ------------------------------------------------------
      0, 0,        // signature index
      0, 0, 0, 0,  // name offset
      0, 0, 0, 0,  // code start offset
      0, 0, 0, 0,  // code end offset
      1, 2,        // local int32 count
      3, 4,        // local int64 count
      5, 6,        // local float32 count
      7, 8,        // local float64 count
      0,           // exported
      1            // external
  };

  ModuleResult result = DecodeModule(data, data + arraysize(data));
  EXPECT_FALSE(result.ok());
  if (result.val) delete result.val;
}


TEST_F(WasmModuleVerifyTest, OneEmptyVoidVoidFunction) {
  const int kCodeStartOffset = 23;
  const int kCodeEndOffset = kCodeStartOffset + 1;

  static const byte data[] = {
      kDeclSignatures, 1,
      // sig#0 -------------------------------------------------------
      0, 0,  // void -> void
      // func#0 ------------------------------------------------------
      kDeclFunctions, 1,
      kDeclFunctionLocals | kDeclFunctionExport | kDeclFunctionName, 0,
      0,           // signature index
      9, 0, 0, 0,  // name offset
      11, 2,       // local int32 count
      13, 4,       // local int64 count
      15, 6,       // local float32 count
      17, 8,       // local float64 count
      1, 0,        // size
      kExprNop,
  };

  {
    // Should decode to exactly one function.
    ModuleResult result = DecodeModule(data, data + arraysize(data));
    EXPECT_TRUE(result.ok());
    EXPECT_EQ(0, result.val->globals->size());
    EXPECT_EQ(1, result.val->signatures->size());
    EXPECT_EQ(1, result.val->functions->size());
    EXPECT_EQ(0, result.val->data_segments->size());
    EXPECT_EQ(0, result.val->function_table->size());

    WasmFunction* function = &result.val->functions->back();

    EXPECT_EQ(9, function->name_offset);
    EXPECT_EQ(kCodeStartOffset, function->code_start_offset);
    EXPECT_EQ(kCodeEndOffset, function->code_end_offset);

    EXPECT_EQ(523, function->local_int32_count);
    EXPECT_EQ(1037, function->local_int64_count);
    EXPECT_EQ(1551, function->local_float32_count);
    EXPECT_EQ(2065, function->local_float64_count);

    EXPECT_TRUE(function->exported);
    EXPECT_FALSE(function->external);

    if (result.val) delete result.val;
  }

  for (size_t size = 5; size < arraysize(data); size++) {
    // Should fall off end of module bytes.
    ModuleResult result = DecodeModule(data, data + size);
    EXPECT_FALSE(result.ok());
    if (result.val) delete result.val;
  }
}


TEST_F(WasmModuleVerifyTest, OneFunctionImported) {
  static const byte data[] = {
      kDeclSignatures, 1,
      // sig#0 -------------------------------------------------------
      0, 0,  // void -> void
      kDeclFunctions, 1,
      // func#0 ------------------------------------------------------
      kDeclFunctionImport,  // no name, no locals, imported
      0, 0,                 // signature index
  };

  ModuleResult result = DecodeModule(data, data + arraysize(data));
  EXPECT_TRUE(result.ok());
  EXPECT_EQ(1, result.val->functions->size());
  WasmFunction* function = &result.val->functions->back();

  EXPECT_EQ(0, function->name_offset);
  EXPECT_EQ(0, function->code_start_offset);
  EXPECT_EQ(0, function->code_end_offset);

  EXPECT_EQ(0, function->local_int32_count);
  EXPECT_EQ(0, function->local_int64_count);
  EXPECT_EQ(0, function->local_float32_count);
  EXPECT_EQ(0, function->local_float64_count);

  EXPECT_FALSE(function->exported);
  EXPECT_TRUE(function->external);

  if (result.val) delete result.val;
}


TEST_F(WasmModuleVerifyTest, OneFunctionWithNopBody) {
  static const byte kCodeStartOffset = 11;
  static const byte kCodeEndOffset = kCodeStartOffset + 1;

  static const byte data[] = {
      kDeclSignatures, 1,
      // sig#0 -------------------------------------------------------
      0, 0,  // void -> void
      kDeclFunctions, 1,
      // func#0 ------------------------------------------------------
      0,        // no name, no locals
      0, 0,     // signature index
      1, 0,     // body size
      kExprNop  // body
  };

  ModuleResult result = DecodeModule(data, data + arraysize(data));
  EXPECT_TRUE(result.ok());
  EXPECT_EQ(1, result.val->functions->size());
  WasmFunction* function = &result.val->functions->back();

  EXPECT_EQ(0, function->name_offset);
  EXPECT_EQ(kCodeStartOffset, function->code_start_offset);
  EXPECT_EQ(kCodeEndOffset, function->code_end_offset);

  EXPECT_EQ(0, function->local_int32_count);
  EXPECT_EQ(0, function->local_int64_count);
  EXPECT_EQ(0, function->local_float32_count);
  EXPECT_EQ(0, function->local_float64_count);

  EXPECT_FALSE(function->exported);
  EXPECT_FALSE(function->external);

  if (result.val) delete result.val;
}


TEST_F(WasmModuleVerifyTest, OneFunctionWithNopBody_WithLocals) {
  static const byte kCodeStartOffset = 19;
  static const byte kCodeEndOffset = kCodeStartOffset + 1;

  static const byte data[] = {
      kDeclSignatures, 1,
      // sig#0 -------------------------------------------------------
      0, 0,  // void -> void
      kDeclFunctions, 1,
      // func#0 ------------------------------------------------------
      kDeclFunctionLocals, 0, 0,  // signature index
      1, 2,                       // local int32 count
      3, 4,                       // local int64 count
      5, 6,                       // local float32 count
      7, 8,                       // local float64 count
      1, 0,                       // body size
      kExprNop                    // body
  };

  ModuleResult result = DecodeModule(data, data + arraysize(data));
  EXPECT_TRUE(result.ok());
  EXPECT_EQ(1, result.val->functions->size());
  WasmFunction* function = &result.val->functions->back();

  EXPECT_EQ(0, function->name_offset);
  EXPECT_EQ(kCodeStartOffset, function->code_start_offset);
  EXPECT_EQ(kCodeEndOffset, function->code_end_offset);

  EXPECT_EQ(513, function->local_int32_count);
  EXPECT_EQ(1027, function->local_int64_count);
  EXPECT_EQ(1541, function->local_float32_count);
  EXPECT_EQ(2055, function->local_float64_count);

  EXPECT_FALSE(function->exported);
  EXPECT_FALSE(function->external);

  if (result.val) delete result.val;
}


TEST_F(WasmModuleVerifyTest, OneGlobalOneFunctionWithNopBodyOneDataSegment) {
  static const byte kCodeStartOffset = 2 + kDeclGlobalSize + 4 + 2 + 17;
  static const byte kCodeEndOffset = kCodeStartOffset + 3;

  static const byte data[] = {
      // global#0 --------------------------------------------------
      kDeclGlobals, 1, 0, 0, 0, 0,  // name offset
      kMemU8,                       // memory type
      0,                            // exported
      // sig#0 -----------------------------------------------------
      kDeclSignatures, 1, 0, 0,  // void -> void
      // func#0 ----------------------------------------------------
      kDeclFunctions, 1, kDeclFunctionLocals | kDeclFunctionName, 0,
      0,           // signature index
      9, 0, 0, 0,  // name offset
      1, 2,        // local int32 count
      3, 4,        // local int64 count
      5, 6,        // local float32 count
      7, 8,        // local float64 count
      3, 0,        // body size
      kExprNop,    // func#0 body
      kExprNop,    // func#0 body
      kExprNop,    // func#0 body
      // segment#0 -------------------------------------------------
      kDeclDataSegments, 1, 0xae, 0xb3, 0x08, 0,  // dest addr
      15, 0, 0, 0,                                // source offset
      5, 0, 0, 0,                                 // source size
      1,                                          // init
      // rest ------------------------------------------------------
      kDeclEnd,
  };

  {
    ModuleResult result = DecodeModule(data, data + arraysize(data));
    EXPECT_TRUE(result.ok());
    EXPECT_EQ(1, result.val->globals->size());
    EXPECT_EQ(1, result.val->functions->size());
    EXPECT_EQ(1, result.val->data_segments->size());

    WasmGlobal* global = &result.val->globals->back();

    EXPECT_EQ(0, global->name_offset);
    EXPECT_EQ(MachineType::Uint8(), global->type);
    EXPECT_EQ(0, global->offset);
    EXPECT_FALSE(global->exported);

    WasmFunction* function = &result.val->functions->back();

    EXPECT_EQ(9, function->name_offset);
    EXPECT_EQ(kCodeStartOffset, function->code_start_offset);
    EXPECT_EQ(kCodeEndOffset, function->code_end_offset);

    EXPECT_FALSE(function->exported);
    EXPECT_FALSE(function->external);

    WasmDataSegment* segment = &result.val->data_segments->back();

    EXPECT_EQ(0x8b3ae, segment->dest_addr);
    EXPECT_EQ(15, segment->source_offset);
    EXPECT_EQ(5, segment->source_size);
    EXPECT_TRUE(segment->init);

    if (result.val) delete result.val;
  }
}


TEST_F(WasmModuleVerifyTest, OneDataSegment) {
  const byte data[] = {
      kDeclDataSegments,
      1,
      0xaa,
      0xbb,
      0x09,
      0,  // dest addr
      11,
      0,
      0,
      0,  // source offset
      3,
      0,
      0,
      0,  // source size
      1,  // init
  };

  {
    ModuleResult result = DecodeModule(data, data + arraysize(data));
    EXPECT_TRUE(result.ok());
    EXPECT_EQ(0, result.val->globals->size());
    EXPECT_EQ(0, result.val->functions->size());
    EXPECT_EQ(1, result.val->data_segments->size());

    WasmDataSegment* segment = &result.val->data_segments->back();

    EXPECT_EQ(0x9bbaa, segment->dest_addr);
    EXPECT_EQ(11, segment->source_offset);
    EXPECT_EQ(3, segment->source_size);
    EXPECT_TRUE(segment->init);

    if (result.val) delete result.val;
  }

  for (size_t size = 1; size < arraysize(data); size++) {
    // Should fall off end of module bytes.
    ModuleResult result = DecodeModule(data, data + size);
    EXPECT_FALSE(result.ok());
    if (result.val) delete result.val;
  }
}


TEST_F(WasmModuleVerifyTest, TwoDataSegments) {
  const byte data[] = {
      kDeclDataSegments,
      2,
      0xee,
      0xff,
      0x07,
      0,  // dest addr
      9,
      0,
      0,
      0,  // #0: source offset
      4,
      0,
      0,
      0,  // source size
      0,  // init
      0xcc,
      0xdd,
      0x06,
      0,  // #1: dest addr
      6,
      0,
      0,
      0,  // source offset
      10,
      0,
      0,
      0,  // source size
      1,  // init
  };

  {
    ModuleResult result = DecodeModule(data, data + arraysize(data));
    EXPECT_TRUE(result.ok());
    EXPECT_EQ(0, result.val->globals->size());
    EXPECT_EQ(0, result.val->functions->size());
    EXPECT_EQ(2, result.val->data_segments->size());

    WasmDataSegment* s0 = &result.val->data_segments->at(0);
    WasmDataSegment* s1 = &result.val->data_segments->at(1);

    EXPECT_EQ(0x7ffee, s0->dest_addr);
    EXPECT_EQ(9, s0->source_offset);
    EXPECT_EQ(4, s0->source_size);
    EXPECT_FALSE(s0->init);

    EXPECT_EQ(0x6ddcc, s1->dest_addr);
    EXPECT_EQ(6, s1->source_offset);
    EXPECT_EQ(10, s1->source_size);
    EXPECT_TRUE(s1->init);

    if (result.val) delete result.val;
  }

  for (size_t size = 1; size < arraysize(data); size++) {
    // Should fall off end of module bytes.
    ModuleResult result = DecodeModule(data, data + size);
    EXPECT_FALSE(result.ok());
    if (result.val) delete result.val;
  }
}


// To make below tests for indirect calls much shorter.
#define FUNCTION(sig_index, external)                \
  kDeclFunctionImport, static_cast<byte>(sig_index), \
      static_cast<byte>(sig_index >> 8)


TEST_F(WasmModuleVerifyTest, OneIndirectFunction) {
  static const byte data[] = {
      // sig#0 -------------------------------------------------------
      kDeclSignatures, 1, 0, 0,  // void -> void
      // func#0 ------------------------------------------------------
      kDeclFunctions, 1, FUNCTION(0, 0),
      // indirect table ----------------------------------------------
      kDeclFunctionTable, 1, 0, 0};

  ModuleResult result = DecodeModule(data, data + arraysize(data));
  EXPECT_TRUE(result.ok());
  if (result.ok()) {
    EXPECT_EQ(1, result.val->signatures->size());
    EXPECT_EQ(1, result.val->functions->size());
    EXPECT_EQ(1, result.val->function_table->size());
    EXPECT_EQ(0, result.val->function_table->at(0));
  }
  if (result.val) delete result.val;
}


TEST_F(WasmModuleVerifyTest, MultipleIndirectFunctions) {
  static const byte data[] = {
      // sig#0 -------------------------------------------------------
      kDeclSignatures, 2, 0, 0,  // void -> void
      0, kLocalI32,              // void -> i32
      // func#0 ------------------------------------------------------
      kDeclFunctions, 4, FUNCTION(0, 1), FUNCTION(1, 1), FUNCTION(0, 1),
      FUNCTION(1, 1),
      // indirect table ----------------------------------------------
      kDeclFunctionTable, 8, 0, 0, 1, 0, 2, 0, 3, 0, 0, 0, 1, 0, 2, 0, 3, 0,
  };

  ModuleResult result = DecodeModule(data, data + arraysize(data));
  EXPECT_TRUE(result.ok());
  if (result.ok()) {
    EXPECT_EQ(2, result.val->signatures->size());
    EXPECT_EQ(4, result.val->functions->size());
    EXPECT_EQ(8, result.val->function_table->size());
    for (int i = 0; i < 8; i++) {
      EXPECT_EQ(i & 3, result.val->function_table->at(i));
    }
  }
  if (result.val) delete result.val;
}


TEST_F(WasmModuleVerifyTest, IndirectFunctionNoFunctions) {
  static const byte data[] = {
      // sig#0 -------------------------------------------------------
      kDeclSignatures, 1, 0, 0,  // void -> void
      // indirect table ----------------------------------------------
      kDeclFunctionTable, 1, 0, 0,
  };

  EXPECT_FAILURE(data);
}


TEST_F(WasmModuleVerifyTest, IndirectFunctionInvalidIndex) {
  static const byte data[] = {
      // sig#0 -------------------------------------------------------
      kDeclSignatures, 1, 0, 0,  // void -> void
      // functions ---------------------------------------------------
      kDeclFunctions, 1, FUNCTION(0, 1),
      // indirect table ----------------------------------------------
      kDeclFunctionTable, 1, 1, 0,
  };

  EXPECT_FAILURE(data);
}


class WasmSignatureDecodeTest : public TestWithZone {};


TEST_F(WasmSignatureDecodeTest, Ok_v_v) {
  static const byte data[] = {0, 0};
  Zone zone;
  FunctionSig* sig =
      DecodeWasmSignatureForTesting(&zone, data, data + arraysize(data));

  EXPECT_TRUE(sig != nullptr);
  EXPECT_EQ(0, sig->parameter_count());
  EXPECT_EQ(0, sig->return_count());
}


TEST_F(WasmSignatureDecodeTest, Ok_t_v) {
  for (size_t i = 0; i < arraysize(kLocalTypes); i++) {
    LocalTypePair ret_type = kLocalTypes[i];
    const byte data[] = {0, ret_type.code};
    FunctionSig* sig =
        DecodeWasmSignatureForTesting(zone(), data, data + arraysize(data));

    EXPECT_TRUE(sig != nullptr);
    EXPECT_EQ(0, sig->parameter_count());
    EXPECT_EQ(1, sig->return_count());
    EXPECT_EQ(ret_type.type, sig->GetReturn());
  }
}


TEST_F(WasmSignatureDecodeTest, Ok_v_t) {
  for (size_t i = 0; i < arraysize(kLocalTypes); i++) {
    LocalTypePair param_type = kLocalTypes[i];
    const byte data[] = {1, 0, param_type.code};
    FunctionSig* sig =
        DecodeWasmSignatureForTesting(zone(), data, data + arraysize(data));

    EXPECT_TRUE(sig != nullptr);
    EXPECT_EQ(1, sig->parameter_count());
    EXPECT_EQ(0, sig->return_count());
    EXPECT_EQ(param_type.type, sig->GetParam(0));
  }
}


TEST_F(WasmSignatureDecodeTest, Ok_t_t) {
  for (size_t i = 0; i < arraysize(kLocalTypes); i++) {
    LocalTypePair ret_type = kLocalTypes[i];
    for (size_t j = 0; j < arraysize(kLocalTypes); j++) {
      LocalTypePair param_type = kLocalTypes[j];
      const byte data[] = {1,                 // param count
                           ret_type.code,     // ret
                           param_type.code};  // param
      FunctionSig* sig =
          DecodeWasmSignatureForTesting(zone(), data, data + arraysize(data));

      EXPECT_TRUE(sig != nullptr);
      EXPECT_EQ(1, sig->parameter_count());
      EXPECT_EQ(1, sig->return_count());
      EXPECT_EQ(param_type.type, sig->GetParam(0));
      EXPECT_EQ(ret_type.type, sig->GetReturn());
    }
  }
}


TEST_F(WasmSignatureDecodeTest, Ok_i_tt) {
  for (size_t i = 0; i < arraysize(kLocalTypes); i++) {
    LocalTypePair p0_type = kLocalTypes[i];
    for (size_t j = 0; j < arraysize(kLocalTypes); j++) {
      LocalTypePair p1_type = kLocalTypes[j];
      const byte data[] = {2,              // param count
                           kLocalI32,      // ret
                           p0_type.code,   // p0
                           p1_type.code};  // p1
      FunctionSig* sig =
          DecodeWasmSignatureForTesting(zone(), data, data + arraysize(data));

      EXPECT_TRUE(sig != nullptr);
      EXPECT_EQ(2, sig->parameter_count());
      EXPECT_EQ(1, sig->return_count());
      EXPECT_EQ(p0_type.type, sig->GetParam(0));
      EXPECT_EQ(p1_type.type, sig->GetParam(1));
    }
  }
}


TEST_F(WasmSignatureDecodeTest, Fail_off_end) {
  byte data[256];
  for (int p = 0; p <= 255; p = p + 1 + p * 3) {
    for (int i = 0; i <= p; i++) data[i] = kLocalI32;
    data[0] = static_cast<byte>(p);

    for (int i = 0; i < p + 1; i++) {
      // Should fall off the end for all signatures.
      FunctionSig* sig = DecodeWasmSignatureForTesting(zone(), data, data + i);
      EXPECT_EQ(nullptr, sig);
    }
  }
}


TEST_F(WasmSignatureDecodeTest, Fail_invalid_type) {
  byte kInvalidType = 76;
  for (int i = 1; i < 3; i++) {
    byte data[] = {2, kLocalI32, kLocalI32, kLocalI32};
    data[i] = kInvalidType;
    FunctionSig* sig =
        DecodeWasmSignatureForTesting(zone(), data, data + arraysize(data));
    EXPECT_EQ(nullptr, sig);
  }
}


TEST_F(WasmSignatureDecodeTest, Fail_invalid_param_type) {
  static const int kParamCount = 3;
  for (int i = 0; i < kParamCount; i++) {
    byte data[] = {kParamCount, kLocalI32, kLocalI32, kLocalI32, kLocalI32};
    data[i + 2] = kLocalVoid;
    FunctionSig* sig =
        DecodeWasmSignatureForTesting(zone(), data, data + arraysize(data));
    EXPECT_EQ(nullptr, sig);
  }
}


class WasmFunctionVerifyTest : public TestWithZone {};


TEST_F(WasmFunctionVerifyTest, Ok_v_v_empty) {
  byte data[] = {
      0,       kLocalVoid,  // signature
      3,       0,           // local int32 count
      4,       0,           // local int64 count
      5,       0,           // local float32 count
      6,       0,           // local float64 count
      kExprNop              // body
  };

  FunctionResult result = DecodeWasmFunction(nullptr, zone(), nullptr, data,
                                             data + arraysize(data));
  EXPECT_TRUE(result.ok());

  if (result.val && result.ok()) {
    WasmFunction* function = result.val;
    EXPECT_EQ(0, function->sig->parameter_count());
    EXPECT_EQ(0, function->sig->return_count());
    EXPECT_EQ(0, function->name_offset);
    EXPECT_EQ(arraysize(data) - 1, function->code_start_offset);
    EXPECT_EQ(arraysize(data), function->code_end_offset);
    EXPECT_EQ(3, function->local_int32_count);
    EXPECT_EQ(4, function->local_int64_count);
    EXPECT_EQ(5, function->local_float32_count);
    EXPECT_EQ(6, function->local_float64_count);
    EXPECT_FALSE(function->external);
    EXPECT_FALSE(function->exported);
  }

  if (result.val) delete result.val;
}


TEST_F(WasmModuleVerifyTest, WLLSectionNoLen) {
  const byte data[] = {
      kDeclWLL,  // section without length.
  };
  EXPECT_FAILURE(data);
}


TEST_F(WasmModuleVerifyTest, WLLSectionEmpty) {
  const byte data[] = {
      kDeclWLL, 0,  // empty section
  };
  ModuleResult result = DecodeModule(data, data + arraysize(data));
  EXPECT_TRUE(result.ok());
  if (result.val) delete result.val;
}


TEST_F(WasmModuleVerifyTest, WLLSectionOne) {
  const byte data[] = {
      kDeclWLL,
      1,  // LEB128 1
      0,  // one byte section
  };
  ModuleResult result = DecodeModule(data, data + arraysize(data));
  EXPECT_TRUE(result.ok());
  if (result.val) delete result.val;
}


TEST_F(WasmModuleVerifyTest, WLLSectionTen) {
  const byte data[] = {
      kDeclWLL,
      10,                             // LEB128 10
      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,  // 10 byte section
  };
  ModuleResult result = DecodeModule(data, data + arraysize(data));
  EXPECT_TRUE(result.ok());
  if (result.val) delete result.val;
}


TEST_F(WasmModuleVerifyTest, WLLSectionOverflow) {
  const byte data[] = {
      kDeclWLL,
      11,                             // LEB128 11
      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,  // 10 byte section
  };
  EXPECT_FAILURE(data);
}


TEST_F(WasmModuleVerifyTest, WLLSectionUnderflow) {
  const byte data[] = {
    kDeclWLL,
    0xff, 0xff, 0xff, 0xff, 0x0f,  // LEB128 0xffffffff
    1, 2, 3, 4,                    // 4 byte section
  };
  EXPECT_FAILURE(data);
}


TEST_F(WasmModuleVerifyTest, WLLSectionLoop) {
  // Would infinite loop decoding if wrapping and allowed.
  const byte data[] = {
    kDeclWLL,
    0xfa, 0xff, 0xff, 0xff, 0x0f,  // LEB128 0xfffffffa
    1, 2, 3, 4,                    // 4 byte section
  };
  EXPECT_FAILURE(data);
}

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