// 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 "lib/mirrors.h"

#include "lib/invocation_mirror.h"
#include "vm/bootstrap_natives.h"
#include "vm/class_finalizer.h"
#include "vm/dart_api_impl.h"
#include "vm/dart_entry.h"
#include "vm/exceptions.h"
#include "vm/kernel.h"
#include "vm/object_store.h"
#include "vm/parser.h"
#include "vm/port.h"
#include "vm/resolver.h"
#include "vm/symbols.h"

namespace dart {

#if !defined(DART_PRECOMPILED_RUNTIME)

#define RETURN_OR_PROPAGATE(expr)                                              \
  ObjectPtr result = expr;                                                     \
  if (IsErrorClassId(result->GetClassId())) {                                  \
    Exceptions::PropagateError(Error::Handle(Error::RawCast(result)));         \
  }                                                                            \
  return result;

static InstancePtr CreateMirror(const String& mirror_class_name,
                                const Array& constructor_arguments) {
  const Library& mirrors_lib = Library::Handle(Library::MirrorsLibrary());
  const String& constructor_name = Symbols::DotUnder();

  const Object& result = Object::Handle(DartLibraryCalls::InstanceCreate(
      mirrors_lib, mirror_class_name, constructor_name, constructor_arguments));
  if (result.IsError()) {
    Exceptions::PropagateError(Error::Cast(result));
  }
  return Instance::Cast(result).ptr();
}

// Conventions:
// * For throwing a NSM in a class klass we use its runtime type as receiver,
//   i.e., klass.RareType().
// * For throwing a NSM in a library, we just pass the null instance as
//   receiver.
static void ThrowNoSuchMethod(const Instance& receiver,
                              const String& function_name,
                              const Array& arguments,
                              const Array& argument_names,
                              const InvocationMirror::Level level,
                              const InvocationMirror::Kind kind) {
  const Smi& invocation_type =
      Smi::Handle(Smi::New(InvocationMirror::EncodeType(level, kind)));

  const Array& args = Array::Handle(Array::New(7));
  args.SetAt(0, receiver);
  args.SetAt(1, function_name);
  args.SetAt(2, invocation_type);
  args.SetAt(3, Object::smi_zero());  // Type arguments length.
  args.SetAt(4, Object::null_type_arguments());
  args.SetAt(5, arguments);
  args.SetAt(6, argument_names);

  const Library& libcore = Library::Handle(Library::CoreLibrary());
  const Class& cls =
      Class::Handle(libcore.LookupClass(Symbols::NoSuchMethodError()));
  const auto& error = cls.EnsureIsFinalized(Thread::Current());
  ASSERT(error == Error::null());
  const Function& throwNew =
      Function::Handle(cls.LookupFunctionAllowPrivate(Symbols::ThrowNew()));
  const Object& result =
      Object::Handle(DartEntry::InvokeFunction(throwNew, args));
  ASSERT(result.IsError());
  Exceptions::PropagateError(Error::Cast(result));
  UNREACHABLE();
}

static void EnsureConstructorsAreCompiled(const Function& func) {
  // Only generative constructors can have initializing formals.
  if (!func.IsGenerativeConstructor()) return;

  Thread* thread = Thread::Current();
  Zone* zone = thread->zone();
  const Class& cls = Class::Handle(zone, func.Owner());
  const Error& error = Error::Handle(zone, cls.EnsureIsFinalized(thread));
  if (!error.IsNull()) {
    Exceptions::PropagateError(error);
    UNREACHABLE();
  }
  func.EnsureHasCode();
}

static InstancePtr CreateParameterMirrorList(const Function& func,
                                             const FunctionType& signature,
                                             const Instance& owner_mirror) {
  Thread* const T = Thread::Current();
  Zone* const Z = T->zone();
  HANDLESCOPE(T);
  const intptr_t implicit_param_count = signature.num_implicit_parameters();
  const intptr_t non_implicit_param_count =
      signature.NumParameters() - implicit_param_count;
  const intptr_t index_of_first_optional_param =
      non_implicit_param_count - signature.NumOptionalParameters();
  const intptr_t index_of_first_named_param =
      non_implicit_param_count - signature.NumOptionalNamedParameters();
  const Array& results = Array::Handle(Z, Array::New(non_implicit_param_count));
  const Array& args = Array::Handle(Z, Array::New(9));

  Smi& pos = Smi::Handle(Z);
  String& name = String::Handle(Z);
  Instance& param = Instance::Handle(Z);
  Bool& is_final = Bool::Handle(Z);
  Object& default_value = Object::Handle(Z);
  Object& metadata = Object::Handle(Z);

  // We force compilation of constructors to ensure the types of initializing
  // formals have been corrected. We do not force the compilation of all types
  // of functions because some have no body, e.g. signature functions.
  if (!func.IsNull()) {
    EnsureConstructorsAreCompiled(func);
  }

  bool has_extra_parameter_info = true;
  if (non_implicit_param_count == 0) {
    has_extra_parameter_info = false;
  }
  if (func.IsNull() || func.IsImplicitConstructor()) {
    // This covers the default constructor and forwarding constructors.
    has_extra_parameter_info = false;
  }
  Array& param_descriptor = Array::Handle();
  if (has_extra_parameter_info) {
    // Reparse the function for the following information:
    // * The default value of a parameter.
    // * Whether a parameters has been declared as final.
    // * Any metadata associated with the parameter.
    Object& result = Object::Handle(kernel::BuildParameterDescriptor(func));
    if (result.IsError()) {
      Exceptions::PropagateError(Error::Cast(result));
      UNREACHABLE();
    }
    param_descriptor ^= result.ptr();
    ASSERT(param_descriptor.Length() ==
           (Parser::kParameterEntrySize * non_implicit_param_count));
  }

  args.SetAt(0, MirrorReference::Handle(MirrorReference::New(signature)));
  args.SetAt(2, owner_mirror);

  if (!has_extra_parameter_info) {
    is_final = Bool::True().ptr();
    default_value = Object::null();
    metadata = Object::null();
  }

  for (intptr_t i = 0; i < non_implicit_param_count; i++) {
    pos = Smi::New(i);
    if (i >= index_of_first_named_param) {
      // Named parameters are stored in the signature.
      name = signature.ParameterNameAt(implicit_param_count + i);
    } else if (!func.IsNull()) {
      // Positional parameters are stored in the function.
      name = func.ParameterNameAt(implicit_param_count + i);
    } else {
      // We were not given a function, only the type, so create placeholder
      // names for the positional parameters.
      const char* const placeholder = OS::SCreate(Z, ":param%" Pd "", i);
      name = String::New(placeholder);
    }
    if (has_extra_parameter_info) {
      is_final ^= param_descriptor.At(i * Parser::kParameterEntrySize +
                                      Parser::kParameterIsFinalOffset);
      default_value = param_descriptor.At(i * Parser::kParameterEntrySize +
                                          Parser::kParameterDefaultValueOffset);
      metadata = param_descriptor.At(i * Parser::kParameterEntrySize +
                                     Parser::kParameterMetadataOffset);
    }
    ASSERT(default_value.IsNull() || default_value.IsInstance());

    // Arguments 0 (referent) and 2 (owner) are the same for all parameters. See
    // above.
    args.SetAt(1, name);
    args.SetAt(3, pos);
    args.SetAt(4, Bool::Get(i >= index_of_first_optional_param));
    args.SetAt(5, Bool::Get(i >= index_of_first_named_param));
    args.SetAt(6, is_final);
    args.SetAt(7, default_value);
    args.SetAt(8, metadata);
    param = CreateMirror(Symbols::_ParameterMirror(), args);
    results.SetAt(i, param);
  }
  results.MakeImmutable();
  return results.ptr();
}

static InstancePtr CreateTypeVariableMirror(const TypeParameter& param,
                                            const Instance& owner_mirror) {
  const Array& args = Array::Handle(Array::New(3));
  args.SetAt(0, param);
  args.SetAt(1, String::Handle(param.UserVisibleName()));
  args.SetAt(2, owner_mirror);
  return CreateMirror(Symbols::_TypeVariableMirror(), args);
}

// We create a list in native code and let Dart code create the type mirror
// object and the ordered map.
static InstancePtr CreateTypeVariableList(const Class& cls) {
  const intptr_t num_type_params = cls.NumTypeParameters();
  if (num_type_params == 0) {
    return Object::empty_array().ptr();
  }
  const Array& result = Array::Handle(Array::New(num_type_params * 2));
  TypeParameter& type = TypeParameter::Handle();
  String& name = String::Handle();
  for (intptr_t i = 0; i < num_type_params; i++) {
    type = cls.TypeParameterAt(i, Nullability::kNonNullable);
    ASSERT(type.IsFinalized());
    name = type.UserVisibleName();
    result.SetAt(2 * i, name);
    result.SetAt(2 * i + 1, type);
  }
  return result.ptr();
}

static InstancePtr CreateFunctionTypeMirror(const AbstractType& type) {
  ASSERT(type.IsFunctionType());
  const Class& closure_class =
      Class::Handle(IsolateGroup::Current()->object_store()->closure_class());
  const FunctionType& sig = FunctionType::Cast(type);
  const Array& args = Array::Handle(Array::New(3));
  args.SetAt(0, MirrorReference::Handle(MirrorReference::New(closure_class)));
  args.SetAt(1, MirrorReference::Handle(MirrorReference::New(sig)));
  args.SetAt(2, type);
  return CreateMirror(Symbols::_FunctionTypeMirror(), args);
}

static InstancePtr CreateMethodMirror(const Function& func,
                                      const Instance& owner_mirror,
                                      const AbstractType& instantiator) {
  const Array& args = Array::Handle(Array::New(6));
  args.SetAt(0, MirrorReference::Handle(MirrorReference::New(func)));

  String& name = String::Handle(func.name());
  name = String::ScrubNameRetainPrivate(
      name, func.is_extension_member() || func.is_extension_type_member());
  args.SetAt(1, name);
  args.SetAt(2, owner_mirror);
  args.SetAt(3, instantiator);
  args.SetAt(4, Bool::Get(func.is_static()));

  intptr_t kind_flags = 0;
  kind_flags |=
      (static_cast<intptr_t>(func.is_abstract()) << Mirrors::kAbstract);
  kind_flags |=
      (static_cast<intptr_t>(func.IsGetterFunction()) << Mirrors::kGetter);
  kind_flags |=
      (static_cast<intptr_t>(func.IsSetterFunction()) << Mirrors::kSetter);
  bool is_ctor = (func.kind() == UntaggedFunction::kConstructor);
  kind_flags |= (static_cast<intptr_t>(is_ctor) << Mirrors::kConstructor);
  kind_flags |= (static_cast<intptr_t>(is_ctor && func.is_const())
                 << Mirrors::kConstCtor);
  kind_flags |=
      (static_cast<intptr_t>(is_ctor && func.IsGenerativeConstructor())
       << Mirrors::kGenerativeCtor);
  kind_flags |= (static_cast<intptr_t>(false) << Mirrors::kRedirectingCtor);
  kind_flags |= (static_cast<intptr_t>(is_ctor && func.IsFactory())
                 << Mirrors::kFactoryCtor);
  kind_flags |=
      (static_cast<intptr_t>(func.is_external()) << Mirrors::kExternal);
  bool is_synthetic = func.is_synthetic();
  kind_flags |= (static_cast<intptr_t>(is_synthetic) << Mirrors::kSynthetic);
  kind_flags |= (static_cast<intptr_t>(func.is_extension_member())
                 << Mirrors::kExtensionMember);
  kind_flags |= (static_cast<intptr_t>(func.is_extension_type_member())
                 << Mirrors::kExtensionTypeMember);
  args.SetAt(5, Smi::Handle(Smi::New(kind_flags)));

  return CreateMirror(Symbols::_MethodMirror(), args);
}

static InstancePtr CreateVariableMirror(const Field& field,
                                        const Instance& owner_mirror) {
  const MirrorReference& field_ref =
      MirrorReference::Handle(MirrorReference::New(field));

  const String& name = String::Handle(field.name());

  const Array& args = Array::Handle(Array::New(9));
  args.SetAt(0, field_ref);
  args.SetAt(1, name);
  args.SetAt(2, owner_mirror);
  args.SetAt(3, Object::null_instance());  // Null for type.
  args.SetAt(4, Bool::Get(field.is_static()));
  args.SetAt(5, Bool::Get(field.is_final()));
  args.SetAt(6, Bool::Get(field.is_const()));
  args.SetAt(7, Bool::Get(field.is_extension_member()));
  args.SetAt(8, Bool::Get(field.is_extension_type_member()));

  return CreateMirror(Symbols::_VariableMirror(), args);
}

static InstancePtr CreateClassMirror(const Class& cls,
                                     const AbstractType& type,
                                     const Bool& is_declaration,
                                     const Instance& owner_mirror) {
  ASSERT(!cls.IsDynamicClass());
  ASSERT(!cls.IsVoidClass());
  ASSERT(!cls.IsNeverClass());
  ASSERT(!type.IsNull());
  ASSERT(type.IsFinalized());
  ASSERT(type.IsCanonical());
  const Array& args = Array::Handle(Array::New(9));
  args.SetAt(0, MirrorReference::Handle(MirrorReference::New(cls)));
  args.SetAt(1, type);
  args.SetAt(2, String::Handle(cls.Name()));
  args.SetAt(3, owner_mirror);
  args.SetAt(4, Bool::Get(cls.is_abstract()));
  args.SetAt(5, Bool::Get(cls.IsGeneric()));
  args.SetAt(6, Bool::Get(cls.is_transformed_mixin_application()));
  args.SetAt(7, cls.NumTypeParameters() == 0 ? Bool::False() : is_declaration);
  args.SetAt(8, Bool::Get(cls.is_enum_class()));
  return CreateMirror(Symbols::_ClassMirror(), args);
}

static bool IsCensoredLibrary(const String& url) {
  static const char* const censored_libraries[] = {
      "dart:_builtin",
      "dart:_vmservice",
      "dart:vmservice_io",
  };
  for (const char* censored_library : censored_libraries) {
    if (url.Equals(censored_library)) {
      return true;
    }
  }
  if (!Api::IsFfiEnabled() && url.Equals(Symbols::DartFfi())) {
    return true;
  }
  return false;
}

static InstancePtr CreateLibraryMirror(Thread* thread, const Library& lib) {
  Zone* zone = thread->zone();
  ASSERT(!lib.IsNull());
  const Array& args = Array::Handle(zone, Array::New(3));
  args.SetAt(0, MirrorReference::Handle(zone, MirrorReference::New(lib)));
  String& str = String::Handle(zone);
  str = lib.name();
  args.SetAt(1, str);
  str = lib.url();
  if (IsCensoredLibrary(str)) {
    // Censored library (grumble).
    return Instance::null();
  }
  args.SetAt(2, str);
  return CreateMirror(Symbols::_LibraryMirror(), args);
}

static InstancePtr CreateCombinatorMirror(const Object& identifiers,
                                          bool is_show) {
  const Array& args = Array::Handle(Array::New(2));
  args.SetAt(0, identifiers);
  args.SetAt(1, Bool::Get(is_show));
  return CreateMirror(Symbols::_CombinatorMirror(), args);
}

static InstancePtr CreateLibraryDependencyMirror(Thread* thread,
                                                 const Instance& importer,
                                                 const Library& importee,
                                                 const Array& show_names,
                                                 const Array& hide_names,
                                                 const Object& metadata,
                                                 const LibraryPrefix& prefix,
                                                 const String& prefix_name,
                                                 const bool is_import,
                                                 const bool is_deferred) {
  const Instance& importee_mirror =
      Instance::Handle(CreateLibraryMirror(thread, importee));
  if (importee_mirror.IsNull()) {
    // Imported library is censored: censor the import.
    return Instance::null();
  }

  intptr_t n = show_names.IsNull() ? 0 : show_names.Length();
  intptr_t m = hide_names.IsNull() ? 0 : hide_names.Length();
  const Array& combinators = Array::Handle(Array::New(n + m));
  Object& t = Object::Handle();
  intptr_t i = 0;
  for (intptr_t j = 0; j < n; j++) {
    t = show_names.At(j);
    t = CreateCombinatorMirror(t, true);
    combinators.SetAt(i++, t);
  }
  for (intptr_t j = 0; j < m; j++) {
    t = hide_names.At(j);
    t = CreateCombinatorMirror(t, false);
    combinators.SetAt(i++, t);
  }

  const Array& args = Array::Handle(Array::New(7));
  args.SetAt(0, importer);
  if (importee.Loaded() || prefix.IsNull()) {
    // A native extension is never "loaded" by the embedder. Use the fact that
    // it doesn't have an prefix where asa  deferred import does to distinguish
    // it from a deferred import. It will appear like an empty library.
    args.SetAt(1, importee_mirror);
  } else {
    args.SetAt(1, prefix);
  }
  args.SetAt(2, combinators);
  args.SetAt(3, prefix_name);
  args.SetAt(4, Bool::Get(is_import));
  args.SetAt(5, Bool::Get(is_deferred));
  args.SetAt(6, metadata);
  return CreateMirror(Symbols::_LibraryDependencyMirror(), args);
}

static InstancePtr CreateLibraryDependencyMirror(Thread* thread,
                                                 const Instance& importer,
                                                 const Namespace& ns,
                                                 const LibraryPrefix& prefix,
                                                 const bool is_import,
                                                 const bool is_deferred) {
  const Library& importee = Library::Handle(ns.target());
  const Array& show_names = Array::Handle(ns.show_names());
  const Array& hide_names = Array::Handle(ns.hide_names());

  const Library& owner = Library::Handle(ns.owner());
  Object& metadata = Object::Handle(owner.GetMetadata(ns));
  if (metadata.IsError()) {
    Exceptions::PropagateError(Error::Cast(metadata));
    UNREACHABLE();
  }

  auto& prefix_name = String::Handle();
  if (!prefix.IsNull()) {
    prefix_name = prefix.name();
  }

  return CreateLibraryDependencyMirror(thread, importer, importee, show_names,
                                       hide_names, metadata, prefix,
                                       prefix_name, is_import, is_deferred);
}

DEFINE_NATIVE_ENTRY(LibraryMirror_fromPrefix, 0, 1) {
  GET_NON_NULL_NATIVE_ARGUMENT(LibraryPrefix, prefix,
                               arguments->NativeArgAt(0));
  const Library& deferred_lib = Library::Handle(prefix.GetLibrary(0));
  if (!deferred_lib.Loaded()) {
    return Instance::null();
  }
  return CreateLibraryMirror(thread, deferred_lib);
}

DEFINE_NATIVE_ENTRY(LibraryMirror_libraryDependencies, 0, 2) {
  GET_NON_NULL_NATIVE_ARGUMENT(Instance, lib_mirror, arguments->NativeArgAt(0));
  GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, ref, arguments->NativeArgAt(1));
  const Library& lib = Library::Handle(ref.GetLibraryReferent());

