test-gc.cc 42.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
// 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 <stdint.h>

#include "src/utils/utils.h"
#include "src/utils/vector.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/struct-types.h"
11
#include "src/wasm/wasm-arguments.h"
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
#include "src/wasm/wasm-engine.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/cctest/compiler/value-helper.h"
#include "test/cctest/wasm/wasm-run-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_gc {

29 30 31 32 33 34
using F = std::pair<ValueType, bool>;

class WasmGCTester {
 public:
  WasmGCTester()
      : flag_gc(&v8::internal::FLAG_experimental_wasm_gc, true),
35
        flag_reftypes(&v8::internal::FLAG_experimental_wasm_reftypes, true),
36 37 38
        flag_typedfuns(&v8::internal::FLAG_experimental_wasm_typed_funcref,
                       true),
        zone(&allocator, ZONE_NAME),
39
        builder_(&zone),
40 41 42 43
        isolate_(CcTest::InitIsolateOnce()),
        scope(isolate_),
        thrower(isolate_, "Test wasm GC") {
    testing::SetupIsolateForWasmModule(isolate_);
44 45
  }

46
  byte AddGlobal(ValueType type, bool mutability, WasmInitExpr init) {
47
    return builder_.AddGlobal(type, mutability, std::move(init));
48 49
  }

50 51
  byte DefineFunction(FunctionSig* sig, std::initializer_list<ValueType> locals,
                      std::initializer_list<byte> code) {
52
    WasmFunctionBuilder* fun = builder_.AddFunction(sig);
53 54 55 56
    for (ValueType local : locals) {
      fun->AddLocal(local);
    }
    fun->EmitCode(code.begin(), static_cast<uint32_t>(code.size()));
57
    return fun->func_index();
58 59
  }

60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
  void DefineExportedFunction(const char* name, FunctionSig* sig,
                              std::initializer_list<byte> code) {
    WasmFunctionBuilder* fun = builder_.AddFunction(sig);
    fun->EmitCode(code.begin(), static_cast<uint32_t>(code.size()));
    builder_.AddExport(CStrVector(name), fun);
  }

  MaybeHandle<Object> CallExportedFunction(const char* name, int argc,
                                           Handle<Object> args[]) {
    Handle<WasmExportedFunction> func =
        testing::GetExportedFunction(isolate_, instance_, name)
            .ToHandleChecked();
    return Execution::Call(isolate_, func,
                           isolate_->factory()->undefined_value(), argc, args);
  }

76
  byte DefineStruct(std::initializer_list<F> fields) {
77 78 79 80 81
    StructType::Builder type_builder(&zone,
                                     static_cast<uint32_t>(fields.size()));
    for (F field : fields) {
      type_builder.AddField(field.first, field.second);
    }
82
    return builder_.AddStructType(type_builder.Build());
83 84
  }

85
  byte DefineArray(ValueType element_type, bool mutability) {
86
    return builder_.AddArrayType(zone.New<ArrayType>(element_type, mutability));
87 88
  }

89 90
  byte DefineSignature(FunctionSig* sig) { return builder_.AddSignature(sig); }

91 92
  void CompileModule() {
    ZoneBuffer buffer(&zone);
93
    builder_.WriteTo(&buffer);
94 95
    MaybeHandle<WasmInstanceObject> maybe_instance =
        testing::CompileAndInstantiateForTesting(
96
            isolate_, &thrower, ModuleWireBytes(buffer.begin(), buffer.end()));
97
    if (thrower.error()) FATAL("%s", thrower.error_msg());
98
    instance_ = maybe_instance.ToHandleChecked();
99 100
  }

101 102 103 104 105 106 107 108 109 110
  void CallFunctionImpl(uint32_t function_index, const FunctionSig* sig,
                        CWasmArgumentsPacker* packer) {
    WasmCodeRefScope scope;
    NativeModule* module = instance_->module_object().native_module();
    WasmCode* code = module->GetCode(function_index);
    Address wasm_call_target = code->instruction_start();
    Handle<Object> object_ref = instance_;
    Handle<Code> c_wasm_entry = compiler::CompileCWasmEntry(isolate_, sig);
    Execution::CallWasm(isolate_, c_wasm_entry, wasm_call_target, object_ref,
                        packer->argv());
111 112
  }

113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
  void CheckResult(uint32_t function_index, int32_t expected) {
    FunctionSig* sig = sigs.i_v();
    DCHECK(*sig == *instance_->module()->functions[function_index].sig);
    CWasmArgumentsPacker packer(CWasmArgumentsPacker::TotalSize(sig));
    CallFunctionImpl(function_index, sig, &packer);
    packer.Reset();
    CHECK_EQ(expected, packer.Pop<int32_t>());
  }

  void CheckResult(uint32_t function_index, int32_t expected, int32_t arg) {
    FunctionSig* sig = sigs.i_i();
    DCHECK(*sig == *instance_->module()->functions[function_index].sig);
    CWasmArgumentsPacker packer(CWasmArgumentsPacker::TotalSize(sig));
    packer.Push(arg);
    CallFunctionImpl(function_index, sig, &packer);
    packer.Reset();
    CHECK_EQ(expected, packer.Pop<int32_t>());
130 131
  }

132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
  MaybeHandle<Object> GetResultObject(uint32_t function_index) {
    const FunctionSig* sig = instance_->module()->functions[function_index].sig;
    CWasmArgumentsPacker packer(CWasmArgumentsPacker::TotalSize(sig));
    CallFunctionImpl(function_index, sig, &packer);
    packer.Reset();
    return Handle<Object>(Object(packer.Pop<Address>()), isolate_);
  }

  void CheckHasThrown(uint32_t function_index, int32_t arg) {
    FunctionSig* sig = sigs.i_i();
    DCHECK(*sig == *instance_->module()->functions[function_index].sig);
    CWasmArgumentsPacker packer(CWasmArgumentsPacker::TotalSize(sig));
    packer.Push(arg);
    CallFunctionImpl(function_index, sig, &packer);
    CHECK(isolate_->has_pending_exception());
147
    isolate_->clear_pending_exception();
148 149
  }

150
  Handle<WasmInstanceObject> instance() { return instance_; }
151
  Isolate* isolate() { return isolate_; }
152
  WasmModuleBuilder* builder() { return &builder_; }
153

154 155
  TestSignatures sigs;

156 157 158 159 160 161 162
 private:
  const FlagScope<bool> flag_gc;
  const FlagScope<bool> flag_reftypes;
  const FlagScope<bool> flag_typedfuns;

  v8::internal::AccountingAllocator allocator;
  Zone zone;
163
  WasmModuleBuilder builder_;
164

165
  Isolate* const isolate_;
166
  const HandleScope scope;
167
  Handle<WasmInstanceObject> instance_;
168 169 170
  ErrorThrower thrower;
};

