// Copyright 2022 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.

// Flags: --experimental-wasm-stringref --allow-natives-syntax
// We just want speculative inlining, but the "stress" variant doesn't like
// that flag for some reason, so use the GC flag which implies it.
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");

let kSig_w_v = makeSig([], [kWasmStringRef]);
let kSig_w_i = makeSig([kWasmI32], [kWasmStringRef]);
let kSig_v_w = makeSig([kWasmStringRef], []);

(function () {
  let huge_builder = new WasmModuleBuilder();
  huge_builder.addMemory(65001, undefined);

  huge_builder.addFunction("huge", kSig_w_v).exportFunc().addBody([
    kExprI32Const, 0,                  // address
    ...wasmI32Const(65000 * 65536),    // bytes
    ...GCInstr(kExprStringNewUtf8), 0  // memory index
  ]);

  let callee = huge_builder.addFunction("callee", kSig_w_i).addBody([
    kExprI32Const, 0,                  // address
    kExprLocalGet, 0,                  // bytes
    ...GCInstr(kExprStringNewUtf8), 0  // memory index
  ]);

  let caller = huge_builder.addFunction("caller", kSig_i_i).exportFunc()
    .addBody([
      kExprTry, kWasmI32,
      kExprLocalGet, 0,
      kExprCallFunction, callee.index,
      kExprDrop,
      kExprI32Const, 1,
      kExprCatchAll,
      kExprI32Const, 0,
      kExprEnd
    ]);

  let instance;
  try {
    instance = huge_builder.instantiate();
    // On 64-bit platforms, expect to see this message.
    console.log("Instantiation successful, proceeding.");
  } catch (e) {
    // 32-bit builds don't have enough virtual memory, that's OK.
    assertInstanceof(e, RangeError);
    assertMatches(/Cannot allocate Wasm memory for new instance/, e.message,
      'Error message');
    return;
  }

  // Bug 1: The Utf8Decoder can't handle more than kMaxInt bytes as input.
  assertThrows(() => instance.exports.huge(), RangeError);

  // Bug 2: Exceptions created by the JS-focused strings infrastructure must
  // be marked as uncatchable by Wasm.
  let f1 = instance.exports.caller;
  assertThrows(() => f1(2147483647), RangeError);

  // Bug 3: Builtin calls that have neither a kNoThrow annotation nor exception-
  // handling support make the Wasm inliner sad.
  for (let i = 0; i < 20; i++) f1(10);
  %WasmTierUpFunction(f1);
  f1(10);
})();

(function () {
  let builder = new WasmModuleBuilder();

  let concat_body = [];
  // This doubles the string 26 times, i.e. multiplies its length with a factor
  // of ~65 million.
  for (let i = 0; i < 26; i++) {
    concat_body.push(...[
      kExprLocalGet, 0, kExprLocalGet, 0,
      ...GCInstr(kExprStringConcat),
      kExprLocalSet, 0
    ]);
  }

  builder.addFunction('concat', kSig_v_w).exportFunc().addBody(concat_body);

  let instance = builder.instantiate();

  // Bug 4: Throwing in StringAdd must clear the "thread in wasm" bit.
  let f2 = instance.exports.concat;
  assertThrows(() => f2("1234567890"));  // 650M characters is too much.

  // Bug 5: Operations that can trap must not be marked as kEliminatable,
  // otherwise the trap may be eliminated.
  for (let i = 0; i < 3; i++) f2("a");   // 65M characters is okay.
  %WasmTierUpFunction(f2);
  assertThrows(() => f2("1234567890"));  // Optimized code still traps.
})();

(function TestThatOneCharacterStringsAreInternalized() {
  let builder = new WasmModuleBuilder();

  builder.addMemory(1, undefined);
  const kMemIndex = 0;
  builder.addDataSegment(0, [65, 0]);

  let a8 = builder.addArray(kWasmI8, true);
  let a16 = builder.addArray(kWasmI16, true);

  builder.addFunction('makeString8A', kSig_w_v).exportFunc().addBody([
    ...wasmI32Const(65),
    kGCPrefix, kExprArrayNewFixed, a8, 1,
    kExprI32Const, 0,
    kExprI32Const, 1,
    ...GCInstr(kExprStringNewWtf8Array),
  ]);

  builder.addFunction('makeString16A', kSig_w_v).exportFunc().addBody([
    ...wasmI32Const(65),
    kGCPrefix, kExprArrayNewFixed, a16, 1,
    kExprI32Const, 0,
    kExprI32Const, 1,
    ...GCInstr(kExprStringNewWtf16Array),
  ]);

  builder.addFunction('makeString8M', kSig_w_v).exportFunc().addBody([
    kExprI32Const, 0,
    kExprI32Const, 1,
    ...GCInstr(kExprStringNewWtf8), kMemIndex,
  ]);

  builder.addFunction('makeString16M', kSig_w_v).exportFunc().addBody([
    kExprI32Const, 0,
    kExprI32Const, 1,
    ...GCInstr(kExprStringNewWtf16), kMemIndex,
  ]);

  let wasm = builder.instantiate().exports;

  assertTrue(%IsSameHeapObject("A", wasm.makeString8A()));
  assertTrue(%IsSameHeapObject("A", wasm.makeString16A()));
  assertTrue(%IsSameHeapObject("A", wasm.makeString8M()));
  assertTrue(%IsSameHeapObject("A", wasm.makeString16M()));
})();
