// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

// This test is a derivative of the Splay benchmark that is run with a variety
// of different GC options. It makes for a good GC stress test because it
// continuously makes small changes to a large, long-lived data structure,
// stressing lots of combinations of references between new-gen and old-gen
// objects, and between marked and unmarked objects.

// VMOptions=
// VMOptions=--no_concurrent_mark --no_concurrent_sweep
// VMOptions=--no_concurrent_mark --concurrent_sweep
// VMOptions=--no_concurrent_mark --use_compactor
// VMOptions=--no_concurrent_mark --use_compactor --force_evacuation
// VMOptions=--concurrent_mark --no_concurrent_sweep
// VMOptions=--concurrent_mark --concurrent_sweep
// VMOptions=--concurrent_mark --use_compactor
// VMOptions=--concurrent_mark --use_compactor --force_evacuation
// VMOptions=--scavenger_tasks=-1
// VMOptions=--scavenger_tasks=1
// VMOptions=--scavenger_tasks=2
// VMOptions=--verify_before_gc
// VMOptions=--verify_after_gc
// VMOptions=--verify_before_gc --verify_after_gc
// VMOptions=--verify_store_buffer
// VMOptions=--verify_after_marking
// VMOptions=--runtime_allocate_old
// VMOptions=--runtime_allocate_spill_tlab
// VMOptions=--no_inline_alloc
// VMOptions=--old_gen_heap_size=150

import "splay_common.dart";

void main() {
  WeakSplay().main();
}

class WeakSplay extends Splay {
  newPayload(int depth, String tag) => Payload.generate(depth, tag);
  Node newNode(num key, Object? value) => new WeakNode(key, value);
}

class Payload {
  Payload(left, right) {
    this.left = left;
    this.right = right;
  }

  // This ordering of fields is deliberate: one strong reference visited before
  // the weak reference and one after.
  var leftWeak;
  @pragma("vm:entry-point") // TODO(50571): Remove illegal optimization.
  var leftStrong;
  @pragma("vm:entry-point") // TODO(50571): Remove illegal optimization.
  var rightStrong;
  var rightWeak;

  get left => leftWeak?.target;
  set left(value) {
    leftWeak = new WeakReference(value as Object);
    // Indirection: chance for WeakRef to be scanned before target is marked.
    leftStrong = [
      [value],
    ];
  }

  get right => rightWeak?.target;
  set right(value) {
    rightWeak = new WeakReference(value as Object);
    // Indirection: chance for WeakRef to be scanned before target is marked.
    rightStrong = [
      [value],
    ];
  }

  static generate(depth, tag) {
    if (depth == 0) return new Leaf(tag);
    return new Payload(generate(depth - 1, tag), generate(depth - 1, tag));
  }
}

class WeakNode extends Node {
  WeakNode(num key, Object? value) : super(key, value);

  // This ordering of fields is deliberate: one strong reference visited before
  // the weak reference and one after.
  var leftWeak;
  @pragma("vm:entry-point") // TODO(50571): Remove illegal optimization.
  var leftStrong;
  @pragma("vm:entry-point") // TODO(50571): Remove illegal optimization.
  var rightStrong;
  var rightWeak;

  Node? get left => leftWeak?.target;
  set left(Node? value) {
    leftWeak = value == null ? null : new WeakReference(value);
    // Indirection: chance for WeakRef to be scanned before target is marked.
    leftStrong = [
      [value],
    ];
  }

  Node? get right => rightWeak?.target;
  set right(Node? value) {
    rightWeak = value == null ? null : new WeakReference(value);
    // Indirection: chance for WeakRef to be scanned before target is marked.
    rightStrong = [
      [value],
    ];
  }
}
