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

// Flags: --allow-natives-syntax --correctness-fuzzer-suppressions
// Files: tools/clusterfuzz/foozzie/v8_mock.js

// Test foozzie mocks for differential fuzzing.

// Deterministic Math.random.
assertEquals(0.7098480789645691, Math.random());
assertEquals(0.9742682568175951, Math.random());
assertEquals(0.20008059867222983, Math.random());

// Deterministic date.
assertEquals(1477662728698, Date.now());
assertEquals(1477662728701, Date.now());
assertEquals(1477662728705, new Date().getTime());
assertEquals(710, new Date.prototype.constructor().getUTCMilliseconds());

// Deterministic arguments in constructor keep working.
assertEquals(819134640000,
             new Date('December 17, 1995 03:24:00 GMT+1000').getTime());

// Deterministic DateTimeFormat.
if (this.Intl) {
  const df = new Intl.DateTimeFormat(undefined, {fractionalSecondDigits: 3});
  assertEquals('004', df.format());
  assertEquals('004', df.formatToParts()[0].value);
}

// Dummy performance methods.
assertEquals(1.2, performance.now());
assertEquals(undefined, performance.mark("a mark"));
assertEquals(undefined, performance.measure("a measure"));
assertEquals([], performance.measureMemory());

// Worker messages follow a predefined deterministic pattern.
const worker = new Worker(``, {type: 'string'});
assertEquals(0, worker.getMessage());
assertEquals(-1, worker.getMessage());

// NaN patterns in typed arrays are mocked out. Test that we get no
// difference between unoptimized and optimized code.
function testSameOptimized(pattern, create_fun) {
  const expected = new Uint32Array(pattern);
  %PrepareFunctionForOptimization(create_fun);
  assertEquals(expected, create_fun());
  %OptimizeFunctionOnNextCall(create_fun);
  assertEquals(expected, create_fun());
}

function testArrayType(arrayType, pattern) {
  // Test passing NaNs to constructor with array.
  let create = function() {
    return new Uint32Array(new arrayType([-NaN]).buffer);
  };
  testSameOptimized(pattern, create);
  // Test passing NaNs to constructor with iterator.
  create = function() {
    const iter = function*(){ yield* [-NaN]; }();
    return new Uint32Array(new arrayType(iter).buffer);
  };
  testSameOptimized(pattern, create);
  // Test setting NaN property.
  create = function() {
    const arr = new arrayType(1);
    arr[0] = -NaN;
    return new Uint32Array(arr.buffer);
  };
  testSameOptimized(pattern, create);
  // Test passing NaN using set.
  create = function() {
    const arr = new arrayType(1);
    arr.set([-NaN], 0);
    return new Uint32Array(arr.buffer);
  };
  testSameOptimized(pattern, create);
}

var isBigEndian = new Uint8Array(new Uint16Array([0xABCD]).buffer)[0] === 0xAB;
testArrayType(Float32Array, [1065353216]);
if (isBigEndian){
  testArrayType(Float64Array, [1072693248, 0]);
}
else {
  testArrayType(Float64Array, [0, 1072693248]);
}

// Test that DataView has the same NaN patterns with optimized and
// unoptimized code.
var expected_array = [4213246272,405619796,61503,0,3675212096,32831];
if (isBigEndian){
  expected_array = [1074340347,1413754136,1072693248,0,1078530011,1065353216];
}
testSameOptimized(expected_array, () => {
  const array = new Uint32Array(6);
  const view = new DataView(array.buffer);
  view.setFloat64(0, Math.PI);
  view.setFloat64(8, -undefined);
  view.setFloat32(16, Math.fround(Math.PI));
  view.setFloat32(20, -undefined);
  assertEquals(Math.PI, view.getFloat64(0));
  assertEquals(Math.fround(Math.PI), view.getFloat32(16));
  return array;
});

// Realm.eval is just eval.
assertEquals(1477662728716, Realm.eval(Realm.create(), `Date.now()`));

// Test suppressions when Math.pow is optimized.
function callPow(v) {
  return Math.pow(v, -0.5);
}
%PrepareFunctionForOptimization(callPow);
const unoptimized = callPow(6996);
%OptimizeFunctionOnNextCall(callPow);
assertEquals(unoptimized, callPow(6996));

// Test mocked Atomics.waitAsync.
let then_called = false;
Atomics.waitAsync().value.then(() => {then_called = true;});
assertEquals(true, then_called);

// Test .caller access is neutralized.
function callee() {
  assertEquals(null, callee.caller);
}
function caller() {
  callee();
}
caller();

// Neutralized serializer API.
let object = {'foo': 42}
assertEquals(d8.serializer.serialize(object), object)
assertEquals(d8.serializer.deserialize(object), object)
