ffi.js 11.1 KB
Newer Older
1 2 3 4
// 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.

5
// Flags: --expose-wasm --allow-natives-syntax
6

7
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
8 9

function testCallFFI(func, check) {
10 11
  var builder = new WasmModuleBuilder();

rossberg's avatar
rossberg committed
12
  var sig_index = builder.addType(kSig_i_dd);
13
  builder.addImport("", "func", sig_index);
14 15
  builder.addFunction("main", sig_index)
    .addBody([
16 17
      kExprLocalGet, 0,            // --
      kExprLocalGet, 1,            // --
18
      kExprCallFunction, 0  // --
19
    ])        // --
20 21
    .exportFunc();

22
  var main = builder.instantiate({"": {func: func}}).exports.main;
23 24 25

  for (var i = 0; i < 100000; i += 10003) {
    var a = 22.5 + i, b = 10.5 + i;
26
    var r = main(a, b);
27 28 29
    if (check) {
      check(r, a, b);
    }
30 31 32 33
  }
}

var global = (function() { return this; })();
34
var params = [-99, -99, -99, -99, -99];
35 36 37 38
var was_called = false;
var length = -1;

function FOREIGN_SUB(a, b) {
39
//  print("FOREIGN_SUB(" + a + ", " + b + ")");
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
  was_called = true;
  params[0] = this;
  params[1] = a;
  params[2] = b;
  return (a - b) | 0;
}

function check_FOREIGN_SUB(r, a, b) {
    assertEquals(a - b | 0, r);
    assertTrue(was_called);
//    assertEquals(global, params[0]);  // sloppy mode
    assertEquals(a, params[1]);
    assertEquals(b, params[2]);
    was_called = false;
}

56 57
// Test calling a normal JSFunction.
print("JSFunction");
58 59
testCallFFI(FOREIGN_SUB, check_FOREIGN_SUB);

60 61 62 63 64 65 66 67 68 69 70
// Test calling a proxy.
print("Proxy");
var proxy_sub = new Proxy(FOREIGN_SUB, {});
testCallFFI(proxy_sub, check_FOREIGN_SUB);

// Test calling a bind function.
print("Bind function");
var bind_sub = FOREIGN_SUB.bind();
testCallFFI(bind_sub, check_FOREIGN_SUB);

(function testCallConstructor() {
71
  print(arguments.callee.name);
72 73 74 75
  class C {}
  var builder = new WasmModuleBuilder();

  var sig_index = builder.addType(kSig_i_dd);
76
  builder.addImport("", "func", sig_index);
77 78
  builder.addFunction("main", sig_index)
    .addBody([
79 80
      kExprLocalGet, 0,            // --
      kExprLocalGet, 1,            // --
81
      kExprCallFunction, 0  // --
82 83 84
    ])        // --
    .exportFunc();

85
  let main_for_constructor_test = builder.instantiate({"": {func: C}}).exports.main;
86

87
  assertThrows(_ => main_for_constructor_test(12, 43), TypeError);
88 89
}) ();

90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
(function testCallConstructorWithSuperClass() {
  print(arguments.callee.name);
  let builder = new WasmModuleBuilder();
  let sig_index = builder.addType(kSig_v_v);
  let func_index = builder.addImport('', 'func', sig_index);
  builder.addExport('exp', func_index);

  class B {}
  class C extends B {
    constructor() {
      super();
    }
  };
  let exports = builder.instantiate({'': {func: C}}).exports;
  assertThrows(_ => exports.exp(), TypeError);
})();

107
(function test_ffi_call_to_native() {
108
  print(arguments.callee.name);
109 110 111

  var builder = new WasmModuleBuilder();

112
  var sig_index = builder.addType(kSig_d_v);
113
  builder.addImport("", "func", sig_index);
114 115
  builder.addFunction("main", sig_index)
    .addBody([
116
      kExprCallFunction, 0  // --
117 118 119
    ])        // --
    .exportFunc();

120
  var main = builder.instantiate({"": {func: Object.prototype.toString}}).exports.main;
121 122 123 124 125 126 127 128
  // The result of the call to Object.prototype.toString should be
  // [object Undefined]. However, we cannot test for this result because wasm
  // cannot return objects but converts them to float64 in this test.
  assertEquals(NaN, main());
})();

print("Callable JSObject");
testCallFFI(%GetCallable(), function check(r, a, b) {assertEquals(a - b, r);});
129 130

function FOREIGN_ABCD(a, b, c, d) {
131
//  print("FOREIGN_ABCD(" + a + ", " + b + ", " + c + ", " + d + ")");
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
  was_called = true;
  params[0] = this;
  params[1] = a;
  params[2] = b;
  params[3] = c;
  params[4] = d;
  return (a * b * 6) | 0;
}

