// Copyright 2019 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

(function TestErrorObjectsRetainMap() {
  const error1 = new Error("foo");
  const error2 = new Error("bar");

  assertTrue(%HaveSameMap(error1, error2));

  // Trigger serialization of the stack-trace.
  error1.stack;
  assertTrue(%HaveSameMap(error1, error2));

  error2.stack;
  assertTrue(%HaveSameMap(error1, error2));
})();

(function TestPrepareStackTraceCallback() {
  Error.prepareStackTrace = (error, frame) => {
    return "custom stack trace No. 42";
  };

  const error = new Error("foo");

  // Check it twice, so both code paths in the accessor are exercised.
  assertEquals(error.stack, "custom stack trace No. 42");
  assertEquals(error.stack, "custom stack trace No. 42");
})();

(function TestPrepareStackTraceCallbackMessesWithProperty() {
  Error.prepareStackTrace = (error, frames) => {
    error.stack = "Yes, we can write to this!";
    return 42;
  };

  const error = new Error("foo");

  // Check it twice. The first returns the formatting result,
  // the second the value of the private symbol.
  assertEquals(error.stack, 42);
  assertEquals(error.stack, 42);
})();

(function TestPrepareStackTraceCallbackInstallsGetter() {
  Error.prepareStackTrace = (error, frames) => {
    Object.defineProperty(error, "stack", { get: () => 42 });
    return "<formatted stack trace>";
  };

  const error = new Error("foo");

  // Check it twice. The second time the accessor should be used.
  assertEquals(error.stack, "<formatted stack trace>");
  assertEquals(error.stack, 42);
})();

(function TestPrepareStackTraceCallbackInstallsSetter() {
  Error.prepareStackTrace = (error, frames) => {
    Object.defineProperty(error, "stack", { set: (x) => {
      error[42] = x;
    }});
    return "<formatted stack trace>";
  };

  const error = new Error("foo");
  // Cause the accessor to get installed.
  error.stack;

  error.stack = "Who needs stack traces anyway?";
  assertEquals(error[42], "Who needs stack traces anyway?");
  assertEquals(error.stack, undefined); // No getter.
})();

(function TestFormatStackPropertyInDictionaryMode() {
  Error.prepareStackTrace = (error, frames) => {
    return "<formatted stack trace>";
  };
  const error = new Error("foo");
  error[%MaxSmi()] = 42;

  assertTrue(%HasDictionaryElements(error));

  // Check it twice.
  assertEquals(error.stack, "<formatted stack trace>");
  assertEquals(error.stack, "<formatted stack trace>");
})();

(function TestTransitionToDictionaryModeAfterFormatting() {
  Error.prepareStackTrace = (error, frames) => {
    return "<formatted stack trace>";
  };
  const error = new Error("foo");
  assertFalse(%HasDictionaryElements(error));

  assertEquals(error.stack, "<formatted stack trace>");

  error[%MaxSmi()] = 42;
  assertTrue(%HasDictionaryElements(error));
  assertEquals(error.stack, "<formatted stack trace>");
})();