171
ValueType ref(uint32_t type_index) {
172
  return ValueType::Ref(type_index, kNonNullable);
173 174
}
ValueType optref(uint32_t type_index) {
175
  return ValueType::Ref(type_index, kNullable);
176 177
}

178
// TODO(7748): Use WASM_EXEC_TEST once interpreter and liftoff are supported.
179 180
TEST(WasmBasicStruct) {
  WasmGCTester tester;
181
  const byte type_index =
182
      tester.DefineStruct({F(kWasmI32, true), F(kWasmI32, true)});
183 184
  ValueType kRefTypes[] = {ref(type_index)};
  ValueType kOptRefType = optref(type_index);
185 186
  FunctionSig sig_q_v(1, 0, kRefTypes);

187
  // Test struct.new and struct.get.
188
  const byte kGet1 = tester.DefineFunction(
189
      tester.sigs.i_v(), {},
190 191
      {WASM_STRUCT_GET(
           type_index, 0,
192 193
           WASM_STRUCT_NEW_WITH_RTT(type_index, WASM_I32V(42), WASM_I32V(64),
                                    WASM_RTT_CANON(type_index))),
194
       kExprEnd});
195

196
  // Test struct.new and struct.get.
197
  const byte kGet2 = tester.DefineFunction(
198
      tester.sigs.i_v(), {},
199 200
      {WASM_STRUCT_GET(
           type_index, 1,
201 202
           WASM_STRUCT_NEW_WITH_RTT(type_index, WASM_I32V(42), WASM_I32V(64),
                                    WASM_RTT_CANON(type_index))),
203
       kExprEnd});
204

205
  // Test struct.new, returning struct reference.
206
  const byte kGetStruct = tester.DefineFunction(
207
      &sig_q_v, {},
208 209 210
      {WASM_STRUCT_NEW_WITH_RTT(type_index, WASM_I32V(42), WASM_I32V(64),
                                WASM_RTT_CANON(type_index)),
       kExprEnd});
211

212
  // Test struct.set, struct refs types in locals.
213 214 215
  const byte j_local_index = 0;
  const byte j_field_index = 0;
  const byte kSet = tester.DefineFunction(
216
      tester.sigs.i_v(), {kOptRefType},
217 218 219 220
      {WASM_SET_LOCAL(
           j_local_index,
           WASM_STRUCT_NEW_WITH_RTT(type_index, WASM_I32V(42), WASM_I32V(64),
                                    WASM_RTT_CANON(type_index))),
221 222 223 224 225
       WASM_STRUCT_SET(type_index, j_field_index, WASM_GET_LOCAL(j_local_index),
                       WASM_I32V(-99)),
       WASM_STRUCT_GET(type_index, j_field_index,
                       WASM_GET_LOCAL(j_local_index)),
       kExprEnd});
226

227 228
  tester.CompileModule();

229 230 231 232
  tester.CheckResult(kGet1, 42);
  tester.CheckResult(kGet2, 64);
  CHECK(tester.GetResultObject(kGetStruct).ToHandleChecked()->IsWasmStruct());
  tester.CheckResult(kSet, -99);
233 234 235 236 237 238
}

// Test struct.set, ref.as_non_null,
// struct refs types in globals and if-results.
TEST(WasmRefAsNonNull) {
  WasmGCTester tester;
239
  const byte type_index =
240 241 242 243 244
      tester.DefineStruct({F(kWasmI32, true), F(kWasmI32, true)});
  ValueType kRefTypes[] = {ref(type_index)};
  ValueType kOptRefType = optref(type_index);
  FunctionSig sig_q_v(1, 0, kRefTypes);

245
  const byte global_index =
246 247 248
      tester.AddGlobal(kOptRefType, true,
                       WasmInitExpr::RefNullConst(
                           static_cast<HeapType::Representation>(type_index)));
249 250
  const byte field_index = 0;
  const byte kFunc = tester.DefineFunction(
251
      tester.sigs.i_v(), {},
252 253 254 255
      {WASM_SET_GLOBAL(
           global_index,
           WASM_STRUCT_NEW_WITH_RTT(type_index, WASM_I32V(55), WASM_I32V(66),
                                    WASM_RTT_CANON(type_index))),
256
       WASM_STRUCT_GET(
257
           type_index, field_index,
258 259 260
           WASM_REF_AS_NON_NULL(WASM_IF_ELSE_R(kOptRefType, WASM_I32V(1),
                                               WASM_GET_GLOBAL(global_index),
                                               WASM_REF_NULL(type_index)))),
261
       kExprEnd});
262

263
  tester.CompileModule();
264
  tester.CheckResult(kFunc, 55);
265 266 267 268
}

TEST(WasmBrOnNull) {
  WasmGCTester tester;
269
  const byte type_index =
270 271 272 273
      tester.DefineStruct({F(kWasmI32, true), F(kWasmI32, true)});
  ValueType kRefTypes[] = {ref(type_index)};
  ValueType kOptRefType = optref(type_index);
  FunctionSig sig_q_v(1, 0, kRefTypes);
274 275
  const byte l_local_index = 0;
  const byte kTaken = tester.DefineFunction(
276
      tester.sigs.i_v(), {kOptRefType},
277 278 279 280 281 282
      {WASM_BLOCK_I(WASM_I32V(42),
                    // Branch will be taken.
                    // 42 left on stack outside the block (not 52).
                    WASM_BR_ON_NULL(0, WASM_GET_LOCAL(l_local_index)),
                    WASM_I32V(52), WASM_BR(0)),
       kExprEnd});
283

284 285
  const byte m_field_index = 0;
  const byte kNotTaken = tester.DefineFunction(
286
      tester.sigs.i_v(), {},
287 288 289 290 291 292
      {WASM_BLOCK_I(
           WASM_I32V(42),
           WASM_STRUCT_GET(
               type_index, m_field_index,
               // Branch will not be taken.
               // 52 left on stack outside the block (not 42).
293 294 295
               WASM_BR_ON_NULL(0, WASM_STRUCT_NEW_WITH_RTT(
                                      type_index, WASM_I32V(52), WASM_I32V(62),
                                      WASM_RTT_CANON(type_index)))),
296 297
           WASM_BR(0)),
       kExprEnd});
298

299
  tester.CompileModule();
300 301
  tester.CheckResult(kTaken, 42);
  tester.CheckResult(kNotTaken, 52);
302 303
}