function check_FOREIGN_ABCD(r, a, b) {
    assertEquals((a * b * 6) | 0, r);
    assertTrue(was_called);
//    assertEquals(global, params[0]);  // sloppy mode.
    assertEquals(a, params[1]);
    assertEquals(b, params[2]);
    assertEquals(undefined, params[3]);
    assertEquals(undefined, params[4]);
    was_called = false;
}

testCallFFI(FOREIGN_ABCD, check_FOREIGN_ABCD);

function FOREIGN_ARGUMENTS0() {
  print("FOREIGN_ARGUMENTS0");
  was_called = true;
  length = arguments.length;
  for (var i = 0; i < arguments.length; i++) {
    params[i] = arguments[i];
  }
  return (arguments[0] * arguments[1] * 7) | 0;
}

function FOREIGN_ARGUMENTS1(a) {
  print("FOREIGN_ARGUMENTS1", a);
  was_called = true;
  length = arguments.length;
  for (var i = 0; i < arguments.length; i++) {
    params[i] = arguments[i];
  }
  return (arguments[0] * arguments[1] * 7) | 0;
}

function FOREIGN_ARGUMENTS2(a, b) {
  print("FOREIGN_ARGUMENTS2", a, b);
  was_called = true;
  length = arguments.length;
  for (var i = 0; i < arguments.length; i++) {
    params[i] = arguments[i];
  }
  return (a * b * 7) | 0;
}

function FOREIGN_ARGUMENTS3(a, b, c) {
  print("FOREIGN_ARGUMENTS3", a, b, c);
  was_called = true;
  length = arguments.length;
  for (var i = 0; i < arguments.length; i++) {
    params[i] = arguments[i];
  }
  return (a * b * 7) | 0;
}

function FOREIGN_ARGUMENTS4(a, b, c, d) {
  print("FOREIGN_ARGUMENTS4", a, b, c, d);
  was_called = true;
  length = arguments.length;
  for (var i = 0; i < arguments.length; i++) {
    params[i] = arguments[i];
  }
  return (a * b * 7) | 0;
}

function check_FOREIGN_ARGUMENTS(r, a, b) {
  assertEquals((a * b * 7) | 0, r);
  assertTrue(was_called);
  assertEquals(2, length);
  assertEquals(a, params[0]);
  assertEquals(b, params[1]);
  was_called = false;
}

// Check a bunch of uses of the arguments object.
testCallFFI(FOREIGN_ARGUMENTS0, check_FOREIGN_ARGUMENTS);
testCallFFI(FOREIGN_ARGUMENTS1, check_FOREIGN_ARGUMENTS);
testCallFFI(FOREIGN_ARGUMENTS2, check_FOREIGN_ARGUMENTS);
testCallFFI(FOREIGN_ARGUMENTS3, check_FOREIGN_ARGUMENTS);
testCallFFI(FOREIGN_ARGUMENTS4, check_FOREIGN_ARGUMENTS);

function returnValue(val) {
  return function(a, b) {
    print("RETURN_VALUE ", val);
    return val;
  }
}


function checkReturn(expected) {
  return function(r, a, b) { assertEquals(expected, r); }
}

// Check that returning weird values doesn't crash
testCallFFI(returnValue(undefined), checkReturn(0));
testCallFFI(returnValue(null), checkReturn(0));
testCallFFI(returnValue("0"), checkReturn(0));
testCallFFI(returnValue("-77"), checkReturn(-77));

var objWithValueOf = {valueOf: function() { return 198; }}

testCallFFI(returnValue(objWithValueOf), checkReturn(198));

function testCallBinopVoid(type, func, check) {
  var passed_length = -1;
  var passed_a = -1;
  var passed_b = -1;
  var args_a = -1;
  var args_b = -1;

249
  ffi = {"": {func: function(a, b) {
250 251 252 253 254
    passed_length = arguments.length;
    passed_a = a;
    passed_b = b;
    args_a = arguments[0];
    args_b = arguments[1];
255
  }}};
256 257

  var builder = new WasmModuleBuilder();
258

259
  builder.addImport("", "func", makeSig_v_xx(type));
260
  builder.addFunction("main", makeSig_r_xx(kWasmI32, type))
261
    .addBody([
262 263
      kExprLocalGet, 0,            // --
      kExprLocalGet, 1,            // --
264
      kExprCallFunction, 0,        // --
265
      kExprI32Const, 33            // --
266
    ])                             // --
267 268 269
    .exportFunc()

  var main = builder.instantiate(ffi).exports.main;
270 271 272 273 274

  print("testCallBinopVoid", type);

  for (var i = 0; i < 100000; i += 10003.1) {
    var a = 22.5 + i, b = 10.5 + i;
275
    var r = main(a, b);
276
    assertEquals(33, r);
277 278 279
    assertEquals(2, passed_length);
    var expected_a, expected_b;
    switch (type) {
280
      case kWasmI32: {
281 282 283 284
        expected_a = a | 0;
        expected_b = b | 0;
        break;
      }
285
      case kWasmF32: {
286 287 288 289
        expected_a = Math.fround(a);
        expected_b = Math.fround(b);
        break;
      }
290
      case kWasmF64: {
291 292 293 294 295 296 297 298 299 300 301 302 303 304
        expected_a = a;
        expected_b = b;
        break;
      }
    }

    assertEquals(expected_a, args_a);
    assertEquals(expected_b, args_b);
    assertEquals(expected_a, passed_a);
    assertEquals(expected_b, passed_b);
  }
}


