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

let {session, contextGroup, Protocol} = InspectorTest.start('Tests imports in wasm');

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

// Build two modules A and B. A defines function func, which contains a
// breakpoint. This function is then imported by B and called via main. The
// breakpoint must be hit.
// This failed before (http://crbug.com/v8/5971).

var builder_a = new WasmModuleBuilder();
var func_idx = builder_a.addFunction('func', kSig_v_v)
                   .addBody([kExprNop])
                   .exportFunc()
                   .index;
var module_a_bytes = builder_a.toArray();

var builder_b = new WasmModuleBuilder();
var import_idx = builder_b.addImport('imp', 'f', kSig_v_v);
builder_b.addFunction('main', kSig_v_v)
    .addBody([kExprCallFunction, import_idx])
    .exportFunc();
var module_b_bytes = builder_b.toArray();

function instantiate(bytes, imp) {
  var buffer = new ArrayBuffer(bytes.length);
  var view = new Uint8Array(buffer);
  for (var i = 0; i < bytes.length; ++i) {
    view[i] = bytes[i] | 0;
  }

  var module = new WebAssembly.Module(buffer);
  // Add to global instances array.
  instances.push(new WebAssembly.Instance(module, imp));
}

var evalWithUrl = (code, url) => Protocol.Runtime.evaluate(
    {'expression': code + '\n//# sourceURL=v8://test/' + url});

session.setupScriptMap();

// Main promise chain:
Protocol.Debugger.enable()
    .then(() => InspectorTest.log('Installing code and global variable.'))
    .then(
        () => evalWithUrl(
            'var instances = [];\n' + instantiate.toString(), 'setup'))
    .then(() => InspectorTest.log('Calling instantiate function for module A.'))
    .then(
        () =>
            (evalWithUrl(
                 'instantiate(' + JSON.stringify(module_a_bytes) + ')',
                 'instantiateA'),
             0))
    .then(() => InspectorTest.log('Waiting for wasm script to be parsed.'))
    .then(waitForWasmScript)
    .then(url => (InspectorTest.log('Setting breakpoint in line 1:'), url))
    .then(
        url =>
            Protocol.Debugger.setBreakpointByUrl({lineNumber: 1, url: url}))
    .then(printFailure)
    .then(msg => session.logSourceLocations(msg.result.locations))
    .then(() => InspectorTest.log('Calling instantiate function for module B.'))
    .then(
        () =>
            (evalWithUrl(
                 'instantiate(' + JSON.stringify(module_b_bytes) +
                     ', {imp: {f: instances[0].exports.func}})',
                 'instantiateB'),
             0))
    .then(() => InspectorTest.log('Calling main function on module B.'))
    .then(() => evalWithUrl('instances[1].exports.main()', 'runWasm'))
    .then(() => InspectorTest.log('exports.main returned.'))
    .then(() => InspectorTest.log('Finished.'))
    .then(InspectorTest.completeTest);

// Separate promise chain for the asynchronous pause:
Protocol.Debugger.oncePaused()
    .then(msg => msg.params.callFrames[0].location)
    .then(
        loc =>
            (InspectorTest.log(
                 'Paused at ' + loc.lineNumber + ':' + loc.columnNumber + '.'),
             loc))
    .then(session.logSourceLocation.bind(session))
    .then(
        () => InspectorTest.log(
            'Getting current stack trace via "new Error().stack".'))
    .then(() => evalWithUrl('new Error().stack', 'getStack'))
    .then(msg => InspectorTest.log(msg.result.result.value))
    .then(Protocol.Debugger.resume);

function printFailure(message) {
  if (!message.result) {
    InspectorTest.logMessage(message);
  }
  return message;
}

function waitForWasmScript(msg) {
  if (!msg || !msg.params.url.startsWith('wasm://')) {
    return Protocol.Debugger.onceScriptParsed().then(waitForWasmScript);
  }
  InspectorTest.log('Got wasm script!');
  return Promise.resolve(msg.params.url);
}