304 305
TEST(BrOnCast) {
  WasmGCTester tester;
306 307
  const byte type_index = tester.DefineStruct({F(kWasmI32, true)});
  const byte rtt_index =
308 309 310
      tester.AddGlobal(ValueType::Rtt(type_index, 1), false,
                       WasmInitExpr::RttCanon(
                           static_cast<HeapType::Representation>(type_index)));
311
  const byte kTestStruct = tester.DefineFunction(
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
      tester.sigs.i_v(), {kWasmI32, kWasmEqRef},
      {WASM_BLOCK(WASM_SET_LOCAL(0, WASM_I32V(111)),
                  // Pipe a struct through a local so it's statically typed
                  // as eqref.
                  WASM_SET_LOCAL(
                      1, WASM_STRUCT_NEW_WITH_RTT(type_index, WASM_I32V(1),
                                                  WASM_GET_GLOBAL(rtt_index))),
                  WASM_GET_LOCAL(1),
                  // The struct is not an i31, so this branch isn't taken.
                  WASM_BR_ON_CAST(0, WASM_RTT_CANON(kLocalI31Ref)),
                  WASM_SET_LOCAL(0, WASM_I32V(222)),  // Final result.
                  // This branch is taken.
                  WASM_BR_ON_CAST(0, WASM_GET_GLOBAL(rtt_index)),
                  // Not executed due to the branch.
                  WASM_DROP, WASM_SET_LOCAL(0, WASM_I32V(333))),
       WASM_GET_LOCAL(0), kExprEnd});

329
  const byte kTestI31 = tester.DefineFunction(
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
      tester.sigs.i_v(), {kWasmI32, kWasmEqRef},
      {WASM_BLOCK(WASM_SET_LOCAL(0, WASM_I32V(111)),
                  // Pipe an i31ref through a local so it's statically typed
                  // as eqref.
                  WASM_SET_LOCAL(1, WASM_I31_NEW(WASM_I32V(42))),
                  WASM_GET_LOCAL(1),
                  // The i31 is not a struct, so this branch isn't taken.
                  WASM_BR_ON_CAST(0, WASM_GET_GLOBAL(rtt_index)),
                  WASM_SET_LOCAL(0, WASM_I32V(222)),  // Final result.
                  // This branch is taken.
                  WASM_BR_ON_CAST(0, WASM_RTT_CANON(kLocalI31Ref)),
                  // Not executed due to the branch.
                  WASM_DROP, WASM_SET_LOCAL(0, WASM_I32V(333))),
       WASM_GET_LOCAL(0), kExprEnd});

345
  const byte kTestNull = tester.DefineFunction(
346 347 348 349 350 351 352 353 354 355
      tester.sigs.i_v(), {kWasmI32, kWasmEqRef},
      {WASM_BLOCK(WASM_SET_LOCAL(0, WASM_I32V(111)),
                  WASM_GET_LOCAL(1),  // Put a nullref onto the value stack.
                  // Neither of these branches is taken for nullref.
                  WASM_BR_ON_CAST(0, WASM_RTT_CANON(kLocalI31Ref)),
                  WASM_SET_LOCAL(0, WASM_I32V(222)),
                  WASM_BR_ON_CAST(0, WASM_GET_GLOBAL(rtt_index)), WASM_DROP,
                  WASM_SET_LOCAL(0, WASM_I32V(333))),  // Final result.
       WASM_GET_LOCAL(0), kExprEnd});

356
  const byte kTypedAfterBranch = tester.DefineFunction(
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
      tester.sigs.i_v(), {kWasmI32, kWasmEqRef},
      {WASM_SET_LOCAL(1, WASM_STRUCT_NEW_WITH_RTT(type_index, WASM_I32V(42),
                                                  WASM_GET_GLOBAL(rtt_index))),
       WASM_BLOCK(WASM_SET_LOCAL(
           // The outer block catches the struct left behind by the inner block
           // and reads its field.
           0,
           WASM_STRUCT_GET(
               type_index, 0,
               // The inner block should take the early branch with a struct
               // on the stack.
               WASM_BLOCK_R(ValueType::Ref(type_index, kNonNullable),
                            WASM_GET_LOCAL(1),
                            WASM_BR_ON_CAST(0, WASM_GET_GLOBAL(rtt_index)),
                            // Returning 123 is the unreachable failure case.
                            WASM_SET_LOCAL(0, WASM_I32V(123)), WASM_BR(1))))),
       WASM_GET_LOCAL(0), kExprEnd});

  tester.CompileModule();
  tester.CheckResult(kTestStruct, 222);
  tester.CheckResult(kTestI31, 222);
  tester.CheckResult(kTestNull, 333);
  tester.CheckResult(kTypedAfterBranch, 42);
}

382 383
TEST(WasmRefEq) {
  WasmGCTester tester;
384
  byte type_index = tester.DefineStruct({F(kWasmI32, true), F(kWasmI32, true)});
385 386 387 388
  ValueType kRefTypes[] = {ref(type_index)};
  ValueType kOptRefType = optref(type_index);
  FunctionSig sig_q_v(1, 0, kRefTypes);

389
  byte local_index = 0;
390
  const byte kFunc = tester.DefineFunction(
391
      tester.sigs.i_v(), {kOptRefType},
392 393 394
      {WASM_SET_LOCAL(local_index, WASM_STRUCT_NEW_WITH_RTT(
                                       type_index, WASM_I32V(55), WASM_I32V(66),
                                       WASM_RTT_CANON(type_index))),
395
       WASM_I32_ADD(
396 397 398 399
           WASM_I32_SHL(
               WASM_REF_EQ(  // true
                   WASM_GET_LOCAL(local_index), WASM_GET_LOCAL(local_index)),
               WASM_I32V(0)),
400 401
           WASM_I32_ADD(
               WASM_I32_SHL(WASM_REF_EQ(  // false
402
                                WASM_GET_LOCAL(local_index),
403 404 405
                                WASM_STRUCT_NEW_WITH_RTT(
                                    type_index, WASM_I32V(55), WASM_I32V(66),
                                    WASM_RTT_CANON(type_index))),
406 407
                            WASM_I32V(1)),
               WASM_I32_ADD(WASM_I32_SHL(  // false
408
                                WASM_REF_EQ(WASM_GET_LOCAL(local_index),
409
                                            WASM_REF_NULL(type_index)),
410 411
                                WASM_I32V(2)),
                            WASM_I32_SHL(WASM_REF_EQ(  // true
412 413
                                             WASM_REF_NULL(type_index),
                                             WASM_REF_NULL(type_index)),
414 415
                                         WASM_I32V(3))))),
       kExprEnd});
416

417
  tester.CompileModule();
418
  tester.CheckResult(kFunc, 0b1001);
419 420
}

