1// Copyright 2015 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Flags: --harmony-simd --harmony-tostring  --harmony-reflect
6// Flags: --allow-natives-syntax --expose-natives-as natives --noalways-opt
7
8function lanesForType(typeName) {
9  // The lane count follows the first 'x' in the type name, which begins with
10  // 'float', 'int', or 'bool'.
11  return Number.parseInt(typeName.substr(typeName.indexOf('x') + 1));
12}
13
14
15// Creates an instance that has been zeroed, so it can be used for equality
16// testing.
17function createInstance(type) {
18  // Provide enough parameters for the longest type (currently 16). It's
19  // important that instances be consistent to better test that different SIMD
20  // types can't be compared and are never equal or the same in any sense.
21  return SIMD[type](0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
22}
23
24
25function isValidSimdString(string, value, type, lanes) {
26  var simdFn = SIMD[type],
27      parseFn =
28          type.indexOf('Float') === 0 ? Number.parseFloat : Number.parseInt,
29      indexOfOpenParen = string.indexOf('(');
30  // Check prefix (e.g. SIMD.Float32x4.)
31  if (string.substr(0, indexOfOpenParen) !== 'SIMD.' + type)
32    return false;
33  // Remove type name (e.g. SIMD.Float32x4) and open parenthesis.
34  string = string.substr(indexOfOpenParen + 1);
35  var laneStrings = string.split(',');
36  if (laneStrings.length !== lanes)
37    return false;
38  for (var i = 0; i < lanes; i++) {
39    var fromString = parseFn(laneStrings[i]),
40        fromValue = simdFn.extractLane(value, i);
41    if (Math.abs(fromString - fromValue) > Number.EPSILON)
42      return false;
43  }
44  return true;
45}
46
47
48var simdTypeNames = ['Float32x4', 'Int32x4', 'Uint32x4', 'Bool32x4',
49                                  'Int16x8', 'Uint16x8', 'Bool16x8',
50                                  'Int8x16', 'Uint8x16', 'Bool8x16'];
51
52var nonSimdValues = [347, 1.275, NaN, "string", null, undefined, {},
53                     function() {}];
54
55function checkTypeMatrix(type, fn) {
56  // Check against non-SIMD types.
57  nonSimdValues.forEach(fn);
58  // Check against SIMD values of a different type.
59  for (var i = 0; i < simdTypeNames.length; i++) {
60    var otherType = simdTypeNames[i];
61    if (type != otherType) fn(createInstance(otherType));
62  }
63}
64
65
66// Test different forms of constructor calls.
67function TestConstructor(type, lanes) {
68  var simdFn = SIMD[type];
69  var instance = createInstance(type);
70
71  assertFalse(Object === simdFn.prototype.constructor)
72  assertFalse(simdFn === Object.prototype.constructor)
73  assertSame(simdFn, simdFn.prototype.constructor)
74
75  assertSame(simdFn, instance.__proto__.constructor)
76  assertSame(simdFn, Object(instance).__proto__.constructor)
77  assertSame(simdFn.prototype, instance.__proto__)
78  assertSame(simdFn.prototype, Object(instance).__proto__)
79}
80
81
82function TestType(type, lanes) {
83  var simdFn = SIMD[type];
84  var instance = createInstance(type);
85  var typeofString = type.charAt(0).toLowerCase() + type.slice(1);
86
87  assertEquals(typeofString, typeof instance)
88  assertTrue(typeof instance === typeofString)
89  assertTrue(typeof Object(instance) === 'object')
90  assertEquals(null, %_ClassOf(instance))
91  assertEquals(type, %_ClassOf(Object(instance)))
92}
93
94
95function TestPrototype(type, lanes) {
96  var simdFn = SIMD[type];
97  var instance = createInstance(type);
98
99  assertSame(Object.prototype, simdFn.prototype.__proto__)
100  assertSame(simdFn.prototype, instance.__proto__)
101  assertSame(simdFn.prototype, Object(instance).__proto__)
102}
103
104
105function TestValueOf(type, lanes) {
106  var simdFn = SIMD[type];
107  var instance = createInstance(type);
108
109  assertTrue(instance === Object(instance).valueOf())
110  assertTrue(instance === instance.valueOf())
111  assertTrue(simdFn.prototype.valueOf.call(Object(instance)) === instance)
112  assertTrue(simdFn.prototype.valueOf.call(instance) === instance)
113}
114
115
116function TestGet(type, lanes) {
117  var simdFn = SIMD[type];
118  var instance = createInstance(type);
119
120  assertEquals(undefined, instance.a)
121  assertEquals(undefined, instance["a" + "b"])
122  assertEquals(undefined, instance["" + "1"])
123  assertEquals(undefined, instance[42])
124}
125
126
127function TestToBoolean(type, lanes) {
128  var simdFn = SIMD[type];
129  var instance = createInstance(type);
130
131  assertTrue(Boolean(Object(instance)))
132  assertFalse(!Object(instance))
133  assertTrue(Boolean(instance).valueOf())
134  assertFalse(!instance)
135  assertTrue(!!instance)
136  assertTrue(instance && true)
137  assertFalse(!instance && false)
138  assertTrue(!instance || true)
139  assertEquals(1, instance ? 1 : 2)
140  assertEquals(2, !instance ? 1 : 2)
141  if (!instance) assertUnreachable();
142  if (instance) {} else assertUnreachable();
143}
144
145
146function TestToString(type, lanes) {
147  var simdFn = SIMD[type];
148  var instance = createInstance(type);
149
150  assertEquals(instance.toString(), String(instance))
151  assertTrue(isValidSimdString(instance.toString(), instance, type, lanes))
152  assertTrue(
153      isValidSimdString(Object(instance).toString(), instance, type, lanes))
154  assertTrue(isValidSimdString(
155      simdFn.prototype.toString.call(instance), instance, type, lanes))
156}
157
158
159function TestToNumber(type, lanes) {
160  var simdFn = SIMD[type];
161  var instance = createInstance(type);
162
163  assertThrows(function() { Number(Object(instance)) }, TypeError)
164  assertThrows(function() { +Object(instance) }, TypeError)
165  assertThrows(function() { Number(instance) }, TypeError)
166  assertThrows(function() { instance + 0 }, TypeError)
167}
168
169
170function TestCoercions(type, lanes) {
171  var simdFn = SIMD[type];
172  var instance = createInstance(type);
173  // Test that setting a lane to value 'a' results in a lane with value 'b'.
174  function test(a, b) {
175    for (var i = 0; i < lanes; i++) {
176      var ainstance = simdFn.replaceLane(instance, i, a);
177      var lane_value = simdFn.extractLane(ainstance, i);
178      assertSame(b, lane_value);
179    }
180  }
181
182  switch (type) {
183    case 'Float32x4':
184      test(0, 0);
185      test(-0, -0);
186      test(NaN, NaN);
187      test(null, 0);
188      test(undefined, NaN);
189      test("5.25", 5.25);
190      test(Number.MAX_VALUE, Infinity);
191      test(-Number.MAX_VALUE, -Infinity);
192      test(Number.MIN_VALUE, 0);
193      break;
194    case 'Int32x4':
195      test(Infinity, 0);
196      test(-Infinity, 0);
197      test(NaN, 0);
198      test(0, 0);
199      test(-0, 0);
200      test(Number.MIN_VALUE, 0);
201      test(-Number.MIN_VALUE, 0);
202      test(0.1, 0);
203      test(-0.1, 0);
204      test(1, 1);
205      test(1.1, 1);
206      test(-1, -1);
207      test(-1.6, -1);
208      test(2147483647, 2147483647);
209      test(2147483648, -2147483648);
210      test(2147483649, -2147483647);
211      test(4294967295, -1);
212      test(4294967296, 0);
213      test(4294967297, 1);
214      break;
215    case 'Uint32x4':
216      test(Infinity, 0);
217      test(-Infinity, 0);
218      test(NaN, 0);
219      test(0, 0);
220      test(-0, 0);
221      test(Number.MIN_VALUE, 0);
222      test(-Number.MIN_VALUE, 0);
223      test(0.1, 0);
224      test(-0.1, 0);
225      test(1, 1);
226      test(1.1, 1);
227      test(-1, 4294967295);
228      test(-1.6, 4294967295);
229      test(4294967295, 4294967295);
230      test(4294967296, 0);
231      test(4294967297, 1);
232      break;
233    case 'Int16x8':
234      test(Infinity, 0);
235      test(-Infinity, 0);
236      test(NaN, 0);
237      test(0, 0);
238      test(-0, 0);
239      test(Number.MIN_VALUE, 0);
240      test(-Number.MIN_VALUE, 0);
241      test(0.1, 0);
242      test(-0.1, 0);
243      test(1, 1);
244      test(1.1, 1);
245      test(-1, -1);
246      test(-1.6, -1);
247      test(32767, 32767);
248      test(32768, -32768);
249      test(32769, -32767);
250      test(65535, -1);
251      test(65536, 0);
252      test(65537, 1);
253      break;
254    case 'Uint16x8':
255      test(Infinity, 0);
256      test(-Infinity, 0);
257      test(NaN, 0);
258      test(0, 0);
259      test(-0, 0);
260      test(Number.MIN_VALUE, 0);
261      test(-Number.MIN_VALUE, 0);
262      test(0.1, 0);
263      test(-0.1, 0);
264      test(1, 1);
265      test(1.1, 1);
266      test(-1, 65535);
267      test(-1.6, 65535);
268      test(65535, 65535);
269      test(65536, 0);
270      test(65537, 1);
271      break;
272    case 'Int8x16':
273      test(Infinity, 0);
274      test(-Infinity, 0);
275      test(NaN, 0);
276      test(0, 0);
277      test(-0, 0);
278      test(Number.MIN_VALUE, 0);
279      test(-Number.MIN_VALUE, 0);
280      test(0.1, 0);
281      test(-0.1, 0);
282      test(1, 1);
283      test(1.1, 1);
284      test(-1, -1);
285      test(-1.6, -1);
286      test(127, 127);
287      test(128, -128);
288      test(129, -127);
289      test(255, -1);
290      test(256, 0);
291      test(257, 1);
292      break;
293    case 'Uint8x16':
294      test(Infinity, 0);
295      test(-Infinity, 0);
296      test(NaN, 0);
297      test(0, 0);
298      test(-0, 0);
299      test(Number.MIN_VALUE, 0);
300      test(-Number.MIN_VALUE, 0);
301      test(0.1, 0);
302      test(-0.1, 0);
303      test(1, 1);
304      test(1.1, 1);
305      test(-1, 255);
306      test(-1.6, 255);
307      test(255, 255);
308      test(256, 0);
309      test(257, 1);
310      break;
311    case 'Bool32x4':
312    case 'Bool16x8':
313    case 'Bool8x16':
314      test(true, true);
315      test(false, false);
316      test(0, false);
317      test(1, true);
318      test(0.1, true);
319      test(NaN, false);
320      test(null, false);
321      test("", false);
322      test("false", true);
323      break;
324  }
325}
326
327
328function TestEquality(type, lanes) {
329  var simdFn = SIMD[type];
330  var instance = createInstance(type);
331
332  // Every SIMD value should equal itself, and non-strictly equal its wrapper.
333  assertSame(instance, instance)
334  assertEquals(instance, instance)
335  assertTrue(Object.is(instance, instance))
336  assertTrue(instance === instance)
337  assertTrue(instance == instance)
338  assertFalse(instance === Object(instance))
339  assertFalse(Object(instance) === instance)
340  assertTrue(instance == Object(instance))
341  assertTrue(Object(instance) == instance)
342  assertTrue(instance === instance.valueOf())
343  assertTrue(instance.valueOf() === instance)
344  assertTrue(instance == instance.valueOf())
345  assertTrue(instance.valueOf() == instance)
346  assertFalse(Object(instance) === Object(instance))
347  assertEquals(Object(instance).valueOf(), Object(instance).valueOf())
348
349  function notEqual(other) {
350    assertFalse(instance === other)
351    assertFalse(other === instance)
352    assertFalse(instance == other)
353    assertFalse(other == instance)
354  }
355
356  // SIMD values should not be equal to instances of different types.
357  checkTypeMatrix(type, function(other) {
358    assertFalse(instance === other)
359    assertFalse(other === instance)
360    assertFalse(instance == other)
361    assertFalse(other == instance)
362  });
363
364  // Test that f(a, b) is the same as f(SIMD(a), SIMD(b)) for equality and
365  // strict equality, at every lane.
366  function test(a, b) {
367    for (var i = 0; i < lanes; i++) {
368      var aval = simdFn.replaceLane(instance, i, a);
369      var bval = simdFn.replaceLane(instance, i, b);
370      assertSame(a == b, aval == bval);
371      assertSame(a === b, aval === bval);
372    }
373  }
374
375  switch (type) {
376    case 'Float32x4':
377      test(1, 2.5);
378      test(1, 1);
379      test(0, 0);
380      test(-0, +0);
381      test(+0, -0);
382      test(-0, -0);
383      test(0, NaN);
384      test(NaN, NaN);
385      break;
386    case 'Int32x4':
387    case 'Uint32x4':
388    case 'Int16x8':
389    case 'Uint16x8':
390    case 'Int8x16':
391    case 'Uint8x16':
392      test(1, 2);
393      test(1, 1);
394      test(1, -1);
395      break;
396    case 'Bool32x4':
397    case 'Bool16x8':
398    case 'Bool8x16':
399      test(true, false);
400      test(false, true);
401      break;
402  }
403}
404
405
406function TestSameValue(type, lanes) {
407  var simdFn = SIMD[type];
408  var instance = createInstance(type);
409  var sameValue = Object.is
410  var sameValueZero = natives.ImportNow("SameValueZero");
411
412  // SIMD values should not be the same as instances of different types.
413  checkTypeMatrix(type, function(other) {
414    assertFalse(sameValue(instance, other));
415    assertFalse(sameValueZero(instance, other));
416  });
417
418  // Test that f(a, b) is the same as f(SIMD(a), SIMD(b)) for sameValue and
419  // sameValueZero, at every lane.
420  function test(a, b) {
421    for (var i = 0; i < lanes; i++) {
422      var aval = simdFn.replaceLane(instance, i, a);
423      var bval = simdFn.replaceLane(instance, i, b);
424      assertSame(sameValue(a, b), sameValue(aval, bval));
425      assertSame(sameValueZero(a, b), sameValueZero(aval, bval));
426    }
427  }
428
429  switch (type) {
430    case 'Float32x4':
431      test(1, 2.5);
432      test(1, 1);
433      test(0, 0);
434      test(-0, +0);
435      test(+0, -0);
436      test(-0, -0);
437      test(0, NaN);
438      test(NaN, NaN);
439      break;
440    case 'Int32x4':
441    case 'Uint32x4':
442    case 'Int16x8':
443    case 'Uint16x8':
444    case 'Int8x16':
445    case 'Uint8x16':
446      test(1, 2);
447      test(1, 1);
448      test(1, -1);
449      break;
450    case 'Bool32x4':
451    case 'Bool16x8':
452    case 'Bool8x16':
453      test(true, false);
454      test(false, true);
455      break;
456  }
457}
458
459
460function TestComparison(type, lanes) {
461  var simdFn = SIMD[type];
462  var a = createInstance(type), b = createInstance(type);
463
464  function compare(other) {
465    var throwFuncs = [
466      function lt() { a < b; },
467      function gt() { a > b; },
468      function le() { a <= b; },
469      function ge() { a >= b; },
470      function lt_same() { a < a; },
471      function gt_same() { a > a; },
472      function le_same() { a <= a; },
473      function ge_same() { a >= a; },
474    ];
475
476    for (var f of throwFuncs) {
477      assertThrows(f, TypeError);
478      %OptimizeFunctionOnNextCall(f);
479      assertThrows(f, TypeError);
480      assertThrows(f, TypeError);
481    }
482  }
483
484  // Test comparison against the same SIMD type.
485  compare(b);
486  // Test comparison against other types.
487  checkTypeMatrix(type, compare);
488}
489
490
491// Test SIMD value wrapping/boxing over non-builtins.
492function TestCall(type, lanes) {
493  var simdFn = SIMD[type];
494  var instance = createInstance(type);
495  simdFn.prototype.getThisProto = function () {
496    return Object.getPrototypeOf(this);
497  }
498  assertTrue(instance.getThisProto() === simdFn.prototype)
499}
500
501
502function TestAsSetKey(type, lanes, set) {
503  var simdFn = SIMD[type];
504  var instance = createInstance(type);
505
506  function test(set, key) {
507    assertFalse(set.has(key));
508    assertFalse(set.delete(key));
509    if (!(set instanceof WeakSet)) {
510      assertSame(set, set.add(key));
511      assertTrue(set.has(key));
512      assertTrue(set.delete(key));
513    } else {
514      // SIMD values can't be used as keys in WeakSets.
515      assertThrows(function() { set.add(key) });
516    }
517    assertFalse(set.has(key));
518    assertFalse(set.delete(key));
519    assertFalse(set.has(key));
520  }
521
522  test(set, instance);
523}
524
525
526function TestAsMapKey(type, lanes, map) {
527  var simdFn = SIMD[type];
528  var instance = createInstance(type);
529
530  function test(map, key, value) {
531    assertFalse(map.has(key));
532    assertSame(undefined, map.get(key));
533    assertFalse(map.delete(key));
534    if (!(map instanceof WeakMap)) {
535      assertSame(map, map.set(key, value));
536      assertSame(value, map.get(key));
537      assertTrue(map.has(key));
538      assertTrue(map.delete(key));
539    } else {
540      // SIMD values can't be used as keys in WeakMaps.
541      assertThrows(function() { map.set(key, value) });
542    }
543    assertFalse(map.has(key));
544    assertSame(undefined, map.get(key));
545    assertFalse(map.delete(key));
546    assertFalse(map.has(key));
547    assertSame(undefined, map.get(key));
548  }
549
550  test(map, instance, {});
551}
552
553
554// Test SIMD type with Harmony reflect-apply.
555function TestReflectApply(type) {
556  var simdFn = SIMD[type];
557  var instance = createInstance(type);
558
559  function returnThis() { return this; }
560  function returnThisStrict() { 'use strict'; return this; }
561  function noop() {}
562  function noopStrict() { 'use strict'; }
563  var R = void 0;
564
565  assertSame(SIMD[type].prototype,
566             Object.getPrototypeOf(
567                Reflect.apply(returnThis, instance, [])));
568  assertSame(instance, Reflect.apply(returnThisStrict, instance, []));
569
570  assertThrows(
571      function() { 'use strict'; Reflect.apply(instance); }, TypeError);
572  assertThrows(
573      function() { Reflect.apply(instance); }, TypeError);
574  assertThrows(
575      function() { Reflect.apply(noopStrict, R, instance); }, TypeError);
576  assertThrows(
577      function() { Reflect.apply(noop, R, instance); }, TypeError);
578}
579
580
581function TestSIMDTypes() {
582  for (var i = 0; i < simdTypeNames.length; ++i) {
583    var type = simdTypeNames[i],
584        lanes = lanesForType(type);
585    TestConstructor(type, lanes);
586    TestType(type, lanes);
587    TestPrototype(type, lanes);
588    TestValueOf(type, lanes);
589    TestGet(type, lanes);
590    TestToBoolean(type, lanes);
591    TestToString(type, lanes);
592    TestToNumber(type, lanes);
593    TestCoercions(type, lanes);
594    TestEquality(type, lanes);
595    TestSameValue(type, lanes);
596    TestComparison(type, lanes);
597    TestCall(type, lanes);
598    TestAsSetKey(type, lanes, new Set);
599    TestAsSetKey(type, lanes, new WeakSet);
600    TestAsMapKey(type, lanes, new Map);
601    TestAsMapKey(type, lanes, new WeakMap);
602    TestReflectApply(type);
603  }
604}
605TestSIMDTypes();
606
607// Tests for the global SIMD object.
608function TestSIMDObject() {
609  assertSame(typeof SIMD, 'object');
610  assertSame(SIMD.constructor, Object);
611  assertSame(Object.getPrototypeOf(SIMD), Object.prototype);
612  assertSame(SIMD + "", "[object SIMD]");
613  // The SIMD object is mutable.
614  SIMD.foo = "foo";
615  assertSame(SIMD.foo, "foo");
616  delete SIMD.foo;
617  delete SIMD.Bool8x16;
618  assertSame(SIMD.Bool8x16, undefined);
619}
620TestSIMDObject()
621
622
623function TestStringify(expected, input) {
624  assertEquals(expected, JSON.stringify(input));
625  assertEquals(expected, JSON.stringify(input, null, 0));
626}
627
628TestStringify(undefined, SIMD.Float32x4(1, 2, 3, 4));
629TestStringify('[null]', [SIMD.Float32x4(1, 2, 3, 4)]);
630TestStringify('[{}]', [Object(SIMD.Float32x4(1, 2, 3, 4))]);
631var simd_wrapper = Object(SIMD.Float32x4(1, 2, 3, 4));
632TestStringify('{}', simd_wrapper);
633simd_wrapper.a = 1;
634TestStringify('{"a":1}', simd_wrapper);
635