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

#include "vm/compiler/ffi/native_type.h"

#include "vm/compiler/backend/il_test_helper.h"
#include "vm/unit_test.h"

namespace dart {
namespace compiler {
namespace ffi {

ISOLATE_UNIT_TEST_CASE(Ffi_NativeType_Primitive_FromAbstractType) {
  Zone* Z = thread->zone();

  const auto& ffi_library = Library::Handle(Library::FfiLibrary());
  const auto& int8_class = Class::Handle(GetClass(ffi_library, "Int8"));
  const auto& int8_type = Type::Handle(int8_class.DeclarationType());
  const char* error = nullptr;
  const auto& native_type = *NativeType::FromAbstractType(Z, int8_type, &error);
  EXPECT_NULLPTR(error);

  EXPECT_EQ(1, native_type.SizeInBytes());
  EXPECT_STREQ("int8", native_type.ToCString());
  EXPECT(native_type.IsInt());
  EXPECT(native_type.IsPrimitive());
}

// In all calling conventions `bool` behaves as `uint8_t` when it comes to:
// - size
// - alignment in structs
// - alignment on stack
// - upper bytes in cpu registers.
ISOLATE_UNIT_TEST_CASE(Ffi_NativeType_Bool_FromAbstractType) {
  Zone* Z = thread->zone();

  const auto& ffi_library = Library::Handle(Library::FfiLibrary());
  const auto& bool_class = Class::Handle(GetClass(ffi_library, "Bool"));
  const auto& bool_type = Type::Handle(bool_class.DeclarationType());
  const char* error = nullptr;
  const auto& bool_native_type =
      *NativeType::FromAbstractType(Z, bool_type, &error);
  EXPECT_NULLPTR(error);

  const auto& uint8_native_type = *new (Z) NativePrimitiveType(kUint8);

  EXPECT(bool_native_type.Equals(uint8_native_type));
  EXPECT_EQ(1, bool_native_type.SizeInBytes());
  EXPECT_STREQ("uint8", bool_native_type.ToCString());
  EXPECT(bool_native_type.IsInt());
  EXPECT(bool_native_type.IsPrimitive());
}

// Test that we construct `NativeType` correctly from `Type`.
ISOLATE_UNIT_TEST_CASE(Ffi_NativeType_Struct_FromAbstractType) {
  Zone* Z = thread->zone();

  const char* kScript =
      R"(
      import 'dart:ffi';

      final class MyStruct extends Struct {
        @Int8()
        external int a0;

        external Pointer<Int8> a1;
      }
      )";

  const auto& root_library = Library::Handle(LoadTestScript(kScript));
  const auto& struct_class = Class::Handle(GetClass(root_library, "MyStruct"));
  const auto& struct_type = Type::Handle(struct_class.DeclarationType());

  const char* error = nullptr;
  const auto& native_type =
      NativeType::FromAbstractType(Z, struct_type, &error)->AsCompound();
  EXPECT_NULLPTR(error);

  EXPECT_EQ(2, native_type.members().length());

  const auto& int8_type = *new (Z) NativePrimitiveType(kInt8);
  EXPECT(int8_type.Equals(*native_type.members()[0]));

  EXPECT_EQ(compiler::target::kWordSize,
            native_type.members()[1]->SizeInBytes());
}

}  // namespace ffi
}  // namespace compiler
}  // namespace dart