421 422 423
TEST(WasmPackedStructU) {
  WasmGCTester tester;

424
  const byte type_index = tester.DefineStruct(
425
      {F(kWasmI8, true), F(kWasmI16, true), F(kWasmI32, true)});
426
  ValueType struct_type = optref(type_index);
427

428
  const byte local_index = 0;
429 430 431 432

  int32_t expected_output_0 = 0x1234;
  int32_t expected_output_1 = -1;

433
  const byte kF0 = tester.DefineFunction(
434
      tester.sigs.i_v(), {struct_type},
435
      {WASM_SET_LOCAL(local_index,
436 437 438 439
                      WASM_STRUCT_NEW_WITH_RTT(
                          type_index, WASM_I32V(expected_output_0),
                          WASM_I32V(expected_output_1), WASM_I32V(0x12345678),
                          WASM_RTT_CANON(type_index))),
440 441 442
       WASM_STRUCT_GET_U(type_index, 0, WASM_GET_LOCAL(local_index)),
       kExprEnd});

443
  const byte kF1 = tester.DefineFunction(
444
      tester.sigs.i_v(), {struct_type},
445
      {WASM_SET_LOCAL(local_index,
446 447 448 449
                      WASM_STRUCT_NEW_WITH_RTT(
                          type_index, WASM_I32V(expected_output_0),
                          WASM_I32V(expected_output_1), WASM_I32V(0x12345678),
                          WASM_RTT_CANON(type_index))),
450 451 452 453
       WASM_STRUCT_GET_U(type_index, 1, WASM_GET_LOCAL(local_index)),
       kExprEnd});
  tester.CompileModule();

454 455
  tester.CheckResult(kF0, static_cast<uint8_t>(expected_output_0));
  tester.CheckResult(kF1, static_cast<uint16_t>(expected_output_1));
456 457 458 459 460
}

TEST(WasmPackedStructS) {
  WasmGCTester tester;

461
  const byte type_index = tester.DefineStruct(
462
      {F(kWasmI8, true), F(kWasmI16, true), F(kWasmI32, true)});
463
  ValueType struct_type = optref(type_index);
464

465
  const byte local_index = 0;
466 467 468 469

  int32_t expected_output_0 = 0x80;
  int32_t expected_output_1 = 42;

470
  const byte kF0 = tester.DefineFunction(
471
      tester.sigs.i_v(), {struct_type},
472 473
      {WASM_SET_LOCAL(
           local_index,
474 475 476
           WASM_STRUCT_NEW_WITH_RTT(type_index, WASM_I32V(expected_output_0),
                                    WASM_I32V(expected_output_1), WASM_I32V(0),
                                    WASM_RTT_CANON(type_index))),
477 478 479
       WASM_STRUCT_GET_S(type_index, 0, WASM_GET_LOCAL(local_index)),
       kExprEnd});

480
  const byte kF1 = tester.DefineFunction(
481
      tester.sigs.i_v(), {struct_type},
482 483 484 485 486
      {WASM_SET_LOCAL(
           local_index,
           WASM_STRUCT_NEW_WITH_RTT(type_index, WASM_I32V(0x80),
                                    WASM_I32V(expected_output_1), WASM_I32V(0),
                                    WASM_RTT_CANON(type_index))),
487 488 489 490 491
       WASM_STRUCT_GET_S(type_index, 1, WASM_GET_LOCAL(local_index)),
       kExprEnd});

  tester.CompileModule();

492 493
  tester.CheckResult(kF0, static_cast<int8_t>(expected_output_0));
  tester.CheckResult(kF1, static_cast<int16_t>(expected_output_1));
494 495
}

496 497
TEST(WasmLetInstruction) {
  WasmGCTester tester;
498
  const byte type_index =
499
      tester.DefineStruct({F(kWasmI32, true), F(kWasmI32, true)});
500

501 502 503
  const byte let_local_index = 0;
  const byte let_field_index = 0;
  const byte kLetTest1 = tester.DefineFunction(
504
      tester.sigs.i_v(), {},
505
      {WASM_LET_1_I(
506
           WASM_SEQ(kLocalRef, type_index),
507 508 509 510
           WASM_STRUCT_NEW_WITH_RTT(type_index, WASM_I32V(42), WASM_I32V(52),
                                    WASM_RTT_CANON(type_index)),
           WASM_STRUCT_GET(type_index, let_field_index,
                           WASM_GET_LOCAL(let_local_index))),
511 512
       kExprEnd});

513 514
  const byte let_2_field_index = 0;
  const byte kLetTest2 = tester.DefineFunction(
515
      tester.sigs.i_v(), {},
516 517
      {WASM_LET_2_I(
           kLocalI32, WASM_I32_ADD(WASM_I32V(42), WASM_I32V(-32)),
518
           WASM_SEQ(kLocalRef, type_index),
519 520 521 522 523
           WASM_STRUCT_NEW_WITH_RTT(type_index, WASM_I32V(42), WASM_I32V(52),
                                    WASM_RTT_CANON(type_index)),
           WASM_I32_MUL(WASM_STRUCT_GET(type_index, let_2_field_index,
                                        WASM_GET_LOCAL(1)),
                        WASM_GET_LOCAL(0))),
524 525
       kExprEnd});

526
  const byte kLetTestLocals = tester.DefineFunction(
527
      tester.sigs.i_i(), {kWasmI32},
528 529 530 531 532 533 534 535
      {WASM_SET_LOCAL(1, WASM_I32V(100)),
       WASM_LET_2_I(
           kLocalI32, WASM_I32V(1), kLocalI32, WASM_I32V(10),
           WASM_I32_SUB(WASM_I32_ADD(WASM_GET_LOCAL(0),     // 1st let-local
                                     WASM_GET_LOCAL(2)),    // Parameter
                        WASM_I32_ADD(WASM_GET_LOCAL(1),     // 2nd let-local
                                     WASM_GET_LOCAL(3)))),  // Function local
       kExprEnd});
536
  // Result: (1 + 1000) - (10 + 100) = 891
537

538 539
  const byte let_erase_local_index = 0;
  const byte kLetTestErase = tester.DefineFunction(
540 541 542 543
      tester.sigs.i_v(), {kWasmI32},
      {WASM_SET_LOCAL(let_erase_local_index, WASM_I32V(0)),
       WASM_LET_1_V(kLocalI32, WASM_I32V(1), WASM_NOP),
       WASM_GET_LOCAL(let_erase_local_index), kExprEnd});
544 545 546
  // The result should be 0 and not 1, as local_get(0) refers to the original
  // local.

547
  tester.CompileModule();
548

549 550 551 552
  tester.CheckResult(kLetTest1, 42);
  tester.CheckResult(kLetTest2, 420);
  tester.CheckResult(kLetTestLocals, 891, 1000);
  tester.CheckResult(kLetTestErase, 0);
553 554
}