  Array& ports = Array::Handle();
  Namespace& ns = Namespace::Handle();
  Instance& dep = Instance::Handle();
  LibraryPrefix& prefix = LibraryPrefix::Handle();
  GrowableObjectArray& deps =
      GrowableObjectArray::Handle(GrowableObjectArray::New());

  // Unprefixed imports.
  ports = lib.imports();
  for (intptr_t i = 0; i < ports.Length(); i++) {
    ns ^= ports.At(i);
    if (!ns.IsNull()) {
      dep = CreateLibraryDependencyMirror(thread, lib_mirror, ns, prefix, true,
                                          false);
      if (!dep.IsNull()) {
        deps.Add(dep);
      }
    }
  }

  // Exports.
  ports = lib.exports();
  for (intptr_t i = 0; i < ports.Length(); i++) {
    ns ^= ports.At(i);
    dep = CreateLibraryDependencyMirror(thread, lib_mirror, ns, prefix, false,
                                        false);
    if (!dep.IsNull()) {
      deps.Add(dep);
    }
  }

  // Prefixed imports.
  DictionaryIterator entries(lib);
  Object& entry = Object::Handle();
  while (entries.HasNext()) {
    entry = entries.GetNext();
    if (entry.IsLibraryPrefix()) {
      prefix ^= entry.ptr();
      ports = prefix.imports();
      for (intptr_t i = 0; i < ports.Length(); i++) {
        ns ^= ports.At(i);
        if (!ns.IsNull()) {
          dep = CreateLibraryDependencyMirror(thread, lib_mirror, ns, prefix,
                                              true, prefix.is_deferred_load());
          if (!dep.IsNull()) {
            deps.Add(dep);
          }
        }
      }
    }
  }

