ffi.js 10.4 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 72 73 74 75 76 77
// 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);

var main_for_constructor_test;
print("Constructor");
(function testCallConstructor() {
  class C {}
  var builder = new WasmModuleBuilder();

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

87
  main_for_constructor_test = builder.instantiate({"": {func: C}}).exports.main;
88 89 90 91 92 93 94 95 96

  assertThrows("main_for_constructor_test(12, 43)", TypeError);
}) ();

print("Native function");
(function test_ffi_call_to_native() {

  var builder = new WasmModuleBuilder();

97
  var sig_index = builder.addType(kSig_d_v);
98
  builder.addImport("", "func", sig_index);
99 100
  builder.addFunction("main", sig_index)
    .addBody([
101
      kExprCallFunction, 0  // --
102 103 104
    ])        // --
    .exportFunc();

105
  var main = builder.instantiate({"": {func: Object.prototype.toString}}).exports.main;
106 107 108 109 110 111 112 113
  // 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);});
114 115

function FOREIGN_ABCD(a, b, c, d) {
116
//  print("FOREIGN_ABCD(" + a + ", " + b + ", " + c + ", " + d + ")");
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 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
  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;

234
  ffi = {"": {func: function(a, b) {
235 236 237 238 239
    passed_length = arguments.length;
    passed_a = a;
    passed_b = b;
    args_a = arguments[0];
    args_b = arguments[1];
240
  }}};
241 242

  var builder = new WasmModuleBuilder();
243

244
  builder.addImport("", "func", makeSig_v_xx(type));
245
  builder.addFunction("main", makeSig_r_xx(kWasmI32, type))
246
    .addBody([
247 248
      kExprGetLocal, 0,            // --
      kExprGetLocal, 1,            // --
249
      kExprCallFunction, 0,        // --
250
      kExprI32Const, 33            // --
251
    ])                             // --
252 253 254
    .exportFunc()

  var main = builder.instantiate(ffi).exports.main;
255 256 257 258 259

  print("testCallBinopVoid", type);

  for (var i = 0; i < 100000; i += 10003.1) {
    var a = 22.5 + i, b = 10.5 + i;
260
    var r = main(a, b);
261
    assertEquals(33, r);
262 263 264
    assertEquals(2, passed_length);
    var expected_a, expected_b;
    switch (type) {
265
      case kWasmI32: {
266 267 268 269
        expected_a = a | 0;
        expected_b = b | 0;
        break;
      }
270
      case kWasmF32: {
271 272 273 274
        expected_a = Math.fround(a);
        expected_b = Math.fround(b);
        break;
      }
275
      case kWasmF64: {
276 277 278 279 280 281 282 283 284 285 286 287 288 289
        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);
  }
}


290 291 292 293
testCallBinopVoid(kWasmI32);
// TODO testCallBinopVoid(kWasmI64);
testCallBinopVoid(kWasmF32);
testCallBinopVoid(kWasmF64);
294

295
(function testCallPrint() {
296 297
  var builder = new WasmModuleBuilder();

298 299 300
  builder.addImport("", "print", makeSig_v_x(kWasmI32));
  builder.addImport("", "print", makeSig_v_x(kWasmF64));
  builder.addFunction("main", makeSig_v_x(kWasmF64))
301
    .addBody([
302
      kExprI32Const, 37,     // --
303 304 305 306
      kExprCallFunction, 0,  // --
      kExprGetLocal, 0,      // --
      kExprCallFunction, 1   // --
    ])                       // --
307 308
    .exportFunc()

309
  var main = builder.instantiate({"": {print: print}}).exports.main;
310
  for (var i = -9; i < 900; i += 6.125) main(i);
311 312 313 314
})();


(function testImportNumbers() {
315
  print("TestImportNumbers...");
316 317
  var builder = new WasmModuleBuilder();

318
  builder.addImport("", '0', kSig_v_i);
319

320
  builder.instantiate({"": {0: print}});
321 322 323
})();

(function testImportNumbers2() {
324
  print("TestImportNumbers2...");
325 326
  var builder = new WasmModuleBuilder();

327 328 329 330 331
  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);
332 333 334 335 336 337 338 339

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

(function ImportSymbolAsVoidDoesNotThrow() {
342
  print("ImportSymbolAsVoidDoesNotThrow...");
343 344
  var builder = new WasmModuleBuilder();
  // Return type is void, so there should be no ToNumber conversion.
345
  var index = builder.addImport("", "func", kSig_v_v);
346 347 348 349
  builder.addFunction("main", kSig_v_v)
      .addBody([kExprCallFunction, 0])
      .exportFunc();
  var func = () => Symbol();
350
  var main = builder.instantiate({"": {func: func}}).exports.main;
351 352 353 354 355 356
  main();
})();

(function ToNumberCalledOnImport() {
  var builder = new WasmModuleBuilder();
  // Return type is int, so there should be a ToNumber conversion.
357
  var index = builder.addImport("", "func", kSig_i_v);
358 359 360 361 362 363 364
  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();
365
  var main = builder.instantiate({"": {func: func}}).exports.main;
366 367 368 369 370 371 372 373 374
  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.
375
  var index = builder.addImport("", "func", kSig_v_v);
376 377 378 379 380 381 382
  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();
383
  var main = builder.instantiate({"": {func: func}}).exports.main;
384 385 386 387
  main();
  main();
  assertEquals(0, num_valueOf);
})();