555 556
TEST(WasmBasicArray) {
  WasmGCTester tester;
557
  const byte type_index = tester.DefineArray(wasm::kWasmI32, true);
558
  ValueType kRefTypes[] = {ref(type_index)};
559
  FunctionSig sig_q_v(1, 0, kRefTypes);
560
  ValueType kOptRefType = optref(type_index);
561 562

  // f: a = [12, 12, 12]; a[1] = 42; return a[arg0]
563 564
  const byte local_index = 1;
  const byte kGetElem = tester.DefineFunction(
565
      tester.sigs.i_i(), {kOptRefType},
566 567 568
      {WASM_SET_LOCAL(local_index, WASM_ARRAY_NEW_WITH_RTT(
                                       type_index, WASM_I32V(12), WASM_I32V(3),
                                       WASM_RTT_CANON(type_index))),
569 570 571 572 573
       WASM_ARRAY_SET(type_index, WASM_GET_LOCAL(local_index), WASM_I32V(1),
                      WASM_I32V(42)),
       WASM_ARRAY_GET(type_index, WASM_GET_LOCAL(local_index),
                      WASM_GET_LOCAL(0)),
       kExprEnd});
574

575
  // Reads and returns an array's length.
576
  const byte kGetLength = tester.DefineFunction(
577
      tester.sigs.i_v(), {},
578 579 580
      {WASM_ARRAY_LEN(type_index, WASM_ARRAY_NEW_WITH_RTT(
                                      type_index, WASM_I32V(0), WASM_I32V(42),
                                      WASM_RTT_CANON(type_index))),
581 582
       kExprEnd});

583
  // Create an array of length 2, initialized to [42, 42].
584
  const byte kAllocate = tester.DefineFunction(
585
      &sig_q_v, {},
586 587 588
      {WASM_ARRAY_NEW_WITH_RTT(type_index, WASM_I32V(42), WASM_I32V(2),
                               WASM_RTT_CANON(type_index)),
       kExprEnd});
589

590
  tester.CompileModule();
591

592 593 594 595 596 597
  tester.CheckResult(kGetElem, 12, 0);
  tester.CheckResult(kGetElem, 42, 1);
  tester.CheckResult(kGetElem, 12, 2);
  tester.CheckHasThrown(kGetElem, 3);
  tester.CheckHasThrown(kGetElem, -1);
  tester.CheckResult(kGetLength, 42);
598

599
  MaybeHandle<Object> h_result = tester.GetResultObject(kAllocate);
600
  CHECK(h_result.ToHandleChecked()->IsWasmArray());
601
#if OBJECT_PRINT
602
  h_result.ToHandleChecked()->Print();
603 604 605
#endif
}

606 607
TEST(WasmPackedArrayU) {
  WasmGCTester tester;
608
  const byte array_index = tester.DefineArray(kWasmI8, true);
609
  ValueType array_type = optref(array_index);
610

611 612
  const byte param_index = 0;
  const byte local_index = 1;
613 614 615

  int32_t expected_output_3 = 258;

616
  const byte kF = tester.DefineFunction(
617
      tester.sigs.i_i(), {array_type},
618 619 620
      {WASM_SET_LOCAL(local_index, WASM_ARRAY_NEW_WITH_RTT(
                                       array_index, WASM_I32V(0), WASM_I32V(4),
                                       WASM_RTT_CANON(array_index))),
621 622 623 624 625 626 627 628 629 630 631 632 633
       WASM_ARRAY_SET(array_index, WASM_GET_LOCAL(local_index), WASM_I32V(0),
                      WASM_I32V(1)),
       WASM_ARRAY_SET(array_index, WASM_GET_LOCAL(local_index), WASM_I32V(1),
                      WASM_I32V(10)),
       WASM_ARRAY_SET(array_index, WASM_GET_LOCAL(local_index), WASM_I32V(2),
                      WASM_I32V(200)),
       WASM_ARRAY_SET(array_index, WASM_GET_LOCAL(local_index), WASM_I32V(3),
                      WASM_I32V(expected_output_3)),
       WASM_ARRAY_GET_U(array_index, WASM_GET_LOCAL(local_index),
                        WASM_GET_LOCAL(param_index)),
       kExprEnd});

  tester.CompileModule();
634 635 636
  tester.CheckResult(kF, 1, 0);
  tester.CheckResult(kF, 10, 1);
  tester.CheckResult(kF, 200, 2);
637
  // Only the 2 lsb's of 258 should be stored in the array.
638
  tester.CheckResult(kF, static_cast<uint8_t>(expected_output_3), 3);
639 640 641 642
}

TEST(WasmPackedArrayS) {
  WasmGCTester tester;
643
  const byte array_index = tester.DefineArray(kWasmI16, true);
644
  ValueType array_type = optref(array_index);
645 646 647

  int32_t expected_outputs[] = {0x12345678, 10, 0xFEDC, 0xFF1234};

648 649 650
  const byte param_index = 0;
  const byte local_index = 1;
  const byte kF = tester.DefineFunction(
651
      tester.sigs.i_i(), {array_type},
652 653
      {WASM_SET_LOCAL(
           local_index,
654 655
           WASM_ARRAY_NEW_WITH_RTT(array_index, WASM_I32V(0x12345678),
                                   WASM_I32V(4), WASM_RTT_CANON(array_index))),
656 657 658 659 660 661 662 663 664 665 666 667
       WASM_ARRAY_SET(array_index, WASM_GET_LOCAL(local_index), WASM_I32V(1),
                      WASM_I32V(10)),
       WASM_ARRAY_SET(array_index, WASM_GET_LOCAL(local_index), WASM_I32V(2),
                      WASM_I32V(0xFEDC)),
       WASM_ARRAY_SET(array_index, WASM_GET_LOCAL(local_index), WASM_I32V(3),
                      WASM_I32V(0xFF1234)),
       WASM_ARRAY_GET_S(array_index, WASM_GET_LOCAL(local_index),
                        WASM_GET_LOCAL(param_index)),
       kExprEnd});

  tester.CompileModule();
  // Exactly the 2 lsb's should be stored by array.new.
668 669
  tester.CheckResult(kF, static_cast<int16_t>(expected_outputs[0]), 0);
  tester.CheckResult(kF, static_cast<int16_t>(expected_outputs[1]), 1);
670
  // Sign should be extended.
671
  tester.CheckResult(kF, static_cast<int16_t>(expected_outputs[2]), 2);
672
  // Exactly the 2 lsb's should be stored by array.set.
673
  tester.CheckResult(kF, static_cast<int16_t>(expected_outputs[3]), 3);
674 675
}

676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708
TEST(NewDefault) {
  WasmGCTester tester;
  const byte struct_type = tester.DefineStruct(
      {F(wasm::kWasmI32, true), F(wasm::kWasmF64, true), F(optref(0), true)});
  const byte array_type = tester.DefineArray(wasm::kWasmI32, true);
  // Returns: struct[0] + f64_to_i32(struct[1]) + (struct[2].is_null ^ 1) == 0.
  const byte allocate_struct = tester.DefineFunction(
      tester.sigs.i_v(), {optref(struct_type)},
      {WASM_SET_LOCAL(0, WASM_STRUCT_NEW_DEFAULT(struct_type,
                                                 WASM_RTT_CANON(struct_type))),
       WASM_I32_ADD(
           WASM_I32_ADD(WASM_STRUCT_GET(struct_type, 0, WASM_GET_LOCAL(0)),
                        WASM_I32_SCONVERT_F64(WASM_STRUCT_GET(
                            struct_type, 1, WASM_GET_LOCAL(0)))),
           WASM_I32_XOR(WASM_REF_IS_NULL(
                            WASM_STRUCT_GET(struct_type, 2, WASM_GET_LOCAL(0))),
                        WASM_I32V(1))),
       kExprEnd});
  const byte allocate_array = tester.DefineFunction(
      tester.sigs.i_v(), {optref(array_type)},
      {WASM_SET_LOCAL(0, WASM_ARRAY_NEW_DEFAULT(array_type, WASM_I32V(2),
                                                WASM_RTT_CANON(array_type))),
       WASM_I32_ADD(
           WASM_ARRAY_GET(array_type, WASM_GET_LOCAL(0), WASM_I32V(0)),
           WASM_ARRAY_GET(array_type, WASM_GET_LOCAL(0), WASM_I32V(1))),
       kExprEnd});

  tester.CompileModule();

  tester.CheckResult(allocate_struct, 0);
  tester.CheckResult(allocate_array, 0);
}