  return deps.ptr();
}

static InstancePtr CreateTypeMirror(const AbstractType& type) {
  ASSERT(type.IsFinalized());
  ASSERT(type.IsCanonical());

  if (type.IsFunctionType()) {
    return CreateFunctionTypeMirror(type);
  }
  if (type.IsRecordType()) {
    const Class& cls =
        Class::Handle(IsolateGroup::Current()->object_store()->record_class());
    return CreateClassMirror(cls, AbstractType::Handle(cls.DeclarationType()),
                             Bool::False(), Object::null_instance());
  }
  if (type.HasTypeClass()) {
    const Class& cls = Class::Handle(type.type_class());
    // Handle void and dynamic types.
    if (cls.IsVoidClass()) {
      Array& args = Array::Handle(Array::New(1));
      args.SetAt(0, Symbols::Void());
      return CreateMirror(Symbols::_SpecialTypeMirror(), args);
    } else if (cls.IsDynamicClass()) {
      Array& args = Array::Handle(Array::New(1));
      args.SetAt(0, Symbols::Dynamic());
      return CreateMirror(Symbols::_SpecialTypeMirror(), args);
    } else if (cls.IsNeverClass()) {
      Array& args = Array::Handle(Array::New(1));
      args.SetAt(0, Symbols::Never());
      return CreateMirror(Symbols::_SpecialTypeMirror(), args);
    }
    // TODO(regis): Until mirrors reflect nullability, force kNonNullable,
    // except for Null type, which should remain nullable.
    if (!type.IsNullType()) {
      Type& legacy_type = Type::Handle(Type::Cast(type).ToNullability(
          Nullability::kNonNullable, Heap::kOld));
      legacy_type ^= legacy_type.Canonicalize(Thread::Current());
      return CreateClassMirror(cls, legacy_type, Bool::False(),
                               Object::null_instance());
    }
    return CreateClassMirror(cls, type, Bool::False(), Object::null_instance());
  } else if (type.IsTypeParameter()) {
    // TODO(regis): Until mirrors reflect nullability, force kNonNullable.
    TypeParameter& legacy_type =
        TypeParameter::Handle(TypeParameter::Cast(type).ToNullability(
            Nullability::kNonNullable, Heap::kOld));
    legacy_type ^= legacy_type.Canonicalize(Thread::Current());
    return CreateTypeVariableMirror(legacy_type, Object::null_instance());
  }
  UNREACHABLE();
  return Instance::null();
}

static InstancePtr CreateIsolateMirror() {
  Thread* thread = Thread::Current();
  Isolate* isolate = thread->isolate();
  const String& debug_name = String::Handle(String::New(isolate->name()));
  const Library& root_library = Library::Handle(
      thread->zone(), isolate->group()->object_store()->root_library());
  const Instance& root_library_mirror =
      Instance::Handle(CreateLibraryMirror(thread, root_library));

  const Array& args = Array::Handle(Array::New(2));
  args.SetAt(0, debug_name);
  args.SetAt(1, root_library_mirror);
  return CreateMirror(Symbols::_IsolateMirror(), args);
}

