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

#include "platform/assert.h"

#include "vm/bootstrap_natives.h"
#include "vm/exceptions.h"
#include "vm/native_entry.h"
#include "vm/object.h"
#include "vm/object_graph_copy.h"

namespace dart {

DEFINE_NATIVE_ENTRY(GrowableList_allocate, 1, 1) {
  const TypeArguments& type_arguments =
      TypeArguments::CheckedHandle(zone, arguments->NativeTypeArgs());
  GET_NON_NULL_NATIVE_ARGUMENT(Array, data, arguments->NativeArgAt(0));
  if (data.Length() < 0) {
    Exceptions::ThrowRangeError("length",
                                Integer::Handle(Integer::New(data.Length())),
                                0,  // This is the limit the user sees.
                                Array::kMaxElements);
  }
  const GrowableObjectArray& new_array =
      GrowableObjectArray::Handle(GrowableObjectArray::New(data));
  new_array.SetTypeArguments(type_arguments);
  return new_array.ptr();
}

DEFINE_NATIVE_ENTRY(GrowableList_setIndexed, 0, 3) {
  const GrowableObjectArray& array =
      GrowableObjectArray::CheckedHandle(zone, arguments->NativeArgAt(0));
  GET_NON_NULL_NATIVE_ARGUMENT(Smi, index, arguments->NativeArgAt(1));
  if ((index.Value() < 0) || (index.Value() >= array.Length())) {
    Exceptions::ThrowRangeError("index", index, 0, array.Length() - 1);
  }
  GET_NON_NULL_NATIVE_ARGUMENT(Instance, value, arguments->NativeArgAt(2));
  array.SetAt(index.Value(), value);
  return Object::null();
}

DEFINE_NATIVE_ENTRY(GrowableList_getLength, 0, 1) {
  const GrowableObjectArray& array =
      GrowableObjectArray::CheckedHandle(zone, arguments->NativeArgAt(0));
  return Smi::New(array.Length());
}

DEFINE_NATIVE_ENTRY(GrowableList_getCapacity, 0, 1) {
  const GrowableObjectArray& array =
      GrowableObjectArray::CheckedHandle(zone, arguments->NativeArgAt(0));
  return Smi::New(array.Capacity());
}

DEFINE_NATIVE_ENTRY(GrowableList_setLength, 0, 2) {
  const GrowableObjectArray& array =
      GrowableObjectArray::CheckedHandle(zone, arguments->NativeArgAt(0));
  GET_NON_NULL_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(1));
  ASSERT((length.Value() >= 0) && (length.Value() <= array.Capacity()));
  array.SetLength(length.Value());
  return Object::null();
}

DEFINE_NATIVE_ENTRY(GrowableList_setData, 0, 2) {
  const GrowableObjectArray& array =
      GrowableObjectArray::CheckedHandle(zone, arguments->NativeArgAt(0));
  GET_NON_NULL_NATIVE_ARGUMENT(Array, data, arguments->NativeArgAt(1));
  ASSERT(data.Length() >= 0);
  array.SetData(data);
  return Object::null();
}

DEFINE_NATIVE_ENTRY(Internal_makeListFixedLength, 0, 1) {
  GET_NON_NULL_NATIVE_ARGUMENT(GrowableObjectArray, array,
                               arguments->NativeArgAt(0));
  return Array::MakeFixedLength(array, /* unique = */ true);
}

DEFINE_NATIVE_ENTRY(Internal_makeFixedListUnmodifiable, 0, 1) {
  GET_NON_NULL_NATIVE_ARGUMENT(Array, array, arguments->NativeArgAt(0));
  array.MakeImmutable();
  return array.ptr();
}

DEFINE_NATIVE_ENTRY(createConstMapFromMapOfDeeplyImmutables, 0, 1) {
  GET_NON_NULL_NATIVE_ARGUMENT(Map, map, arguments->NativeArgAt(0));
  const Instance& instance =
      Instance::Handle(zone, ConstMap::NewUninitialized());
  const LinkedHashBase& const_map = LinkedHashBase::Cast(instance);

  const_map.SetTypeArguments(
      TypeArguments::Handle(zone, map.GetTypeArguments()));
  intptr_t used_data = map.Length() << 1;
  const_map.set_used_data(used_data);
  const auto& data = Array::Handle(zone, map.data());
  const auto& new_data = Array::Handle(zone, ImmutableArray::New(used_data));
  const_map.set_data(new_data);
  const_map.set_deleted_keys(0);
  const_map.ComputeAndSetHashMask();
  Object& object = Object::Handle(zone);
  for (intptr_t i = 0; i < used_data; i++) {
    object = data.At(i);
    object.EnsureDeeplyImmutable(zone);
    new_data.SetAt(i, object);
  }
  return instance.ptr();
}

}  // namespace dart