305 306 307 308
testCallBinopVoid(kWasmI32);
// TODO testCallBinopVoid(kWasmI64);
testCallBinopVoid(kWasmF32);
testCallBinopVoid(kWasmF64);
309

310
(function testCallPrint() {
311 312
  var builder = new WasmModuleBuilder();

313 314 315
  builder.addImport("", "print", makeSig_v_x(kWasmI32));
  builder.addImport("", "print", makeSig_v_x(kWasmF64));
  builder.addFunction("main", makeSig_v_x(kWasmF64))
316
    .addBody([
317
      kExprI32Const, 37,     // --
318
      kExprCallFunction, 0,  // --
319
      kExprLocalGet, 0,      // --
320 321
      kExprCallFunction, 1   // --
    ])                       // --
322 323
    .exportFunc()

324
  var main = builder.instantiate({"": {print: print}}).exports.main;
325
  for (var i = -9; i < 900; i += 6.125) main(i);
326 327 328 329
})();


(function testImportNumbers() {
330
  print("TestImportNumbers...");
331 332
  var builder = new WasmModuleBuilder();

333
  builder.addImport("", '0', kSig_v_i);
334

335
  builder.instantiate({"": {0: print}});
336 337 338
})();

(function testImportNumbers2() {
339
  print("TestImportNumbers2...");
340 341
  var builder = new WasmModuleBuilder();

342 343 344 345 346
  builder.addImport('foo', '0', kSig_v_i);
  builder.addImport('0', 'foo', kSig_v_i);
  builder.addImport('0', '0', kSig_v_i);
  builder.addImport('18', '-3', kSig_v_i);
  builder.addImport('-3', '18', kSig_v_i);
347 348 349 350 351 352 353 354

  builder.instantiate({
    foo: {0: print},
    0: {0: print, foo: print},
    18: {'-3': print},
    '-3': {18: print}
  });
})();
355 356

(function ImportSymbolAsVoidDoesNotThrow() {
357
  print("ImportSymbolAsVoidDoesNotThrow...");
358 359
  var builder = new WasmModuleBuilder();
  // Return type is void, so there should be no ToNumber conversion.
360
  var index = builder.addImport("", "func", kSig_v_v);
361 362 363 364
  builder.addFunction("main", kSig_v_v)
      .addBody([kExprCallFunction, 0])
      .exportFunc();
  var func = () => Symbol();
365
  var main = builder.instantiate({"": {func: func}}).exports.main;
366 367 368 369 370 371
  main();
})();

(function ToNumberCalledOnImport() {
  var builder = new WasmModuleBuilder();
  // Return type is int, so there should be a ToNumber conversion.
372
  var index = builder.addImport("", "func", kSig_i_v);
373 374 375 376 377 378 379
  builder.addFunction("main", kSig_i_v)
      .addBody([kExprCallFunction, 0])
      .exportFunc();
  var num_valueOf = 0;
  function Foo() {}
  Foo.prototype.valueOf = () => ++num_valueOf;
  var func = () => new Foo();
380
  var main = builder.instantiate({"": {func: func}}).exports.main;
381 382 383 384 385 386 387 388 389
  main();
  assertEquals(1, num_valueOf);
  main();
  assertEquals(2, num_valueOf);
})();

(function ToNumberNotCalledOnVoidImport() {
  var builder = new WasmModuleBuilder();
  // Return type is void, so there should be no ToNumber conversion.
390
  var index = builder.addImport("", "func", kSig_v_v);
391 392 393 394 395 396 397
  builder.addFunction("main", kSig_v_v)
      .addBody([kExprCallFunction, 0])
      .exportFunc();
  var num_valueOf = 0;
  function Foo() {}
  Foo.prototype.valueOf = () => ++num_valueOf;
  var func = () => new Foo();
398
  var main = builder.instantiate({"": {func: func}}).exports.main;
399 400 401 402
  main();
  main();
  assertEquals(0, num_valueOf);
})();
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417

(function ImportWithCustomGetter() {
  print(arguments.callee.name);
  const builder = new WasmModuleBuilder();
  builder.addImport("import", "func", kSig_v_v);

  const ffi = {};
  Object.defineProperty(ffi, 'import', {
    get: _ => {
      return {func: () => null };
    }
  });

  builder.instantiate(ffi);
})();