static void VerifyMethodKindShifts() {
#ifdef DEBUG
  Thread* thread = Thread::Current();
  Zone* zone = thread->zone();
  const Library& lib = Library::Handle(zone, Library::MirrorsLibrary());
  const Class& cls = Class::Handle(
      zone, lib.LookupClassAllowPrivate(Symbols::_MethodMirror()));
  Error& error = Error::Handle(zone);
  error ^= cls.EnsureIsFinalized(thread);
  ASSERT(error.IsNull());

  Field& field = Field::Handle(zone);
  Smi& value = Smi::Handle(zone);
  String& fname = String::Handle(zone);

#define CHECK_KIND_SHIFT(name)                                                 \
  fname ^= String::New(#name);                                                 \
  field = cls.LookupField(fname);                                              \
  ASSERT(!field.IsNull());                                                     \
  if (field.IsUninitialized()) {                                               \
    error ^= field.InitializeStatic();                                         \
    ASSERT(error.IsNull());                                                    \
  }                                                                            \
  value ^= field.StaticValue();                                                \
  ASSERT(value.Value() == Mirrors::name);
  MIRRORS_KIND_SHIFT_LIST(CHECK_KIND_SHIFT)
#undef CHECK_KIND_SHIFT
#endif
}

static AbstractTypePtr InstantiateType(const AbstractType& type,
                                       const AbstractType& instantiator) {
  // Generic function type parameters are not reified, but mapped to dynamic,
  // i.e. all function type parameters are free with a null vector.
  ASSERT(type.IsFinalized());
  ASSERT(type.IsCanonical());
  Thread* thread = Thread::Current();

  if (type.IsInstantiated()) {
    return type.Canonicalize(thread);
  }
  TypeArguments& instantiator_type_args = TypeArguments::Handle();
  if (!instantiator.IsNull() && instantiator.IsType()) {
    ASSERT(instantiator.IsFinalized());
    if (instantiator.type_class_id() == kInstanceCid) {
      // Handle types created in ClosureMirror_function.
      instantiator_type_args = instantiator.arguments();
    } else {
      instantiator_type_args =
          Type::Cast(instantiator)
              .GetInstanceTypeArguments(thread, /*canonicalize=*/false);
    }
  }
  AbstractType& result = AbstractType::Handle(type.InstantiateFrom(
      instantiator_type_args, Object::null_type_arguments(), kAllFree,
      Heap::kOld));
  ASSERT(result.IsFinalized());
  return result.Canonicalize(thread);
}

DEFINE_NATIVE_ENTRY(MirrorSystem_libraries, 0, 0) {
  const GrowableObjectArray& libraries = GrowableObjectArray::Handle(
      zone, isolate->group()->object_store()->libraries());

  const intptr_t num_libraries = libraries.Length();
  const GrowableObjectArray& library_mirrors = GrowableObjectArray::Handle(
      zone, GrowableObjectArray::New(num_libraries));
  Library& library = Library::Handle(zone);
  Instance& library_mirror = Instance::Handle(zone);

  for (int i = 0; i < num_libraries; i++) {
    library ^= libraries.At(i);
    library_mirror = CreateLibraryMirror(thread, library);
    if (!library_mirror.IsNull() && library.Loaded()) {
      library_mirrors.Add(library_mirror);
    }
  }
  return library_mirrors.ptr();
}

DEFINE_NATIVE_ENTRY(MirrorSystem_isolate, 0, 0) {
  VerifyMethodKindShifts();

  return CreateIsolateMirror();
}

static void ThrowLanguageError(const char* message) {
  const Error& error =
      Error::Handle(LanguageError::New(String::Handle(String::New(message))));
  Exceptions::PropagateError(error);
}

DEFINE_NATIVE_ENTRY(IsolateMirror_loadUri, 0, 1) {
  GET_NON_NULL_NATIVE_ARGUMENT(String, uri, arguments->NativeArgAt(0));

  if (!isolate->group()->HasTagHandler()) {
    ThrowLanguageError("no library handler registered");
  }

  NoReloadScope no_reload(thread);

  // Canonicalize library URI.
  String& canonical_uri = String::Handle(zone);
  if (uri.StartsWith(Symbols::DartScheme())) {
    canonical_uri = uri.ptr();
  } else {
    isolate->BlockClassFinalization();
    const Object& result = Object::Handle(
        zone, isolate->group()->CallTagHandler(
                  Dart_kCanonicalizeUrl,
                  Library::Handle(
                      zone, isolate->group()->object_store()->root_library()),
                  uri));
    isolate->UnblockClassFinalization();
    if (result.IsError()) {
      if (result.IsLanguageError()) {
        Exceptions::ThrowCompileTimeError(LanguageError::Cast(result));
      }
      Exceptions::PropagateError(Error::Cast(result));
    } else if (!result.IsString()) {
      ThrowLanguageError("library handler failed URI canonicalization");
    }

    canonical_uri ^= result.ptr();
  }

  // Return the existing library if it has already been loaded.
  Library& library =
      Library::Handle(zone, Library::LookupLibrary(thread, canonical_uri));
  if (!library.IsNull()) {
    return CreateLibraryMirror(thread, library);
  }

  // Request the embedder to load the library.
  isolate->BlockClassFinalization();
  Object& result = Object::Handle(
      zone, isolate->group()->CallTagHandler(
                Dart_kImportTag,
                Library::Handle(
                    zone, isolate->group()->object_store()->root_library()),
                canonical_uri));
  isolate->UnblockClassFinalization();
  if (result.IsError()) {
    if (result.IsLanguageError()) {
      Exceptions::ThrowCompileTimeError(LanguageError::Cast(result));
    }
    Exceptions::PropagateError(Error::Cast(result));
  }

  // This code assumes a synchronous tag handler (which dart::bin and tonic
  // provide). Strictly though we should complete a future in response to
  // Dart_FinalizeLoading.

  if (!ClassFinalizer::ProcessPendingClasses()) {
    Exceptions::PropagateError(Error::Handle(thread->sticky_error()));
  }

  // Prefer the tag handler's idea of which library is represented by the URI.
  if (result.IsLibrary()) {
    return CreateLibraryMirror(thread, Library::Cast(result));
  }

  if (result.IsNull()) {
    library = Library::LookupLibrary(thread, canonical_uri);
    if (!library.IsNull()) {
      return CreateLibraryMirror(thread, library);
    }
  }

  FATAL("Non-library from tag handler");
}

DEFINE_NATIVE_ENTRY(Mirrors_makeLocalClassMirror, 0, 1) {
  GET_NON_NULL_NATIVE_ARGUMENT(AbstractType, type, arguments->NativeArgAt(0));
  ASSERT(type.IsFinalized());
  if (!type.IsType() || type.IsDynamicType() || type.IsVoidType() ||
      type.IsNeverType()) {
    Exceptions::ThrowArgumentError(type);
    UNREACHABLE();
  }
  const Class& cls = Class::Handle(type.type_class());
  ASSERT(!cls.IsNull());
  return CreateClassMirror(cls, AbstractType::Handle(cls.DeclarationType()),
                           Bool::True(),  // is_declaration
                           Object::null_instance());
}

DEFINE_NATIVE_ENTRY(Mirrors_makeLocalTypeMirror, 0, 1) {
  GET_NON_NULL_NATIVE_ARGUMENT(AbstractType, type, arguments->NativeArgAt(0));
  return CreateTypeMirror(type);
}

DEFINE_NATIVE_ENTRY(Mirrors_instantiateGenericType, 0, 2) {
  GET_NON_NULL_NATIVE_ARGUMENT(AbstractType, type, arguments->NativeArgAt(0));
  GET_NON_NULL_NATIVE_ARGUMENT(Array, args, arguments->NativeArgAt(1));

  const Class& clz = Class::Handle(
      type.IsFunctionType()
          ? IsolateGroup::Current()->object_store()->closure_class()
          : type.type_class());
  if (!clz.IsGeneric()) {
    const Array& error_args = Array::Handle(Array::New(3));
    error_args.SetAt(0, type);
    error_args.SetAt(1, String::Handle(String::New("key")));
    error_args.SetAt(2, String::Handle(String::New(
                            "Type must be a generic class or function.")));
    Exceptions::ThrowByType(Exceptions::kArgumentValue, error_args);
    UNREACHABLE();
  }
  if (clz.NumTypeParameters() != args.Length()) {
    const Array& error_args = Array::Handle(Array::New(3));
    error_args.SetAt(0, args);
    error_args.SetAt(1, String::Handle(String::New("typeArguments")));
    error_args.SetAt(2, String::Handle(String::New(
                            "Number of type arguments does not match.")));
    Exceptions::ThrowByType(Exceptions::kArgumentValue, error_args);
    UNREACHABLE();
  }

  intptr_t num_expected_type_arguments = args.Length();
  TypeArguments& type_args_obj = TypeArguments::Handle();
  type_args_obj = TypeArguments::New(num_expected_type_arguments);
  AbstractType& type_arg = AbstractType::Handle();
  Instance& instance = Instance::Handle();
  for (intptr_t i = 0; i < args.Length(); i++) {
    instance ^= args.At(i);
    if (!instance.IsType()) {
      const Array& error_args = Array::Handle(Array::New(3));
      error_args.SetAt(0, args);
      error_args.SetAt(1, String::Handle(String::New("typeArguments")));
      error_args.SetAt(2, String::Handle(String::New(
                              "Type arguments must be instances of Type.")));
      Exceptions::ThrowByType(Exceptions::kArgumentValue, error_args);
      UNREACHABLE();
    }
    type_arg ^= args.At(i);
    type_args_obj.SetTypeAt(i, type_arg);
  }

  Type& instantiated_type = Type::Handle(Type::New(clz, type_args_obj));
  instantiated_type ^= ClassFinalizer::FinalizeType(instantiated_type);
  return instantiated_type.ptr();
}

DEFINE_NATIVE_ENTRY(Mirrors_mangleName, 0, 2) {
  GET_NON_NULL_NATIVE_ARGUMENT(String, name, arguments->NativeArgAt(0));
  GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, ref, arguments->NativeArgAt(1));
  const Library& lib = Library::Handle(ref.GetLibraryReferent());
  return lib.IsPrivate(name) ? lib.PrivateName(name) : name.ptr();
}

DEFINE_NATIVE_ENTRY(MirrorReference_equals, 0, 2) {
  GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, a, arguments->NativeArgAt(0));
  GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, b, arguments->NativeArgAt(1));
  return Bool::Get(a.referent() == b.referent()).ptr();
}

DEFINE_NATIVE_ENTRY(DeclarationMirror_metadata, 0, 1) {
  GET_NON_NULL_NATIVE_ARGUMENT(Instance, reflectee, arguments->NativeArgAt(0));
  Object& decl = Object::Handle();
  if (reflectee.IsMirrorReference()) {
    const MirrorReference& decl_ref = MirrorReference::Cast(reflectee);
    decl = decl_ref.referent();
  } else if (reflectee.IsTypeParameter()) {
    decl = reflectee.ptr();
  } else {
    UNREACHABLE();
  }

  Class& klass = Class::Handle();
  Library& library = Library::Handle();

  if (decl.IsClass()) {
    klass ^= decl.ptr();
    library = klass.library();
  } else if (decl.IsFunction()) {
    klass = Function::Cast(decl).Owner();
    library = klass.library();
  } else if (decl.IsField()) {
    klass = Field::Cast(decl).Owner();
    library = klass.library();
  } else if (decl.IsLibrary()) {
    library ^= decl.ptr();
  } else if (decl.IsTypeParameter()) {
    // There is no reference from a canonical type parameter to its declaration.
    return Object::empty_array().ptr();
  } else {
    return Object::empty_array().ptr();
  }

  const Object& metadata = Object::Handle(library.GetMetadata(decl));
  if (metadata.IsError()) {
    Exceptions::PropagateError(Error::Cast(metadata));
  }
  return metadata.ptr();
}

DEFINE_NATIVE_ENTRY(FunctionTypeMirror_call_method, 0, 2) {
  GET_NON_NULL_NATIVE_ARGUMENT(Instance, owner_mirror,
                               arguments->NativeArgAt(0));
  // Return get:call() method on class _Closure.
  const auto& getter_name = Symbols::GetCall();
  const Class& closure_class =
      Class::Handle(IsolateGroup::Current()->object_store()->closure_class());
  const Function& get_call = Function::Handle(
      Resolver::ResolveDynamicAnyArgs(zone, closure_class, getter_name,
                                      /*allow_add=*/false));
  ASSERT(!get_call.IsNull());
  return CreateMethodMirror(get_call, owner_mirror, AbstractType::Handle());
}

