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

// Test setting and removing breakpoints in Wasm.
// Similar tests exist as inspector tests already, but inspector tests are not
// run concurrently in multiple isolates (see `run-tests.py --isolates`).

load('test/mjsunit/wasm/wasm-module-builder.js');

const builder = new WasmModuleBuilder();
const body_a = [
  kExprLocalGet, 0,  // local.get i0
  kExprI32Const, 1,  // i32.const 1
  kExprI32Add        // i32.add i0 1
];
const fun_a = builder.addFunction('a', kSig_i_i).addBody(body_a).exportFunc();
const body_b = [
  kExprLocalGet, 0,                // local.get i0
  kExprCallFunction, fun_a.index,  // call a
  kExprLocalGet, 1,                // local.get i1
  kExprI32Sub,                     // i32.sub a(i0) i1
  kExprCallFunction, fun_a.index,  // call a
];
const fun_b = builder.addFunction('b', kSig_i_ii).addBody(body_b).exportFunc();

const instance = builder.instantiate();

Debug = debug.Debug;

const a_localget_offset = body_a.indexOf(kExprLocalGet);
const a_const_offset = body_a.indexOf(kExprI32Const);
const a_add_offset = body_a.indexOf(kExprI32Add);
const b_sub_offset = body_b.indexOf(kExprI32Sub);

const expected_breaks = [
  `$a:1:${fun_a.body_offset + a_add_offset}`,       // break in a at i32.add
  `$b:1:${fun_b.body_offset + b_sub_offset}`,       // break in b at i32.sub
  `$a:1:${fun_a.body_offset + a_localget_offset}`,  // break in a at local.get i0
  `$a:1:${fun_a.body_offset + a_const_offset}`      // break in a at i32.const 1
];
let error;
function onBreak(event, exec_state, data) {
  try {
    if (event != Debug.DebugEvent.Break) return;
    if (error) return;
    const pos =
        [data.functionName(), data.sourceLine(), data.sourceColumn()].join(':');
    const loc = [pos, data.sourceLineText()].join(':');
    print(`Break at ${loc}`);
    assertTrue(expected_breaks.length > 0, 'expecting more breaks');
    const expected_pos = expected_breaks.shift();
    assertEquals(expected_pos, pos);
    // When we stop in b, we add another breakpoint in a at offset 0 and remove
    // the existing breakpoint.
    if (data.functionName() === '$b') {
      Debug.setBreakPoint(instance.exports.a, 0, a_localget_offset);
      Debug.clearBreakPoint(breakpoint_a);
    }
    // When we stop at a at local.get, we set another breakpoint *in the same
    // function*, one instruction later (at i32.const).
    if (data.functionName() === '$a' &&
        data.sourceColumn() == fun_a.body_offset) {
      Debug.setBreakPoint(instance.exports.a, 0, a_const_offset);
    }
  } catch (e) {
    if (!error) error = e;
  }
}

Debug.setListener(onBreak);
const breakpoint_a = Debug.setBreakPoint(instance.exports.a, 0, a_add_offset);
const breakpoint_b = Debug.setBreakPoint(instance.exports.b, 0, b_sub_offset);
print('Running b(11).');
const result = instance.exports.b(11);
print('Returned from wasm.');
if (error) throw error;
assertEquals(0, expected_breaks.length, 'all breaks were hit');