709 710
TEST(BasicRTT) {
  WasmGCTester tester;
711 712
  const byte type_index = tester.DefineStruct({F(wasm::kWasmI32, true)});
  const byte subtype_index =
713
      tester.DefineStruct({F(wasm::kWasmI32, true), F(wasm::kWasmI32, true)});
714
  ValueType kRttTypes[] = {ValueType::Rtt(type_index, 1)};
715
  FunctionSig sig_t_v(1, 0, kRttTypes);
716 717 718
  ValueType kRttSubtypes[] = {
      ValueType::Rtt(static_cast<HeapType>(subtype_index), 2)};
  FunctionSig sig_t2_v(1, 0, kRttSubtypes);
719 720
  ValueType kRttTypesDeeper[] = {ValueType::Rtt(type_index, 2)};
  FunctionSig sig_t3_v(1, 0, kRttTypesDeeper);
721
  ValueType kRefTypes[] = {ref(type_index)};
722
  FunctionSig sig_q_v(1, 0, kRefTypes);
723

724
  const byte kRttCanon = tester.DefineFunction(
725
      &sig_t_v, {}, {WASM_RTT_CANON(type_index), kExprEnd});
726
  const byte kRttSub = tester.DefineFunction(
727
      &sig_t2_v, {},
728
      {WASM_RTT_SUB(subtype_index, WASM_RTT_CANON(type_index)), kExprEnd});
729
  const byte kRttSubGeneric = tester.DefineFunction(
730
      &sig_t3_v, {},
731
      {WASM_RTT_SUB(type_index, WASM_RTT_CANON(kLocalEqRef)), kExprEnd});
732
  const byte kStructWithRtt = tester.DefineFunction(
733 734 735 736
      &sig_q_v, {},
      {WASM_STRUCT_NEW_WITH_RTT(type_index, WASM_I32V(42),
                                WASM_RTT_CANON(type_index)),
       kExprEnd});
737 738 739 740 741 742 743 744 745 746 747
  const int kFieldIndex = 1;
  const int kLocalStructIndex = 1;  // Shifted in 'let' block.
  const int kLocalRttIndex = 0;     // Let-bound, hence first local.
  // This implements the following function:
  //   var local_struct: type0;
  //   let (local_rtt = rtt.sub(rtt.canon(type0), type1) in {
  //     local_struct = new type1 with rtt 'local_rtt';
  //     return (ref.test local_struct local_rtt) +
  //            ((ref.cast local_struct local_rtt)[field0]);
  //   }
  // The expected return value is 1+42 = 43.
748
  const byte kRefCast = tester.DefineFunction(
749
      tester.sigs.i_v(), {optref(type_index)},
750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765
      {WASM_LET_1_I(
          WASM_RTT(2, subtype_index),
          WASM_RTT_SUB(subtype_index, WASM_RTT_CANON(type_index)),
          WASM_SET_LOCAL(kLocalStructIndex,
                         WASM_STRUCT_NEW_WITH_RTT(
                             subtype_index, WASM_I32V(11), WASM_I32V(42),
                             WASM_GET_LOCAL(kLocalRttIndex))),
          WASM_I32_ADD(
              WASM_REF_TEST(type_index, subtype_index,
                            WASM_GET_LOCAL(kLocalStructIndex),
                            WASM_GET_LOCAL(kLocalRttIndex)),
              WASM_STRUCT_GET(subtype_index, kFieldIndex,
                              WASM_REF_CAST(type_index, subtype_index,
                                            WASM_GET_LOCAL(kLocalStructIndex),
                                            WASM_GET_LOCAL(kLocalRttIndex)))),
          kExprEnd)});
766 767 768

  tester.CompileModule();

769 770
  Handle<Object> ref_result =
      tester.GetResultObject(kRttCanon).ToHandleChecked();
771 772 773 774 775 776 777

  CHECK(ref_result->IsMap());
  Handle<Map> map = Handle<Map>::cast(ref_result);
  CHECK(map->IsWasmStructMap());
  CHECK_EQ(reinterpret_cast<Address>(
               tester.instance()->module()->struct_type(type_index)),
           map->wasm_type_info().foreign_address());
778

779 780
  Handle<Object> subref_result =
      tester.GetResultObject(kRttSub).ToHandleChecked();
781 782 783 784 785 786
  CHECK(subref_result->IsMap());
  Handle<Map> submap = Handle<Map>::cast(subref_result);
  CHECK_EQ(*map, submap->wasm_type_info().parent());
  CHECK_EQ(reinterpret_cast<Address>(
               tester.instance()->module()->struct_type(subtype_index)),
           submap->wasm_type_info().foreign_address());
787 788 789 790 791 792 793 794 795
  Handle<Object> subref_result_canonicalized =
      tester.GetResultObject(kRttSub).ToHandleChecked();
  CHECK(subref_result.is_identical_to(subref_result_canonicalized));

  Handle<Object> sub_generic_1 =
      tester.GetResultObject(kRttSubGeneric).ToHandleChecked();
  Handle<Object> sub_generic_2 =
      tester.GetResultObject(kRttSubGeneric).ToHandleChecked();
  CHECK(sub_generic_1.is_identical_to(sub_generic_2));
796

797
  Handle<Object> s = tester.GetResultObject(kStructWithRtt).ToHandleChecked();
798 799
  CHECK(s->IsWasmStruct());
  CHECK_EQ(Handle<WasmStruct>::cast(s)->map(), *map);
800 801

  tester.CheckResult(kRefCast, 43);
802 803
}

