// Copyright (c) 2020, 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.

import 'dart:_internal' show VMInternalsForTesting;

import 'package:expect/expect.dart';

import 'unboxed_parameter_helper.dart';

// The VM class finalizer ensures that we only unbox fields which can be
// represented in the unboxed field bitmap in the target architecture (even when
// cross-compiling from 64-bit to 32-bit), see `Class::CalculateFieldOffsets()`.

int i = 0;
final Object objectFieldValue = Foo('i${i++}');

void main() {
  final object = buildTargetLayout64Bit();

  // Ensure all fields of the class are used.
  print(object);

  Expect.identical(object.field1, objectFieldValue);
  Expect.identical(object.field2, constIntegerFieldValue);
  Expect.identical(object.field64, objectFieldValue);
  Expect.identical(object.field65, constIntegerFieldValue);
  Expect.identical(object.field66, objectFieldValue);
  Expect.equals('i0', object.field66.value);

  // Forcefully cause a GC.
  VMInternalsForTesting.collectAllGarbage();

  Expect.identical(object.field1, objectFieldValue);
  Expect.identical(object.field2, constIntegerFieldValue);
  Expect.identical(object.field64, objectFieldValue);
  Expect.identical(object.field65, constIntegerFieldValue);
  Expect.identical(object.field66, objectFieldValue);
  Expect.equals('i0', object.field66.value);

  print(object);
}

class Foo {
  final String value;
  Foo(this.value);
}

class TargetLayout64Bit {
  // field0 is header word.
  final Object field1;
  final int field2;
  final int field3;
  final int field4;
  final int field5;
  final int field6;
  final int field7;
  final int field8;
  final int field9;
  final int field10;
  final int field11;
  final int field12;
  final int field13;
  final int field14;
  final int field15;
  final int field16;
  final int field17;
  final int field18;
  final int field19;
  final int field20;
  final int field21;
  final int field22;
  final int field23;
  final int field24;
  final int field25;
  final int field26;
  final int field27;
  final int field28;
  final int field29;
  final int field30;
  final int field31;
  final int field32;
  final int field33;
  final int field34;
  final int field35;
  final int field36;
  final int field37;
  final int field38;
  final int field39;
  final int field40;
  final int field41;
  final int field42;
  final int field43;
  final int field44;
  final int field45;
  final int field46;
  final int field47;
  final int field48;
  final int field49;
  final int field50;
  final int field51;
  final int field52;
  final int field53;
  final int field54;
  final int field55;
  final int field56;
  final int field57;
  final int field58;
  final int field59;
  final int field60;
  final int field61;
  final int field62;
  final int field63;

  // For 64-bit targets there are no more bits available so we need to start
  // boxing all fields from here (this is performed in the class finalizer).

  final Object field64;

  // If GC incorrectly uses (bitmap & (1 << 65)) then will access bit of
  // [field1] and incorrectly think it is an object pointer.
  final int field65;

  // If GC incorrectly uses (bitmap & (1 << 66)) then will access bit of
  // [field2] and incorrectly think it is a unboxed value.
  final Object field66;

  const TargetLayout64Bit(
    this.field1,
    this.field2,
    this.field3,
    this.field4,
    this.field5,
    this.field6,
    this.field7,
    this.field8,
    this.field9,
    this.field10,
    this.field11,
    this.field12,
    this.field13,
    this.field14,
    this.field15,
    this.field16,
    this.field17,
    this.field18,
    this.field19,
    this.field20,
    this.field21,
    this.field22,
    this.field23,
    this.field24,
    this.field25,
    this.field26,
    this.field27,
    this.field28,
    this.field29,
    this.field30,
    this.field31,
    this.field32,
    this.field33,
    this.field34,
    this.field35,
    this.field36,
    this.field37,
    this.field38,
    this.field39,
    this.field40,
    this.field41,
    this.field42,
    this.field43,
    this.field44,
    this.field45,
    this.field46,
    this.field47,
    this.field48,
    this.field49,
    this.field50,
    this.field51,
    this.field52,
    this.field53,
    this.field54,
    this.field55,
    this.field56,
    this.field57,
    this.field58,
    this.field59,
    this.field60,
    this.field61,
    this.field62,
    this.field63,
    this.field64,
    this.field65,
    this.field66,
  );

  // Ensure all fields are used.
  String toString() =>
      '''
      <header>, $field1, $field2, $field3, $field4, $field5, $field6, $field7,
      $field8, $field9, $field10, $field11, $field12, $field13, $field14, $field15,
      $field16, $field17, $field18, $field19, $field20, $field21, $field22, $field23,
      $field24, $field25, $field26, $field27, $field28, $field29, $field30, $field31,
      $field32, $field33, $field34, $field35, $field36, $field37, $field38, $field39,
      $field40, $field41, $field42, $field43, $field44, $field45, $field46, $field47,
      $field48, $field49, $field50, $field51, $field52, $field53, $field54, $field55,
      $field56, $field57, $field58, $field59, $field60, $field61, $field62, $field63,
      $field64, $field65, $field66''';
}

@pragma('vm:never-inline')
buildTargetLayout64Bit() => TargetLayout64Bit(
  objectFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  integerFieldValue,
  objectFieldValue,
  integerFieldValue,
  objectFieldValue,
);