DEFINE_NATIVE_ENTRY(FunctionTypeMirror_parameters, 0, 2) {
  GET_NON_NULL_NATIVE_ARGUMENT(Instance, owner, arguments->NativeArgAt(0));
  GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, ref, arguments->NativeArgAt(1));
  const FunctionType& sig = FunctionType::Handle(ref.GetFunctionTypeReferent());
  return CreateParameterMirrorList(Object::null_function(), sig, owner);
}

DEFINE_NATIVE_ENTRY(FunctionTypeMirror_return_type, 0, 1) {
  GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, ref, arguments->NativeArgAt(0));
  const FunctionType& sig = FunctionType::Handle(ref.GetFunctionTypeReferent());
  ASSERT(!sig.IsNull());
  AbstractType& type = AbstractType::Handle(sig.result_type());
  // Signatures of function types are instantiated, but not canonical.
  return type.Canonicalize(thread);
}

DEFINE_NATIVE_ENTRY(ClassMirror_libraryUri, 0, 1) {
  GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, ref, arguments->NativeArgAt(0));
  const Class& klass = Class::Handle(ref.GetClassReferent());
  const Library& library = Library::Handle(klass.library());
  ASSERT(!library.IsNull());
  return library.url();
}

DEFINE_NATIVE_ENTRY(ClassMirror_supertype, 0, 1) {
  GET_NON_NULL_NATIVE_ARGUMENT(AbstractType, type, arguments->NativeArgAt(0));
  ASSERT(type.IsFinalized());
  const Class& cls = Class::Handle(
      type.IsFunctionType()
          ? IsolateGroup::Current()->object_store()->closure_class()
          : type.type_class());
  const AbstractType& super_type = AbstractType::Handle(cls.super_type());
  ASSERT(super_type.IsNull() || super_type.IsFinalized());
  return super_type.ptr();
}

DEFINE_NATIVE_ENTRY(ClassMirror_supertype_instantiated, 0, 1) {
  GET_NON_NULL_NATIVE_ARGUMENT(AbstractType, type, arguments->NativeArgAt(0));
  ASSERT(type.IsFinalized());
  const Class& cls = Class::Handle(
      type.IsFunctionType()
          ? IsolateGroup::Current()->object_store()->closure_class()
          : type.type_class());
  const AbstractType& super_type = AbstractType::Handle(cls.super_type());
  return InstantiateType(super_type, type);
}

DEFINE_NATIVE_ENTRY(ClassMirror_interfaces, 0, 1) {
  GET_NON_NULL_NATIVE_ARGUMENT(AbstractType, type, arguments->NativeArgAt(0));
  ASSERT(type.IsFinalized());
  const Class& cls = Class::Handle(
      type.IsFunctionType()
          ? IsolateGroup::Current()->object_store()->closure_class()
          : type.type_class());
  const Error& error = Error::Handle(cls.EnsureIsFinalized(thread));
  if (!error.IsNull()) {
    Exceptions::PropagateError(error);
  }

  return cls.interfaces();
}

DEFINE_NATIVE_ENTRY(ClassMirror_interfaces_instantiated, 0, 1) {
  GET_NON_NULL_NATIVE_ARGUMENT(AbstractType, type, arguments->NativeArgAt(0));
  ASSERT(type.IsFinalized());
  const Class& cls = Class::Handle(
      type.IsFunctionType()
          ? IsolateGroup::Current()->object_store()->closure_class()
          : type.type_class());
  const Error& error = Error::Handle(cls.EnsureIsFinalized(thread));
  if (!error.IsNull()) {
    Exceptions::PropagateError(error);
  }

  Array& interfaces = Array::Handle(cls.interfaces());
  Array& interfaces_inst = Array::Handle(Array::New(interfaces.Length()));
  AbstractType& interface = AbstractType::Handle();

  for (int i = 0; i < interfaces.Length(); i++) {
    interface ^= interfaces.At(i);
    interface = InstantiateType(interface, type);
    interfaces_inst.SetAt(i, interface);
  }

  return interfaces_inst.ptr();
}

DEFINE_NATIVE_ENTRY(ClassMirror_mixin, 0, 1) {
  GET_NON_NULL_NATIVE_ARGUMENT(AbstractType, type, arguments->NativeArgAt(0));
  ASSERT(type.IsFinalized());
  const Class& cls = Class::Handle(
      type.IsFunctionType()
          ? IsolateGroup::Current()->object_store()->closure_class()
          : type.type_class());
  AbstractType& mixin_type = AbstractType::Handle();
  if (cls.is_transformed_mixin_application()) {
    const Array& interfaces = Array::Handle(cls.interfaces());
    mixin_type ^= interfaces.At(interfaces.Length() - 1);
  }
  ASSERT(mixin_type.IsNull() || mixin_type.IsFinalized());
  return mixin_type.ptr();
}

DEFINE_NATIVE_ENTRY(ClassMirror_mixin_instantiated, 0, 2) {
  GET_NON_NULL_NATIVE_ARGUMENT(AbstractType, type, arguments->NativeArgAt(0));
  GET_NON_NULL_NATIVE_ARGUMENT(AbstractType, instantiator,
                               arguments->NativeArgAt(1));
  ASSERT(type.IsFinalized());
  const Class& cls = Class::Handle(
      type.IsFunctionType()
          ? IsolateGroup::Current()->object_store()->closure_class()
          : type.type_class());
  AbstractType& mixin_type = AbstractType::Handle();
  if (cls.is_transformed_mixin_application()) {
    const Array& interfaces = Array::Handle(cls.interfaces());
    mixin_type ^= interfaces.At(interfaces.Length() - 1);
  }
  if (mixin_type.IsNull()) {
    return mixin_type.ptr();
  }

  return InstantiateType(mixin_type, instantiator);
}

DEFINE_NATIVE_ENTRY(ClassMirror_members, 0, 3) {
  GET_NON_NULL_NATIVE_ARGUMENT(Instance, owner_mirror,
                               arguments->NativeArgAt(0));
  GET_NATIVE_ARGUMENT(AbstractType, owner_instantiator,
                      arguments->NativeArgAt(1));
  GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, ref, arguments->NativeArgAt(2));
  const Class& klass = Class::Handle(ref.GetClassReferent());

  const Error& error = Error::Handle(klass.EnsureIsFinalized(thread));
  if (!error.IsNull()) {
    Exceptions::PropagateError(error);
  }

  const Array& fields = Array::Handle(klass.fields());
  const intptr_t num_fields = fields.Length();

  const Array& functions = Array::Handle(klass.current_functions());
  const intptr_t num_functions = functions.Length();

  Instance& member_mirror = Instance::Handle();
  const GrowableObjectArray& member_mirrors = GrowableObjectArray::Handle(
      GrowableObjectArray::New(num_fields + num_functions));

  Field& field = Field::Handle();
  for (intptr_t i = 0; i < num_fields; i++) {
    field ^= fields.At(i);
    if (field.is_reflectable()) {
      member_mirror = CreateVariableMirror(field, owner_mirror);
      member_mirrors.Add(member_mirror);
    }
  }

  Function& func = Function::Handle();
  for (intptr_t i = 0; i < num_functions; i++) {
    func ^= functions.At(i);
    if (func.is_reflectable() &&
        (func.kind() == UntaggedFunction::kRegularFunction ||
         func.kind() == UntaggedFunction::kGetterFunction ||
         func.kind() == UntaggedFunction::kSetterFunction)) {
      member_mirror =
          CreateMethodMirror(func, owner_mirror, owner_instantiator);
      member_mirrors.Add(member_mirror);
    }
  }

  return member_mirrors.ptr();
}

DEFINE_NATIVE_ENTRY(ClassMirror_constructors, 0, 3) {
  GET_NON_NULL_NATIVE_ARGUMENT(Instance, owner_mirror,
                               arguments->NativeArgAt(0));
  GET_NATIVE_ARGUMENT(AbstractType, owner_instantiator,
                      arguments->NativeArgAt(1));
  GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, ref, arguments->NativeArgAt(2));
  const Class& klass = Class::Handle(ref.GetClassReferent());

  const Error& error = Error::Handle(klass.EnsureIsFinalized(thread));
  if (!error.IsNull()) {
    Exceptions::PropagateError(error);
  }

  const Array& functions = Array::Handle(klass.current_functions());
  const intptr_t num_functions = functions.Length();

  Instance& constructor_mirror = Instance::Handle();
  const GrowableObjectArray& constructor_mirrors =
      GrowableObjectArray::Handle(GrowableObjectArray::New(num_functions));

  Function& func = Function::Handle();
  for (intptr_t i = 0; i < num_functions; i++) {
    func ^= functions.At(i);
    if (func.is_reflectable() &&
        func.kind() == UntaggedFunction::kConstructor) {
      constructor_mirror =
          CreateMethodMirror(func, owner_mirror, owner_instantiator);
      constructor_mirrors.Add(constructor_mirror);
    }
  }

  return constructor_mirrors.ptr();
}