804
TEST(ArrayNewMap) {
805
  WasmGCTester tester;
806
  const byte type_index = tester.DefineArray(kWasmI32, true);
807 808 809

  ValueType array_type = ValueType::Ref(type_index, kNonNullable);
  FunctionSig sig(1, 0, &array_type);
810
  const byte array_new_with_rtt = tester.DefineFunction(
811 812 813 814 815 816 817
      &sig, {},
      {WASM_ARRAY_NEW_WITH_RTT(type_index, WASM_I32V(10), WASM_I32V(42),
                               WASM_RTT_CANON(type_index)),
       kExprEnd});

  ValueType rtt_type = ValueType::Rtt(type_index, 1);
  FunctionSig rtt_canon_sig(1, 0, &rtt_type);
818
  const byte kRttCanon = tester.DefineFunction(
819 820 821 822 823 824 825 826 827 828 829 830
      &rtt_canon_sig, {}, {WASM_RTT_CANON(type_index), kExprEnd});

  tester.CompileModule();

  Handle<Object> map = tester.GetResultObject(kRttCanon).ToHandleChecked();

  Handle<Object> result =
      tester.GetResultObject(array_new_with_rtt).ToHandleChecked();
  CHECK(result->IsWasmArray());
  CHECK_EQ(Handle<WasmArray>::cast(result)->map(), *map);
}

831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902
TEST(FunctionRefs) {
  WasmGCTester tester;
  const byte func_index =
      tester.DefineFunction(tester.sigs.i_v(), {}, {WASM_I32V(42), kExprEnd});
  const byte sig_index = 0;

  const byte other_sig_index = tester.DefineSignature(tester.sigs.d_d());

  // This is just so func_index counts as "declared".
  tester.AddGlobal(ValueType::Ref(sig_index, kNullable), false,
                   WasmInitExpr::RefFuncConst(func_index));

  ValueType func_type = ValueType::Ref(sig_index, kNonNullable);
  FunctionSig sig_func(1, 0, &func_type);

  ValueType rtt1 = ValueType::Rtt(sig_index, 1);
  FunctionSig sig_rtt1(1, 0, &rtt1);
  const byte rtt_canon = tester.DefineFunction(
      &sig_rtt1, {}, {WASM_RTT_CANON(sig_index), kExprEnd});

  ValueType rtt2 = ValueType::Rtt(sig_index, 2);
  FunctionSig sig_rtt2(1, 0, &rtt2);
  const byte rtt_sub = tester.DefineFunction(
      &sig_rtt2, {},
      {WASM_RTT_SUB(sig_index, WASM_RTT_CANON(kLocalFuncRef)), kExprEnd});

  const byte cast = tester.DefineFunction(
      &sig_func, {kWasmFuncRef},
      {WASM_SET_LOCAL(0, WASM_REF_FUNC(func_index)),
       WASM_REF_CAST(kLocalFuncRef, sig_index, WASM_GET_LOCAL(0),
                     WASM_RTT_CANON(sig_index)),
       kExprEnd});

  const byte cast_reference = tester.DefineFunction(
      &sig_func, {}, {WASM_REF_FUNC(sig_index), kExprEnd});

  const byte test = tester.DefineFunction(
      tester.sigs.i_v(), {kWasmFuncRef},
      {WASM_SET_LOCAL(0, WASM_REF_FUNC(func_index)),
       WASM_REF_TEST(kLocalFuncRef, other_sig_index, WASM_GET_LOCAL(0),
                     WASM_RTT_CANON(other_sig_index)),
       kExprEnd});

  tester.CompileModule();

  Handle<Object> result_canon =
      tester.GetResultObject(rtt_canon).ToHandleChecked();
  CHECK(result_canon->IsMap());
  Handle<Map> map_canon = Handle<Map>::cast(result_canon);
  CHECK(map_canon->IsJSFunctionMap());

  Handle<Object> result_sub = tester.GetResultObject(rtt_sub).ToHandleChecked();
  CHECK(result_sub->IsMap());
  Handle<Map> map_sub = Handle<Map>::cast(result_sub);
  CHECK(map_sub->IsJSFunctionMap());

  Handle<Object> result_cast = tester.GetResultObject(cast).ToHandleChecked();
  CHECK(result_cast->IsJSFunction());
  Handle<JSFunction> cast_function = Handle<JSFunction>::cast(result_cast);

  Handle<Object> result_cast_reference =
      tester.GetResultObject(cast_reference).ToHandleChecked();
  CHECK(result_cast_reference->IsJSFunction());
  Handle<JSFunction> cast_function_reference =
      Handle<JSFunction>::cast(result_cast_reference);

  CHECK_EQ(cast_function->code().raw_instruction_start(),
           cast_function_reference->code().raw_instruction_start());

  tester.CheckResult(test, 0);
}

903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921
TEST(CallRef) {
  WasmGCTester tester;
  byte callee = tester.DefineFunction(
      tester.sigs.i_ii(), {},
      {WASM_I32_ADD(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), kExprEnd});
  byte caller = tester.DefineFunction(
      tester.sigs.i_i(), {},
      {WASM_CALL_REF(WASM_REF_FUNC(callee), WASM_I32V(42), WASM_GET_LOCAL(0)),
       kExprEnd});

  // This is just so func_index counts as "declared".
  tester.AddGlobal(ValueType::Ref(0, kNullable), false,
                   WasmInitExpr::RefFuncConst(callee));

  tester.CompileModule();

  tester.CheckResult(caller, 47, 5);
}

922 923
TEST(RefTestCastNull) {
  WasmGCTester tester;
924
  byte type_index = tester.DefineStruct({F(wasm::kWasmI32, true)});
925

926
  const byte kRefTestNull = tester.DefineFunction(
927 928 929 930 931
      tester.sigs.i_v(), {},
      {WASM_REF_TEST(type_index, type_index, WASM_REF_NULL(type_index),
                     WASM_RTT_CANON(type_index)),
       kExprEnd});

932
  const byte kRefCastNull = tester.DefineFunction(
933 934 935 936 937 938 939 940 941 942
      tester.sigs.i_i(),  // Argument and return value ignored
      {},
      {WASM_REF_CAST(type_index, type_index, WASM_REF_NULL(type_index),
                     WASM_RTT_CANON(type_index)),
       kExprDrop, WASM_I32V(0), kExprEnd});
  tester.CompileModule();
  tester.CheckResult(kRefTestNull, 0);
  tester.CheckHasThrown(kRefCastNull, 0);
}

943 944
TEST(BasicI31) {
  WasmGCTester tester;
945
  const byte kSigned = tester.DefineFunction(
946
      tester.sigs.i_i(), {},
947
      {WASM_I31_GET_S(WASM_I31_NEW(WASM_GET_LOCAL(0))), kExprEnd});
948
  const byte kUnsigned = tester.DefineFunction(
949
      tester.sigs.i_i(), {},
950 951 952 953
      {WASM_I31_GET_U(WASM_I31_NEW(WASM_GET_LOCAL(0))), kExprEnd});
  // TODO(7748): Support (rtt.canon i31), and add a test like:
  // (ref.test (i31.new ...) (rtt.canon i31)).
  tester.CompileModule();
954 955
  tester.CheckResult(kSigned, 123, 123);
  tester.CheckResult(kUnsigned, 123, 123);
956
  // Truncation:
957 958
  tester.CheckResult(kSigned, 0x1234, static_cast<int32_t>(0x80001234));
  tester.CheckResult(kUnsigned, 0x1234, static_cast<int32_t>(0x80001234));
959
  // Sign/zero extension:
960 961 962 963
  tester.CheckResult(kSigned, -1, 0x7FFFFFFF);
  tester.CheckResult(kUnsigned, 0x7FFFFFFF, 0x7FFFFFFF);
}

