ffi.js 11.2 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
load("test/mjsunit/wasm/wasm-constants.js");
8
load("test/mjsunit/wasm/wasm-module-builder.js");
9 10

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

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

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

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

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

function FOREIGN_SUB(a, b) {
40
//  print("FOREIGN_SUB(" + a + ", " + b + ")");
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
  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;
}

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

61 62 63 64 65 66 67 68 69 70 71
// 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() {
72
  print(arguments.callee.name);
73 74 75 76
  class C {}
  var builder = new WasmModuleBuilder();

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

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

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

91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
(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);
})();

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

  var builder = new WasmModuleBuilder();

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

121
  var main = builder.instantiate({"": {func: Object.prototype.toString}}).exports.main;
122 123 124 125 126 127 128 129
  // 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);});
130 131

function FOREIGN_ABCD(a, b, c, d) {
132
//  print("FOREIGN_ABCD(" + a + ", " + b + ", " + c + ", " + d + ")");
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 249
  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;

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

  var builder = new WasmModuleBuilder();
259

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

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

  print("testCallBinopVoid", type);

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


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

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

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

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


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

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

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

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

343 344 345 346 347
  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);
348 349 350 351 352 353 354 355

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

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

(function ToNumberCalledOnImport() {
  var builder = new WasmModuleBuilder();
  // Return type is int, so there should be a ToNumber conversion.
373
  var index = builder.addImport("", "func", kSig_i_v);
374 375 376 377 378 379 380
  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();
381
  var main = builder.instantiate({"": {func: func}}).exports.main;
382 383 384 385 386 387 388 389 390
  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.
391
  var index = builder.addImport("", "func", kSig_v_v);
392 393 394 395 396 397 398
  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();
399
  var main = builder.instantiate({"": {func: func}}).exports.main;
400 401 402 403
  main();
  main();
  assertEquals(0, num_valueOf);
})();
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418

(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);
})();