DEFINE_NATIVE_ENTRY(LibraryMirror_members, 0, 2) {
  GET_NON_NULL_NATIVE_ARGUMENT(Instance, owner_mirror,
                               arguments->NativeArgAt(0));
  GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, ref, arguments->NativeArgAt(1));
  const Library& library = Library::Handle(zone, ref.GetLibraryReferent());

  library.EnsureTopLevelClassIsFinalized();

  Instance& member_mirror = Instance::Handle(zone);
  const GrowableObjectArray& member_mirrors =
      GrowableObjectArray::Handle(zone, GrowableObjectArray::New());

  Object& entry = Object::Handle(zone);
  DictionaryIterator entries(library);

  Error& error = Error::Handle(zone);
  AbstractType& type = AbstractType::Handle(zone);

  while (entries.HasNext()) {
    entry = entries.GetNext();
    if (entry.IsClass()) {
      const Class& klass = Class::Cast(entry);
      ASSERT(!klass.IsDynamicClass());
      ASSERT(!klass.IsVoidClass());
      ASSERT(!klass.IsNeverClass());
      error = klass.EnsureIsFinalized(thread);
      if (!error.IsNull()) {
        Exceptions::PropagateError(error);
      }
      type = klass.DeclarationType();
      member_mirror = CreateClassMirror(klass, type,
                                        Bool::True(),  // is_declaration
                                        owner_mirror);
      member_mirrors.Add(member_mirror);
    } else if (entry.IsField()) {
      const Field& field = Field::Cast(entry);
      if (field.is_reflectable()) {
        member_mirror = CreateVariableMirror(field, owner_mirror);
        member_mirrors.Add(member_mirror);
      }
    } else if (entry.IsFunction()) {
      const Function& func = Function::Cast(entry);
      if (func.is_reflectable() &&
          (func.kind() == UntaggedFunction::kRegularFunction ||
           func.kind() == UntaggedFunction::kGetterFunction ||
           func.kind() == UntaggedFunction::kSetterFunction)) {
        member_mirror =
            CreateMethodMirror(func, owner_mirror, AbstractType::Handle());
        member_mirrors.Add(member_mirror);
      }
    }
  }

  return member_mirrors.ptr();
}

DEFINE_NATIVE_ENTRY(ClassMirror_type_variables, 0, 1) {
  GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, ref, arguments->NativeArgAt(0));
  const Class& klass = Class::Handle(ref.GetClassReferent());
  const Error& error = Error::Handle(zone, klass.EnsureIsFinalized(thread));
  if (!error.IsNull()) {
    Exceptions::PropagateError(error);
    UNREACHABLE();
  }
  return CreateTypeVariableList(klass);
}

DEFINE_NATIVE_ENTRY(ClassMirror_type_arguments, 0, 1) {
  GET_NON_NULL_NATIVE_ARGUMENT(AbstractType, type, arguments->NativeArgAt(0));

  const Class& cls = Class::Handle(
      type.IsFunctionType()
          ? IsolateGroup::Current()->object_store()->closure_class()
          : type.type_class());
  const intptr_t num_params = cls.NumTypeParameters();

  if (num_params == 0) {
    return Object::empty_array().ptr();
  }

  const Array& result = Array::Handle(Array::New(num_params));
  AbstractType& arg_type = AbstractType::Handle();
  Instance& type_mirror = Instance::Handle();
  const TypeArguments& args =
      TypeArguments::Handle(Type::Cast(type).arguments());

  // Handle argument lists that have been optimized away, because either no
  // arguments have been provided, or all arguments are dynamic. Return a list
  // of typemirrors on dynamic in this case.
  if (args.IsNull()) {
    arg_type = Object::dynamic_type().ptr();
    type_mirror = CreateTypeMirror(arg_type);
    for (intptr_t i = 0; i < num_params; i++) {
      result.SetAt(i, type_mirror);
    }
    return result.ptr();
  }

  ASSERT(args.Length() == num_params);
  for (intptr_t i = 0; i < num_params; i++) {
    arg_type = args.TypeAt(i);
    type_mirror = CreateTypeMirror(arg_type);
    result.SetAt(i, type_mirror);
  }
  return result.ptr();
}

DEFINE_NATIVE_ENTRY(TypeVariableMirror_owner, 0, 1) {
  // Type parameters do not have a reference to their owner anymore.
  const AbstractType& type = AbstractType::Handle(Type::NullType());
  Class& owner = Class::Handle(type.type_class());
  return CreateClassMirror(owner, type,
                           Bool::True(),  // is_declaration
                           Instance::null_instance());
}

DEFINE_NATIVE_ENTRY(TypeVariableMirror_upper_bound, 0, 1) {
  GET_NON_NULL_NATIVE_ARGUMENT(TypeParameter, param, arguments->NativeArgAt(0));
  return param.bound();
}

static constexpr bool kNoStrictEntryPointChecks = false;

DEFINE_NATIVE_ENTRY(InstanceMirror_invoke, 0, 5) {
  // Argument 0 is the mirror, which is unused by the native. It exists
  // because this native is an instance method in order to be polymorphic
  // with its cousins.
  GET_NATIVE_ARGUMENT(Instance, reflectee, arguments->NativeArgAt(1));
  GET_NON_NULL_NATIVE_ARGUMENT(String, function_name,
                               arguments->NativeArgAt(2));
  GET_NON_NULL_NATIVE_ARGUMENT(Array, args, arguments->NativeArgAt(3));
  GET_NON_NULL_NATIVE_ARGUMENT(Array, arg_names, arguments->NativeArgAt(4));
  RETURN_OR_PROPAGATE(reflectee.Invoke(function_name, args, arg_names,
                                       kNoStrictEntryPointChecks));
}

DEFINE_NATIVE_ENTRY(InstanceMirror_invokeGetter, 0, 3) {
  // Argument 0 is the mirror, which is unused by the native. It exists
  // because this native is an instance method in order to be polymorphic
  // with its cousins.
  GET_NATIVE_ARGUMENT(Instance, reflectee, arguments->NativeArgAt(1));
  GET_NON_NULL_NATIVE_ARGUMENT(String, getter_name, arguments->NativeArgAt(2));
  RETURN_OR_PROPAGATE(
      reflectee.InvokeGetter(getter_name, kNoStrictEntryPointChecks));
}

DEFINE_NATIVE_ENTRY(InstanceMirror_invokeSetter, 0, 4) {
  // Argument 0 is the mirror, which is unused by the native. It exists
  // because this native is an instance method in order to be polymorphic
  // with its cousins.
  GET_NATIVE_ARGUMENT(Instance, reflectee, arguments->NativeArgAt(1));
  GET_NON_NULL_NATIVE_ARGUMENT(String, setter_name, arguments->NativeArgAt(2));
  GET_NATIVE_ARGUMENT(Instance, value, arguments->NativeArgAt(3));
  RETURN_OR_PROPAGATE(
      reflectee.InvokeSetter(setter_name, value, kNoStrictEntryPointChecks));
}

DEFINE_NATIVE_ENTRY(InstanceMirror_computeType, 0, 1) {
  GET_NON_NULL_NATIVE_ARGUMENT(Instance, instance, arguments->NativeArgAt(0));
  const AbstractType& type = AbstractType::Handle(instance.GetType(Heap::kNew));
  // The static type of null is specified to be the bottom type, however, the
  // runtime type of null is the Null type, which we correctly return here.
  return type.Canonicalize(thread);
}

DEFINE_NATIVE_ENTRY(ClosureMirror_function, 0, 1) {
  GET_NON_NULL_NATIVE_ARGUMENT(Instance, closure, arguments->NativeArgAt(0));
  ASSERT(!closure.IsNull());

  Function& function = Function::Handle();
  bool callable = closure.IsCallable(&function);
  if (callable) {
    const Function& parent = Function::Handle(function.parent_function());
    if (function.IsImplicitClosureFunction() || parent.is_extension_member() ||
        parent.is_extension_type_member()) {
      // The VM uses separate Functions for tear-offs, but the mirrors consider
      // the tear-offs to be the same as the torn-off methods. Avoid handing out
      // a reference to the tear-off here to avoid a special case in the
      // the equality test.
      // In the case of extension methods also we avoid handing out a reference
      // to the tear-off and instead get the parent function of the
      // anonymous closure.
      function = parent.ptr();
    }

    Type& instantiator = Type::Handle();
    if (closure.IsClosure()) {
      const TypeArguments& arguments = TypeArguments::Handle(
          Closure::Cast(closure).instantiator_type_arguments());
      // TODO(regis): Mirrors need work to properly support generic functions.
      // The 'instantiator' created below should not be a type, but two type
      // argument vectors: instantiator_type_arguments and
      // function_type_arguments.
      const Class& cls = Class::Handle(
          IsolateGroup::Current()->object_store()->object_class());
      instantiator = Type::New(cls, arguments);
      instantiator.SetIsFinalized();
    }
    return CreateMethodMirror(function, Instance::null_instance(),
                              instantiator);
  }
  return Instance::null();
}