964 965
TEST(I31Casts) {
  WasmGCTester tester;
966 967 968 969 970
  const byte struct_type = tester.DefineStruct({F(wasm::kWasmI32, true)});
  const byte i31_rtt =
      tester.AddGlobal(ValueType::Rtt(HeapType::kI31, 1), false,
                       WasmInitExpr::RttCanon(HeapType::kI31));
  const byte struct_rtt =
971 972 973 974 975
      tester.AddGlobal(ValueType::Rtt(struct_type, 1), false,
                       WasmInitExpr::RttCanon(
                           static_cast<HeapType::Representation>(struct_type)));
  // Adds the result of a successful typecheck to the untagged value, i.e.
  // should return 1 + 42 = 43.
976
  const byte kTestAndCastSuccess = tester.DefineFunction(
977 978 979 980 981 982 983 984 985 986
      tester.sigs.i_v(), {kWasmEqRef},
      {WASM_SET_LOCAL(0, WASM_I31_NEW(WASM_I32V(42))),
       WASM_I32_ADD(WASM_REF_TEST(kLocalEqRef, kLocalI31Ref, WASM_GET_LOCAL(0),
                                  WASM_GET_GLOBAL(i31_rtt)),
                    WASM_I31_GET_S(WASM_REF_CAST(kLocalEqRef, kLocalI31Ref,
                                                 WASM_GET_LOCAL(0),
                                                 WASM_GET_GLOBAL(i31_rtt)))),
       kExprEnd});
  // Adds the results of two unsuccessful type checks (an i31ref is not a
  // struct, nor the other way round).
987
  const byte kTestFalse = tester.DefineFunction(
988 989 990 991 992 993 994 995 996 997
      tester.sigs.i_v(), {},
      {WASM_I32_ADD(
           WASM_REF_TEST(kLocalEqRef, kLocalI31Ref,
                         WASM_STRUCT_NEW_WITH_RTT(struct_type, WASM_I32V(42),
                                                  WASM_GET_GLOBAL(struct_rtt)),
                         WASM_GET_GLOBAL(i31_rtt)),
           WASM_REF_TEST(kLocalEqRef, struct_type, WASM_I31_NEW(WASM_I32V(23)),
                         WASM_GET_GLOBAL(struct_rtt))),
       kExprEnd});
  // Tries to cast an i31ref to a struct, which should trap.
998
  const byte kCastI31ToStruct = tester.DefineFunction(
999 1000 1001 1002 1003 1004 1005 1006
      tester.sigs.i_i(),  // Argument and return value ignored
      {},
      {WASM_STRUCT_GET(
           struct_type, 0,
           WASM_REF_CAST(kLocalEqRef, struct_type, WASM_I31_NEW(WASM_I32V(42)),
                         WASM_GET_GLOBAL(struct_rtt))),
       kExprEnd});
  // Tries to cast a struct to i31ref, which should trap.
1007
  const byte kCastStructToI31 = tester.DefineFunction(
1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022
      tester.sigs.i_i(),  // Argument and return value ignored
      {},
      {WASM_I31_GET_S(
           WASM_REF_CAST(kLocalEqRef, kLocalI31Ref,
                         WASM_STRUCT_NEW_WITH_RTT(struct_type, WASM_I32V(42),
                                                  WASM_GET_GLOBAL(struct_rtt)),
                         WASM_GET_GLOBAL(i31_rtt))),
       kExprEnd});
  tester.CompileModule();
  tester.CheckResult(kTestAndCastSuccess, 43);
  tester.CheckResult(kTestFalse, 0);
  tester.CheckHasThrown(kCastI31ToStruct, 0);
  tester.CheckHasThrown(kCastStructToI31, 0);
}

1023
TEST(JsAccess) {
1024
  WasmGCTester tester;
1025
  const byte type_index = tester.DefineStruct({F(wasm::kWasmI32, true)});
1026
  ValueType kRefTypes[] = {ref(type_index)};
1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049
  ValueType kEqRefTypes[] = {kWasmEqRef};
  ValueType kEqToI[] = {kWasmI32, kWasmEqRef};
  FunctionSig sig_t_v(1, 0, kRefTypes);
  FunctionSig sig_q_v(1, 0, kEqRefTypes);
  FunctionSig sig_i_q(1, 1, kEqToI);

  tester.DefineExportedFunction(
      "disallowed", &sig_t_v,
      {WASM_STRUCT_NEW_WITH_RTT(type_index, WASM_I32V(42),
                                WASM_RTT_CANON(type_index)),
       kExprEnd});
  // Same code, different signature.
  tester.DefineExportedFunction(
      "producer", &sig_q_v,
      {WASM_STRUCT_NEW_WITH_RTT(type_index, WASM_I32V(42),
                                WASM_RTT_CANON(type_index)),
       kExprEnd});
  tester.DefineExportedFunction(
      "consumer", &sig_i_q,
      {WASM_STRUCT_GET(type_index, 0,
                       WASM_REF_CAST(kLocalEqRef, type_index, WASM_GET_LOCAL(0),
                                     WASM_RTT_CANON(type_index))),
       kExprEnd});
1050 1051

  tester.CompileModule();
1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075
  Isolate* isolate = tester.isolate();
  TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
  MaybeHandle<Object> maybe_result =
      tester.CallExportedFunction("disallowed", 0, nullptr);
  CHECK(maybe_result.is_null());
  CHECK(try_catch.HasCaught());
  try_catch.Reset();
  isolate->clear_pending_exception();

  maybe_result = tester.CallExportedFunction("producer", 0, nullptr);
  {
    Handle<Object> args[] = {maybe_result.ToHandleChecked()};
    maybe_result = tester.CallExportedFunction("consumer", 1, args);
  }
  Handle<Object> result = maybe_result.ToHandleChecked();
  CHECK(result->IsSmi());
  CHECK_EQ(42, Smi::cast(*result).value());
  // Calling {consumer} with any other object (e.g. the Smi we just got as
  // {result}) should trap.
  {
    Handle<Object> args[] = {result};
    maybe_result = tester.CallExportedFunction("consumer", 1, args);
  }
  CHECK(maybe_result.is_null());
1076
  CHECK(try_catch.HasCaught());
1077 1078
  try_catch.Reset();
  isolate->clear_pending_exception();
1079 1080
}

1081 1082 1083 1084
}  // namespace test_gc
}  // namespace wasm
}  // namespace internal
}  // namespace v8