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

// OtherResources=print_object_layout_script.dart

// Test for --print-object-layout-to option of gen_snapshot.

import 'dart:convert' show jsonDecode;
import 'dart:math' show max;
import 'dart:io' show File, Platform;

import 'package:expect/expect.dart';
import 'package:path/path.dart' as path;
import 'use_flag_test_helper.dart';

verifyObjectLayout(String path) {
  final classes = jsonDecode(File(path).readAsStringSync());
  var sizeA, fieldsA;
  var sizeB, fieldsB;
  for (var cls in classes) {
    if (cls['class'] == 'ClassA') {
      sizeA = cls['size'].toInt();
      fieldsA = cls['fields'];
      print(cls);
    } else if (cls['class'] == 'ClassB') {
      sizeB = cls['size'].toInt();
      fieldsB = cls['fields'];
      print(cls);
    }
  }
  Expect.isNotNull(sizeA);
  Expect.isTrue(sizeA > 0);
  Expect.isTrue(fieldsA.length == 2);
  int maxOffsetA = 0;
  for (var field in fieldsA) {
    String fieldName = field['field'];
    Expect.isTrue(fieldName == 'fieldA1' || fieldName == 'fieldA2');
    int offset = field['offset'].toInt();
    Expect.isTrue((offset > 0) && (offset < sizeA));
    maxOffsetA = max(offset, maxOffsetA);
  }

  Expect.isNotNull(sizeB);
  Expect.isTrue(sizeB > 0);
  Expect.isTrue(sizeA <= sizeB);
  Expect.isTrue(fieldsB.length == 3);
  for (var field in fieldsB) {
    String fieldName = field['field'];
    if (fieldName == 'staticB4') {
      Expect.isTrue(field['static']);
    } else {
      Expect.isTrue(fieldName == 'fieldB1' || fieldName == 'fieldB2');
      int offset = field['offset'].toInt();
      Expect.isTrue((offset > 0) && (offset < sizeB));
      Expect.isTrue(offset > maxOffsetA);
    }
  }
}

main() async {
  if (!isAOTRuntime) {
    return; // Running in JIT: AOT binaries not available.
  }

  if (Platform.isAndroid) {
    return; // SDK tree not available on the test device.
  }

  // These are the tools we need to be available to run on a given platform:
  if (!File(platformDill).existsSync()) {
    throw "Cannot run test as $platformDill does not exist";
  }
  if (!await testExecutable(genSnapshot)) {
    throw "Cannot run test as $genSnapshot not available";
  }

  final testScriptUri = Platform.script.resolve(
    'print_object_layout_script.dart',
  );

  await withTempDir('print-object-layout-test', (String temp) async {
    final appDillPath = path.join(temp, 'app.dill');
    final snapshotPath = path.join(temp, 'aot.snapshot');
    final objectLayoutPath = path.join(temp, 'layout.json');

    await run(genKernel, <String>[
      '--aot',
      '--platform=$platformDill',
      '--output=$appDillPath',
      testScriptUri.toFilePath(),
    ]);

    await run(genSnapshot, <String>[
      '--snapshot-kind=app-aot-elf',
      '--elf=$snapshotPath',
      '--print-object-layout-to=$objectLayoutPath',
      appDillPath,
    ]);

    verifyObjectLayout(objectLayoutPath);
  });
}