DEFINE_NATIVE_ENTRY(ClassMirror_invoke, 0, 5) {
  // Argument 0 is the mirror, which is unused by the native. It exists
  // because this native is an instance method in order to be polymorphic
  // with its cousins.
  GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, ref, arguments->NativeArgAt(1));
  const Class& klass = Class::Handle(ref.GetClassReferent());
  GET_NON_NULL_NATIVE_ARGUMENT(String, function_name,
                               arguments->NativeArgAt(2));
  GET_NON_NULL_NATIVE_ARGUMENT(Array, args, arguments->NativeArgAt(3));
  GET_NON_NULL_NATIVE_ARGUMENT(Array, arg_names, arguments->NativeArgAt(4));
  RETURN_OR_PROPAGATE(
      klass.Invoke(function_name, args, arg_names, kNoStrictEntryPointChecks));
}

DEFINE_NATIVE_ENTRY(ClassMirror_invokeGetter, 0, 3) {
  // Argument 0 is the mirror, which is unused by the native. It exists
  // because this native is an instance method in order to be polymorphic
  // with its cousins.
  GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, ref, arguments->NativeArgAt(1));
  const Class& klass = Class::Handle(ref.GetClassReferent());
  const Error& error = Error::Handle(zone, klass.EnsureIsFinalized(thread));
  if (!error.IsNull()) {
    Exceptions::PropagateError(error);
    UNREACHABLE();
  }
  GET_NON_NULL_NATIVE_ARGUMENT(String, getter_name, arguments->NativeArgAt(2));
  RETURN_OR_PROPAGATE(
      klass.InvokeGetter(getter_name, kNoStrictEntryPointChecks));
}

DEFINE_NATIVE_ENTRY(ClassMirror_invokeSetter, 0, 4) {
  // Argument 0 is the mirror, which is unused by the native. It exists
  // because this native is an instance method in order to be polymorphic
  // with its cousins.
  GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, ref, arguments->NativeArgAt(1));
  const Class& klass = Class::Handle(ref.GetClassReferent());
  GET_NON_NULL_NATIVE_ARGUMENT(String, setter_name, arguments->NativeArgAt(2));
  GET_NATIVE_ARGUMENT(Instance, value, arguments->NativeArgAt(3));
  RETURN_OR_PROPAGATE(
      klass.InvokeSetter(setter_name, value, kNoStrictEntryPointChecks));
}

DEFINE_NATIVE_ENTRY(ClassMirror_invokeConstructor, 0, 5) {
  GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, ref, arguments->NativeArgAt(0));
  const Class& klass = Class::Handle(ref.GetClassReferent());
  GET_NATIVE_ARGUMENT(Type, type, arguments->NativeArgAt(1));
  GET_NON_NULL_NATIVE_ARGUMENT(String, constructor_name,
                               arguments->NativeArgAt(2));
  GET_NON_NULL_NATIVE_ARGUMENT(Array, explicit_args, arguments->NativeArgAt(3));
  GET_NON_NULL_NATIVE_ARGUMENT(Array, arg_names, arguments->NativeArgAt(4));

  const Error& error =
      Error::Handle(zone, klass.EnsureIsAllocateFinalized(thread));
  if (!error.IsNull()) {
    Exceptions::PropagateError(error);
    UNREACHABLE();
  }

  // By convention, the static function implementing a named constructor 'C'
  // for class 'A' is labeled 'A.C', and the static function implementing the
  // unnamed constructor for class 'A' is labeled 'A.'.
  // This convention prevents users from explicitly calling constructors.
  const String& klass_name = String::Handle(klass.Name());
  String& external_constructor_name = String::Handle(klass_name.ptr());
  String& internal_constructor_name =
      String::Handle(String::Concat(klass_name, Symbols::Dot()));
  if (!constructor_name.IsNull() && constructor_name.Length() > 0) {
    internal_constructor_name =
        String::Concat(internal_constructor_name, constructor_name);
    external_constructor_name = internal_constructor_name.ptr();
  }

  Function& lookup_constructor = Function::Handle(
      Resolver::ResolveFunction(zone, klass, internal_constructor_name));

  if (lookup_constructor.IsNull() ||
      (lookup_constructor.kind() != UntaggedFunction::kConstructor) ||
      !lookup_constructor.is_reflectable()) {
    ThrowNoSuchMethod(AbstractType::Handle(klass.RareType()),
                      external_constructor_name, explicit_args, arg_names,
                      InvocationMirror::kConstructor,
                      InvocationMirror::kMethod);
    UNREACHABLE();
  }

  if (klass.is_abstract() && !lookup_constructor.IsFactory()) {
    const Array& error_args = Array::Handle(Array::New(3));
    error_args.SetAt(0, klass_name);
    // 1 = script url
    // 2 = token position
    Exceptions::ThrowByType(Exceptions::kAbstractClassInstantiation,
                            error_args);
    UNREACHABLE();
  }

  if (klass.is_enum_class() && !lookup_constructor.IsFactory()) {
    const Array& error_args = Array::Handle(Array::New(1));
    error_args.SetAt(0, String::Handle(String::New("Cannot instantiate enum")));
    Exceptions::ThrowByType(Exceptions::kUnsupported, error_args);
    UNREACHABLE();
  }

  ASSERT(!type.IsNull());
  if (!type.IsInstantiated()) {
    // Must have been a declaration type.
    type = klass.RareType();
    ASSERT(type.IsInstantiated());
  }

  const bool is_generic_factory =
      lookup_constructor.IsFactory() && lookup_constructor.IsGeneric();
  intptr_t type_args_len = 0;
  TypeArguments& instantiator_type_arguments = TypeArguments::Handle();
  TypeArguments& function_type_arguments = TypeArguments::Handle();
  if (lookup_constructor.IsGenerativeConstructor()) {
    instantiator_type_arguments = type.GetInstanceTypeArguments(thread);
  } else if (is_generic_factory) {
    function_type_arguments = type.arguments();
    type_args_len = lookup_constructor.NumTypeParameters();
    ASSERT(function_type_arguments.IsNull() ||
           function_type_arguments.Length() == type_args_len);
  }

  Class& redirected_klass = Class::Handle(klass.ptr());
  const intptr_t num_explicit_args = explicit_args.Length();
  const intptr_t num_implicit_positional_args =
      lookup_constructor.IsGenerativeConstructor() ? 1 : 0;
  const intptr_t num_implicit_args =
      ((type_args_len > 0) ? 1 : 0) + num_implicit_positional_args;
  const Array& args =
      Array::Handle(Array::New(num_implicit_args + num_explicit_args));

  // Copy over the explicit arguments.
  Object& explicit_argument = Object::Handle();
  for (int i = 0; i < num_explicit_args; i++) {
    explicit_argument = explicit_args.At(i);
    args.SetAt(i + num_implicit_args, explicit_argument);
  }
  if (is_generic_factory) {
    args.SetAt(0, function_type_arguments);
  }

  const Array& args_descriptor_array =
      Array::Handle(ArgumentsDescriptor::NewBoxed(
          type_args_len, num_implicit_positional_args + num_explicit_args,
          arg_names));

  ArgumentsDescriptor args_descriptor(args_descriptor_array);
  if (!lookup_constructor.AreValidArguments(args_descriptor, nullptr)) {
    external_constructor_name = lookup_constructor.name();
    ThrowNoSuchMethod(AbstractType::Handle(klass.RareType()),
                      external_constructor_name, explicit_args, arg_names,
                      InvocationMirror::kConstructor,
                      InvocationMirror::kMethod);
    UNREACHABLE();
  }
#if defined(DEBUG)
  if (lookup_constructor.IsGenerativeConstructor()) {
    // Make sure the receiver is the null value, so that DoArgumentTypesMatch
    // does not attempt to retrieve the instantiator type arguments from
    // the receiver.
    explicit_argument = args.At(args_descriptor.FirstArgIndex());
    ASSERT(explicit_argument.IsNull());
  }
#endif
  const Object& type_error =
      Object::Handle(lookup_constructor.DoArgumentTypesMatch(
          args, args_descriptor, instantiator_type_arguments,
          function_type_arguments));
  if (!type_error.IsNull()) {
    Exceptions::PropagateError(Error::Cast(type_error));
    UNREACHABLE();
  }

  Instance& new_object = Instance::Handle();
  if (lookup_constructor.IsGenerativeConstructor()) {
    // Constructors get the uninitialized object.
    // Note we have delayed allocation until after the function
    // type and argument matching checks.
    new_object = Instance::New(redirected_klass);
    if (!instantiator_type_arguments.IsNull()) {
      // The type arguments will be null if the class has no type parameters, in
      // which case the following call would fail because there is no slot
      // reserved in the object for the type vector.
      new_object.SetTypeArguments(instantiator_type_arguments);
    }
    args.SetAt(0, new_object);
  }

  // Invoke the constructor and return the new object.
  const Object& result = Object::Handle(DartEntry::InvokeFunction(
      lookup_constructor, args, args_descriptor_array));
  if (result.IsError()) {
    Exceptions::PropagateError(Error::Cast(result));
    UNREACHABLE();
  }

  // Factories may return null.
  ASSERT(result.IsInstance() || result.IsNull());

  if (lookup_constructor.IsGenerativeConstructor()) {
    return new_object.ptr();
  } else {
    return result.ptr();
  }
}

DEFINE_NATIVE_ENTRY(LibraryMirror_invoke, 0, 5) {
  // Argument 0 is the mirror, which is unused by the native. It exists
  // because this native is an instance method in order to be polymorphic
  // with its cousins.
  GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, ref, arguments->NativeArgAt(1));
  const Library& library = Library::Handle(ref.GetLibraryReferent());
  GET_NON_NULL_NATIVE_ARGUMENT(String, function_name,
                               arguments->NativeArgAt(2));
  GET_NON_NULL_NATIVE_ARGUMENT(Array, args, arguments->NativeArgAt(3));
  GET_NON_NULL_NATIVE_ARGUMENT(Array, arg_names, arguments->NativeArgAt(4));
  RETURN_OR_PROPAGATE(library.Invoke(function_name, args, arg_names,
                                     kNoStrictEntryPointChecks));
}

DEFINE_NATIVE_ENTRY(LibraryMirror_invokeGetter, 0, 3) {
  // Argument 0 is the mirror, which is unused by the native. It exists
  // because this native is an instance method in order to be polymorphic
  // with its cousins.
  GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, ref, arguments->NativeArgAt(1));
  const Library& library = Library::Handle(ref.GetLibraryReferent());
  GET_NON_NULL_NATIVE_ARGUMENT(String, getter_name, arguments->NativeArgAt(2));
  RETURN_OR_PROPAGATE(
      library.InvokeGetter(getter_name, kNoStrictEntryPointChecks));
}

DEFINE_NATIVE_ENTRY(LibraryMirror_invokeSetter, 0, 4) {
  // Argument 0 is the mirror, which is unused by the native. It exists
  // because this native is an instance method in order to be polymorphic
  // with its cousins.
  GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, ref, arguments->NativeArgAt(1));
  const Library& library = Library::Handle(ref.GetLibraryReferent());
  GET_NON_NULL_NATIVE_ARGUMENT(String, setter_name, arguments->NativeArgAt(2));
  GET_NATIVE_ARGUMENT(Instance, value, arguments->NativeArgAt(3));
  RETURN_OR_PROPAGATE(
      library.InvokeSetter(setter_name, value, kNoStrictEntryPointChecks));
}

DEFINE_NATIVE_ENTRY(MethodMirror_owner, 0, 2) {
  GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, ref, arguments->NativeArgAt(0));
  GET_NATIVE_ARGUMENT(AbstractType, instantiator, arguments->NativeArgAt(1));
  const Function& func = Function::Handle(ref.GetFunctionReferent());
  if (func.IsNonImplicitClosureFunction()) {
    return CreateMethodMirror(Function::Handle(func.parent_function()),
                              Object::null_instance(), instantiator);
  }
  const Class& owner = Class::Handle(func.Owner());
  if (owner.IsTopLevel()) {
    return CreateLibraryMirror(thread, Library::Handle(owner.library()));
  }

  AbstractType& type = AbstractType::Handle(owner.DeclarationType());
  return CreateClassMirror(owner, type, Bool::True(), Object::null_instance());
}

DEFINE_NATIVE_ENTRY(MethodMirror_parameters, 0, 2) {
  GET_NON_NULL_NATIVE_ARGUMENT(Instance, owner, arguments->NativeArgAt(0));
  GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, ref, arguments->NativeArgAt(1));
  const Function& func = Function::Handle(ref.GetFunctionReferent());
  const FunctionType& sig = FunctionType::Handle(func.signature());
  return CreateParameterMirrorList(func, sig, owner);
}

DEFINE_NATIVE_ENTRY(MethodMirror_return_type, 0, 2) {
  GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, ref, arguments->NativeArgAt(0));
  const Function& func = Function::Handle(ref.GetFunctionReferent());
  GET_NATIVE_ARGUMENT(AbstractType, instantiator, arguments->NativeArgAt(1));
  // We handle constructors in Dart code.
  ASSERT(!func.IsGenerativeConstructor());
  AbstractType& type = AbstractType::Handle(func.result_type());
  type =
      type.Canonicalize(thread);  // Instantiated signatures are not canonical.
  return InstantiateType(type, instantiator);
}

DEFINE_NATIVE_ENTRY(MethodMirror_source, 0, 1) {
  GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, ref, arguments->NativeArgAt(0));
  const Function& func = Function::Handle(ref.GetFunctionReferent());
  return func.GetSource();
}

static InstancePtr CreateSourceLocation(const String& uri,
                                        intptr_t line,
                                        intptr_t column) {
  const Array& args = Array::Handle(Array::New(3));
  args.SetAt(0, uri);
  args.SetAt(1, Smi::Handle(Smi::New(line)));
  args.SetAt(2, Smi::Handle(Smi::New(column)));
  return CreateMirror(Symbols::_SourceLocation(), args);
}

DEFINE_NATIVE_ENTRY(DeclarationMirror_location, 0, 1) {
  GET_NON_NULL_NATIVE_ARGUMENT(Instance, reflectee, arguments->NativeArgAt(0));
  Object& decl = Object::Handle(zone);
  if (reflectee.IsMirrorReference()) {
    const MirrorReference& decl_ref = MirrorReference::Cast(reflectee);
    decl = decl_ref.referent();
  } else if (reflectee.IsTypeParameter()) {
    decl = reflectee.ptr();
  } else {
    UNREACHABLE();
  }

  Script& script = Script::Handle(zone);
  TokenPosition token_pos = TokenPosition::kNoSource;

  if (decl.IsFunction()) {
    const Function& func = Function::Cast(decl);
    if (func.IsImplicitConstructor()) {
      // These are synthetic methods; they have no source.
      return Instance::null();
    }
    script = func.script();
    token_pos = func.token_pos();
  } else if (decl.IsClass()) {
    const Class& cls = Class::Cast(decl);
    if (cls.is_synthesized_class() && !cls.is_enum_class()) {
      return Instance::null();  // Synthetic.
    }
    script = cls.script();
    token_pos = cls.token_pos();
  } else if (decl.IsField()) {
    const Field& field = Field::Cast(decl);
    script = field.Script();
    token_pos = field.token_pos();
  } else if (decl.IsTypeParameter()) {
    return Instance::null();
  } else if (decl.IsLibrary()) {
    const Library& lib = Library::Cast(decl);
    if (lib.ptr() == Library::NativeWrappersLibrary()) {
      return Instance::null();  // No source.
    }
    const Array& scripts = Array::Handle(zone, lib.LoadedScripts());
    ASSERT(scripts.Length() > 0);
    script ^= scripts.At(scripts.Length() - 1);
    ASSERT(!script.IsNull());
    const String& uri = String::Handle(zone, script.url());
    return CreateSourceLocation(uri, 1, 1);
  } else {
    FATAL("Unexpected declaration type: %s", decl.ToCString());
  }

  ASSERT(!script.IsNull());
  if (token_pos == TokenPosition::kNoSource) {
    return Instance::null();
  }

  const String& uri = String::Handle(zone, script.url());
  intptr_t from_line = 0, from_col = 0;
  script.GetTokenLocation(token_pos, &from_line, &from_col);
  return CreateSourceLocation(uri, from_line, from_col);
}

DEFINE_NATIVE_ENTRY(ParameterMirror_type, 0, 3) {
  GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, ref, arguments->NativeArgAt(0));
  GET_NON_NULL_NATIVE_ARGUMENT(Smi, pos, arguments->NativeArgAt(1));
  GET_NATIVE_ARGUMENT(AbstractType, instantiator, arguments->NativeArgAt(2));
  const FunctionType& signature =
      FunctionType::Handle(ref.GetFunctionTypeReferent());
  AbstractType& type = AbstractType::Handle(signature.ParameterTypeAt(
      signature.num_implicit_parameters() + pos.Value()));
  type =
      type.Canonicalize(thread);  // Instantiated signatures are not canonical.
  return InstantiateType(type, instantiator);
}

DEFINE_NATIVE_ENTRY(VariableMirror_type, 0, 2) {
  GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, ref, arguments->NativeArgAt(0));
  const Field& field = Field::Handle(ref.GetFieldReferent());
  GET_NATIVE_ARGUMENT(AbstractType, instantiator, arguments->NativeArgAt(1));
  const AbstractType& type = AbstractType::Handle(field.type());
  return InstantiateType(type, instantiator);
}

DEFINE_NATIVE_ENTRY(TypeMirror_subtypeTest, 0, 2) {
  GET_NON_NULL_NATIVE_ARGUMENT(AbstractType, a, arguments->NativeArgAt(0));
  GET_NON_NULL_NATIVE_ARGUMENT(AbstractType, b, arguments->NativeArgAt(1));
  return Bool::Get(a.IsSubtypeOf(b, Heap::kNew)).ptr();
}

#endif  // !DART_PRECOMPILED_RUNTIME

}  // namespace dart
