[vm] Clean up metadata tracking.

Remove the use of Fields, which have since the metadata mechanism was introduced come to require global registration.

Retain some sloppiness matching of declarations by name instead of identity for the sake of hot reload.

TEST=ci
Change-Id: Ifa4cc7724096c606f994b057b7838e124ceb3626
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/175141
Commit-Queue: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
diff --git a/runtime/lib/mirrors.cc b/runtime/lib/mirrors.cc
index a829957..eddf299 100644
--- a/runtime/lib/mirrors.cc
+++ b/runtime/lib/mirrors.cc
@@ -435,11 +435,12 @@
                                                  const LibraryPrefix& prefix,
                                                  const bool is_import,
                                                  const bool is_deferred) {
-  const Library& importee = Library::Handle(ns.library());
+  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());
 
-  Object& metadata = Object::Handle(ns.GetMetadata());
+  const Library& owner = Library::Handle(ns.owner());
+  Object& metadata = Object::Handle(owner.GetMetadata(ns));
   if (metadata.IsError()) {
     Exceptions::PropagateError(Error::Cast(metadata));
     UNREACHABLE();
diff --git a/runtime/vm/canonical_tables.cc b/runtime/vm/canonical_tables.cc
new file mode 100644
index 0000000..d5ab2ff
--- /dev/null
+++ b/runtime/vm/canonical_tables.cc
@@ -0,0 +1,74 @@
+// 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/canonical_tables.h"
+
+namespace dart {
+
+bool MetadataMapTraits::IsMatch(const Object& a, const Object& b) {
+  // In the absence of hot reload, this can just be an identity check. With
+  // reload, some old program elements may be retained by the stack, closures
+  // or mirrors, which are absent from the new version of the library's
+  // metadata table. This name-based matching fuzzy maps the old program
+  // elements to corresponding new elements, preserving the behavior of the old
+  // metaname+fields scheme.
+  if (a.IsLibrary() && b.IsLibrary()) {
+    const String& url_a = String::Handle(Library::Cast(a).url());
+    const String& url_b = String::Handle(Library::Cast(b).url());
+    return url_a.Equals(url_b);
+  } else if (a.IsClass() && b.IsClass()) {
+    const String& name_a = String::Handle(Class::Cast(a).Name());
+    const String& name_b = String::Handle(Class::Cast(b).Name());
+    return name_a.Equals(name_b);
+  } else if (a.IsFunction() && b.IsFunction()) {
+    const String& name_a = String::Handle(Function::Cast(a).name());
+    const String& name_b = String::Handle(Function::Cast(b).name());
+    if (!name_a.Equals(name_b)) {
+      return false;
+    }
+    const Object& owner_a = Object::Handle(Function::Cast(a).Owner());
+    const Object& owner_b = Object::Handle(Function::Cast(b).Owner());
+    return IsMatch(owner_a, owner_b);
+  } else if (a.IsField() && b.IsField()) {
+    const String& name_a = String::Handle(Field::Cast(a).name());
+    const String& name_b = String::Handle(Field::Cast(b).name());
+    if (!name_a.Equals(name_b)) {
+      return false;
+    }
+    const Object& owner_a = Object::Handle(Field::Cast(a).Owner());
+    const Object& owner_b = Object::Handle(Field::Cast(b).Owner());
+    return IsMatch(owner_a, owner_b);
+  } else if (a.IsTypeParameter() && b.IsTypeParameter()) {
+    const String& name_a = String::Handle(TypeParameter::Cast(a).name());
+    const String& name_b = String::Handle(TypeParameter::Cast(b).name());
+    if (!name_a.Equals(name_b)) {
+      return false;
+    }
+    const Object& owner_a = Object::Handle(TypeParameter::Cast(a).Owner());
+    const Object& owner_b = Object::Handle(TypeParameter::Cast(b).Owner());
+    return IsMatch(owner_a, owner_b);
+  }
+  return a.raw() == b.raw();
+}
+
+uword MetadataMapTraits::Hash(const Object& key) {
+  if (key.IsLibrary()) {
+    return String::Hash(Library::Cast(key).url());
+  } else if (key.IsClass()) {
+    return String::Hash(Class::Cast(key).Name());
+  } else if (key.IsFunction()) {
+    return CombineHashes(String::Hash(Function::Cast(key).name()),
+                         Hash(Object::Handle(Function::Cast(key).Owner())));
+  } else if (key.IsField()) {
+    return CombineHashes(String::Hash(Field::Cast(key).name()),
+                         Hash(Object::Handle(Field::Cast(key).Owner())));
+  } else if (key.IsTypeParameter()) {
+    return TypeParameter::Cast(key).Hash();
+  } else if (key.IsNamespace()) {
+    return Hash(Library::Handle(Namespace::Cast(key).target()));
+  }
+  UNREACHABLE();
+}
+
+}  // namespace dart
diff --git a/runtime/vm/canonical_tables.h b/runtime/vm/canonical_tables.h
index 69b79eb..f0c3d2b 100644
--- a/runtime/vm/canonical_tables.h
+++ b/runtime/vm/canonical_tables.h
@@ -254,6 +254,15 @@
 typedef UnorderedHashSet<CanonicalTypeArgumentsTraits>
     CanonicalTypeArgumentsSet;
 
+class MetadataMapTraits {
+ public:
+  static const char* Name() { return "MetadataMapTraits"; }
+  static bool ReportStats() { return false; }
+  static bool IsMatch(const Object& a, const Object& b);
+  static uword Hash(const Object& key);
+};
+typedef UnorderedHashMap<MetadataMapTraits> MetadataMap;
+
 }  // namespace dart
 
 #endif  // RUNTIME_VM_CANONICAL_TABLES_H_
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index 023f790..895f537 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -5837,7 +5837,12 @@
   WriteUnsigned(num_objects);
   WriteUnsigned(canonical_clusters.length());
   WriteUnsigned(clusters.length());
-  WriteUnsigned(initial_field_table_->NumFieldIds());
+  // TODO(dartbug.com/36097): Not every snapshot carries the field table.
+  if (current_loading_unit_id_ <= LoadingUnit::kRootId) {
+    WriteUnsigned(initial_field_table_->NumFieldIds());
+  } else {
+    WriteUnsigned(0);
+  }
 
   for (SerializationCluster* cluster : canonical_clusters) {
     cluster->WriteAndMeasureAlloc(this);
@@ -6573,8 +6578,8 @@
   refs_ = Array::New(num_objects_ + kFirstReference, Heap::kOld);
   if (initial_field_table_len > 0) {
     initial_field_table_->AllocateIndex(initial_field_table_len - 1);
+    ASSERT_EQUAL(initial_field_table_->NumFieldIds(), initial_field_table_len);
   }
-  ASSERT_EQUAL(initial_field_table_->NumFieldIds(), initial_field_table_len);
 
   {
     NoSafepointScope no_safepoint;
diff --git a/runtime/vm/compiler/aot/precompiler.cc b/runtime/vm/compiler/aot/precompiler.cc
index 61cd91d..7a8f7b8 100644
--- a/runtime/vm/compiler/aot/precompiler.cc
+++ b/runtime/vm/compiler/aot/precompiler.cc
@@ -2117,47 +2117,9 @@
 
 void Precompiler::DropMetadata() {
   Library& lib = Library::Handle(Z);
-  const GrowableObjectArray& null_growable_list =
-      GrowableObjectArray::Handle(Z);
-  Array& dependencies = Array::Handle(Z);
-  Namespace& ns = Namespace::Handle(Z);
-  const Field& null_field = Field::Handle(Z);
-  GrowableObjectArray& metadata = GrowableObjectArray::Handle(Z);
-  Field& metadata_field = Field::Handle(Z);
-
   for (intptr_t i = 0; i < libraries_.Length(); i++) {
     lib ^= libraries_.At(i);
-    metadata ^= lib.metadata();
-    for (intptr_t j = 0; j < metadata.Length(); j++) {
-      metadata_field ^= metadata.At(j);
-      if (metadata_field.is_static()) {
-        // Although this field will become garbage after clearing the list
-        // below, we also need to clear its value from the field table.
-        // The value may be an instance of an otherwise dead class, and if
-        // it remains in the field table we can get an instance on the heap
-        // with a deleted class.
-        metadata_field.SetStaticValue(Object::null_instance(),
-                                      /*save_initial_value=*/true);
-      }
-    }
-
-    lib.set_metadata(null_growable_list);
-
-    dependencies = lib.imports();
-    for (intptr_t j = 0; j < dependencies.Length(); j++) {
-      ns ^= dependencies.At(j);
-      if (!ns.IsNull()) {
-        ns.set_metadata_field(null_field);
-      }
-    }
-
-    dependencies = lib.exports();
-    for (intptr_t j = 0; j < dependencies.Length(); j++) {
-      ns ^= dependencies.At(j);
-      if (!ns.IsNull()) {
-        ns.set_metadata_field(null_field);
-      }
-    }
+    lib.set_metadata(Array::null_array());
   }
 }
 
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 49c32bc..2071a31 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -6013,7 +6013,7 @@
     for (intptr_t j = 0; j < imports.Length(); j++) {
       ns ^= imports.At(j);
       if (ns.IsNull()) continue;
-      importee = ns.library();
+      importee = ns.target();
       importee_uri = importee.url();
       if (importee_uri.StartsWith(scheme_vm)) {
         result.Add(importer);
diff --git a/runtime/vm/isolate_reload.cc b/runtime/vm/isolate_reload.cc
index a9e660f..8e586b3 100644
--- a/runtime/vm/isolate_reload.cc
+++ b/runtime/vm/isolate_reload.cc
@@ -961,7 +961,7 @@
     for (intptr_t import_idx = 0; import_idx < ports.Length(); import_idx++) {
       ns ^= ports.At(import_idx);
       if (!ns.IsNull()) {
-        target = ns.library();
+        target = ns.target();
         target_url = target.url();
         if (!target_url.StartsWith(Symbols::DartExtensionScheme())) {
           (*imported_by)[target.index()]->Add(lib.index());
@@ -974,7 +974,7 @@
     for (intptr_t export_idx = 0; export_idx < ports.Length(); export_idx++) {
       ns ^= ports.At(export_idx);
       if (!ns.IsNull()) {
-        target = ns.library();
+        target = ns.target();
         (*imported_by)[target.index()]->Add(lib.index());
       }
     }
@@ -992,7 +992,7 @@
              import_idx++) {
           ns ^= ports.At(import_idx);
           if (!ns.IsNull()) {
-            target = ns.library();
+            target = ns.target();
             (*imported_by)[target.index()]->Add(lib.index());
           }
         }
diff --git a/runtime/vm/kernel.cc b/runtime/vm/kernel.cc
index 7c53e72..952e41a 100644
--- a/runtime/vm/kernel.cc
+++ b/runtime/vm/kernel.cc
@@ -430,26 +430,28 @@
   DISALLOW_COPY_AND_ASSIGN(MetadataEvaluator);
 };
 
-ObjectPtr EvaluateMetadata(const Field& metadata_field,
+ObjectPtr EvaluateMetadata(const Library& library,
+                           intptr_t kernel_offset,
                            bool is_annotations_offset) {
   LongJumpScope jump;
   if (setjmp(*jump.Set()) == 0) {
     Thread* thread = Thread::Current();
     Zone* zone = thread->zone();
     TranslationHelper helper(thread);
-    Script& script = Script::Handle(zone, metadata_field.Script());
+    Script& script = Script::Handle(
+        zone, Class::Handle(zone, library.toplevel_class()).script());
     helper.InitFromScript(script);
 
-    const Class& owner_class = Class::Handle(zone, metadata_field.Owner());
+    const Class& owner_class = Class::Handle(zone, library.toplevel_class());
     ActiveClass active_class;
     ActiveClassScope active_class_scope(&active_class, &owner_class);
 
     MetadataEvaluator metadata_evaluator(
         zone, &helper, script,
-        ExternalTypedData::Handle(zone, metadata_field.KernelData()),
-        metadata_field.KernelDataProgramOffset(), &active_class);
+        ExternalTypedData::Handle(zone, library.kernel_data()),
+        library.kernel_offset(), &active_class);
 
-    return metadata_evaluator.EvaluateMetadata(metadata_field.kernel_offset(),
+    return metadata_evaluator.EvaluateMetadata(kernel_offset,
                                                is_annotations_offset);
 
   } else {
diff --git a/runtime/vm/kernel.h b/runtime/vm/kernel.h
index 9b0d8de..ed66e25 100644
--- a/runtime/vm/kernel.h
+++ b/runtime/vm/kernel.h
@@ -196,7 +196,8 @@
 void CollectTokenPositionsFor(const Script& script);
 
 ObjectPtr EvaluateStaticConstFieldInitializer(const Field& field);
-ObjectPtr EvaluateMetadata(const Field& metadata_field,
+ObjectPtr EvaluateMetadata(const Library& library,
+                           intptr_t kernel_offset,
                            bool is_annotations_offset);
 ObjectPtr BuildParameterDescriptor(const Function& function);
 
diff --git a/runtime/vm/kernel_loader.cc b/runtime/vm/kernel_loader.cc
index e37f325..4dbcfb6 100644
--- a/runtime/vm/kernel_loader.cc
+++ b/runtime/vm/kernel_loader.cc
@@ -694,7 +694,7 @@
       // Dart_GetImportsOfScheme('dart-ext').
       const auto& native_library = Library::Handle(Library::New(uri_path));
       library.AddImport(Namespace::Handle(Namespace::New(
-          native_library, Array::null_array(), Array::null_array())));
+          native_library, Array::null_array(), Array::null_array(), library)));
     }
   }
 }
@@ -1122,8 +1122,7 @@
 
   if (FLAG_enable_mirrors && annotation_count > 0) {
     ASSERT(annotations_kernel_offset > 0);
-    library.AddLibraryMetadata(toplevel_class, TokenPosition::kNoSource,
-                               annotations_kernel_offset);
+    library.AddMetadata(library, annotations_kernel_offset);
   }
 
   if (register_class) {
@@ -1248,7 +1247,7 @@
 
     if ((FLAG_enable_mirrors || has_pragma_annotation) &&
         annotation_count > 0) {
-      library.AddFieldMetadata(field, TokenPosition::kNoSource, field_offset);
+      library.AddMetadata(field, field_offset);
     }
     fields_.Add(&field);
   }
@@ -1366,7 +1365,7 @@
           "import of dart:ffi is not supported in the current Dart runtime");
     }
     String& prefix = H.DartSymbolPlain(dependency_helper.name_index_);
-    ns = Namespace::New(target_library, show_names, hide_names);
+    ns = Namespace::New(target_library, show_names, hide_names, *library);
     if ((dependency_helper.flags_ & LibraryDependencyHelper::Export) != 0) {
       library->AddExport(ns);
     } else {
@@ -1389,8 +1388,7 @@
 
     if (FLAG_enable_mirrors && dependency_helper.annotation_count_ > 0) {
       ASSERT(annotations_kernel_offset > 0);
-      ns.AddMetadata(toplevel_class, TokenPosition::kNoSource,
-                     annotations_kernel_offset);
+      library->AddMetadata(ns, annotations_kernel_offset);
     }
 
     if (prefix.IsNull()) {
@@ -1511,9 +1509,7 @@
   }
 
   if ((FLAG_enable_mirrors || has_pragma_annotation) && annotation_count > 0) {
-    library.AddClassMetadata(*out_class, toplevel_class,
-                             TokenPosition::kNoSource,
-                             class_offset - correction_offset_);
+    library.AddMetadata(*out_class, class_offset - correction_offset_);
   }
 
   // We do not register expression evaluation classes with the VM:
@@ -1625,7 +1621,7 @@
       }
       if ((FLAG_enable_mirrors || has_pragma_annotation) &&
           annotation_count > 0) {
-        library.AddFieldMetadata(field, TokenPosition::kNoSource, field_offset);
+        library.AddMetadata(field, field_offset);
       }
       fields_.Add(&field);
     }
@@ -1724,8 +1720,7 @@
 
     if ((FLAG_enable_mirrors || has_pragma_annotation) &&
         annotation_count > 0) {
-      library.AddFunctionMetadata(function, TokenPosition::kNoSource,
-                                  constructor_offset);
+      library.AddMetadata(function, constructor_offset);
     }
   }
 
@@ -2049,8 +2044,7 @@
   helper_.SetOffset(procedure_end);
 
   if (annotation_count > 0) {
-    library.AddFunctionMetadata(function, TokenPosition::kNoSource,
-                                procedure_offset);
+    library.AddMetadata(function, procedure_offset);
   }
 
   if (has_pragma_annotation) {
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 9352916..888819c 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -11609,175 +11609,25 @@
   StoreNonPointer(&raw_ptr()->load_state_, LibraryLayout::kLoaded);
 }
 
-static StringPtr MakeClassMetaName(Thread* thread,
-                                   Zone* zone,
-                                   const Class& cls) {
-  return Symbols::FromConcat(thread, Symbols::At(),
-                             String::Handle(zone, cls.Name()));
-}
-
-static StringPtr MakeFieldMetaName(Thread* thread,
-                                   Zone* zone,
-                                   const Field& field) {
-  const String& cname = String::Handle(
-      zone,
-      MakeClassMetaName(thread, zone, Class::Handle(zone, field.Origin())));
-  GrowableHandlePtrArray<const String> pieces(zone, 3);
-  pieces.Add(cname);
-  pieces.Add(Symbols::At());
-  pieces.Add(String::Handle(zone, field.name()));
-  return Symbols::FromConcatAll(thread, pieces);
-}
-
-static StringPtr MakeFunctionMetaName(Thread* thread,
-                                      Zone* zone,
-                                      const Function& func) {
-  const String& cname = String::Handle(
-      zone,
-      MakeClassMetaName(thread, zone, Class::Handle(zone, func.origin())));
-  GrowableHandlePtrArray<const String> pieces(zone, 3);
-  pieces.Add(cname);
-  pieces.Add(Symbols::At());
-  pieces.Add(String::Handle(zone, func.name()));
-  return Symbols::FromConcatAll(thread, pieces);
-}
-
-static StringPtr MakeTypeParameterMetaName(Thread* thread,
-                                           Zone* zone,
-                                           const TypeParameter& param) {
-  const String& cname = String::Handle(
-      zone,
-      MakeClassMetaName(thread, zone,
-                        Class::Handle(zone, param.parameterized_class())));
-  GrowableHandlePtrArray<const String> pieces(zone, 3);
-  pieces.Add(cname);
-  pieces.Add(Symbols::At());
-  pieces.Add(String::Handle(zone, param.name()));
-  return Symbols::FromConcatAll(thread, pieces);
-}
-
-void Library::AddMetadata(const Object& owner,
-                          const String& name,
-                          TokenPosition token_pos,
+void Library::AddMetadata(const Object& declaration,
                           intptr_t kernel_offset) const {
 #if defined(DART_PRECOMPILED_RUNTIME)
   UNREACHABLE();
 #else
-  Thread* thread = Thread::Current();
-  ASSERT(thread->IsMutatorThread());
-  Zone* zone = thread->zone();
-  const String& metaname = String::Handle(zone, Symbols::New(thread, name));
-  const Field& field =
-      Field::Handle(zone, Field::NewTopLevel(metaname,
-                                             false,  // is_final
-                                             false,  // is_const
-                                             false,  // is_late
-                                             owner, token_pos, token_pos));
-  field.SetFieldType(Object::dynamic_type());
-  field.set_is_reflectable(false);
-  field.set_kernel_offset(kernel_offset);
-  thread->isolate_group()->RegisterStaticField(field, Array::empty_array());
-
-  GrowableObjectArray& metadata =
-      GrowableObjectArray::Handle(zone, this->metadata());
-  metadata.Add(field, Heap::kOld);
+  MetadataMap map(metadata());
+  map.UpdateOrInsert(declaration, Smi::Handle(Smi::New(kernel_offset)));
+  set_metadata(map.Release());
 #endif  // defined(DART_PRECOMPILED_RUNTIME)
 }
 
-void Library::AddClassMetadata(const Class& cls,
-                               const Object& tl_owner,
-                               TokenPosition token_pos,
-                               intptr_t kernel_offset) const {
-  Thread* thread = Thread::Current();
-  Zone* zone = thread->zone();
-  // We use the toplevel class as the owner of a class's metadata field because
-  // a class's metadata is in scope of the library, not the class.
-  AddMetadata(tl_owner,
-              String::Handle(zone, MakeClassMetaName(thread, zone, cls)),
-              token_pos, kernel_offset);
-}
-
-void Library::AddFieldMetadata(const Field& field,
-                               TokenPosition token_pos,
-                               intptr_t kernel_offset) const {
-  Thread* thread = Thread::Current();
-  Zone* zone = thread->zone();
-  const auto& owner = Object::Handle(zone, field.RawOwner());
-  const auto& name =
-      String::Handle(zone, MakeFieldMetaName(thread, zone, field));
-  AddMetadata(owner, name, token_pos, kernel_offset);
-}
-
-void Library::AddFunctionMetadata(const Function& func,
-                                  TokenPosition token_pos,
-                                  intptr_t kernel_offset) const {
-  Thread* thread = Thread::Current();
-  Zone* zone = thread->zone();
-  const auto& owner = Object::Handle(zone, func.RawOwner());
-  const auto& name =
-      String::Handle(zone, MakeFunctionMetaName(thread, zone, func));
-  AddMetadata(owner, name, token_pos, kernel_offset);
-}
-
-void Library::AddTypeParameterMetadata(const TypeParameter& param,
-                                       TokenPosition token_pos) const {
-  Thread* thread = Thread::Current();
-  Zone* zone = thread->zone();
-  const auto& owner = Class::Handle(zone, param.parameterized_class());
-  const auto& name =
-      String::Handle(zone, MakeTypeParameterMetaName(thread, zone, param));
-  AddMetadata(owner, name, token_pos, 0);
-}
-
-void Library::AddLibraryMetadata(const Object& tl_owner,
-                                 TokenPosition token_pos,
-                                 intptr_t kernel_offset) const {
-  AddMetadata(tl_owner, Symbols::TopLevel(), token_pos, kernel_offset);
-}
-
-StringPtr Library::MakeMetadataName(const Object& obj) const {
-  Thread* thread = Thread::Current();
-  Zone* zone = thread->zone();
-  if (obj.IsClass()) {
-    return MakeClassMetaName(thread, zone, Class::Cast(obj));
-  } else if (obj.IsField()) {
-    return MakeFieldMetaName(thread, zone, Field::Cast(obj));
-  } else if (obj.IsFunction()) {
-    return MakeFunctionMetaName(thread, zone, Function::Cast(obj));
-  } else if (obj.IsLibrary()) {
-    return Symbols::TopLevel().raw();
-  } else if (obj.IsTypeParameter()) {
-    return MakeTypeParameterMetaName(thread, zone, TypeParameter::Cast(obj));
-  }
-  UNIMPLEMENTED();
-  return String::null();
-}
-
-FieldPtr Library::GetMetadataField(const String& metaname) const {
-  const GrowableObjectArray& metadata =
-      GrowableObjectArray::Handle(this->metadata());
-  Field& entry = Field::Handle();
-  String& entryname = String::Handle();
-  intptr_t num_entries = metadata.Length();
-  for (intptr_t i = 0; i < num_entries; i++) {
-    entry ^= metadata.At(i);
-    entryname = entry.name();
-    if (entryname.Equals(metaname)) {
-      return entry.raw();
-    }
-  }
-  return Field::null();
-}
-
-ObjectPtr Library::GetMetadata(const Object& obj) const {
+ObjectPtr Library::GetMetadata(const Object& declaration) const {
 #if defined(DART_PRECOMPILED_RUNTIME)
   return Object::empty_array().raw();
 #else
-  if (!obj.IsClass() && !obj.IsField() && !obj.IsFunction() &&
-      !obj.IsLibrary() && !obj.IsTypeParameter()) {
-    UNREACHABLE();
-  }
-  if (obj.IsLibrary()) {
+  RELEASE_ASSERT(declaration.IsClass() || declaration.IsField() ||
+                 declaration.IsFunction() || declaration.IsLibrary() ||
+                 declaration.IsTypeParameter() || declaration.IsNamespace());
+  if (declaration.IsLibrary()) {
     // Ensure top-level class is loaded as it may contain annotations of
     // a library.
     const auto& cls = Class::Handle(toplevel_class());
@@ -11785,31 +11635,36 @@
       cls.EnsureDeclarationLoaded();
     }
   }
-  const String& metaname = String::Handle(MakeMetadataName(obj));
-  Field& field = Field::Handle(GetMetadataField(metaname));
-  if (field.IsNull()) {
+  Object& value = Object::Handle();
+  {
+    MetadataMap map(metadata());
+    value = map.GetOrNull(declaration);
+    set_metadata(map.Release());
+  }
+  if (value.IsNull()) {
     // There is no metadata for this object.
     return Object::empty_array().raw();
   }
-  Object& metadata = Object::Handle(field.StaticValue());
-  if (metadata.raw() == Object::empty_array().raw()) {
-    ASSERT(field.kernel_offset() > 0);
-    metadata = kernel::EvaluateMetadata(
-        field, /* is_annotations_offset = */ obj.IsLibrary());
-    if (metadata.IsArray() || metadata.IsNull()) {
-      ASSERT(metadata.raw() != Object::empty_array().raw());
-      if (!Compiler::IsBackgroundCompilation()) {
-        field.SetStaticValue(
-            metadata.IsNull() ? Object::null_array() : Array::Cast(metadata),
-            true);
-      }
+  if (!value.IsSmi()) {
+    // Metadata is already evaluated.
+    ASSERT(value.IsArray());
+    return value.raw();
+  }
+  intptr_t kernel_offset = Smi::Cast(value).Value();
+  ASSERT(kernel_offset > 0);
+  value = kernel::EvaluateMetadata(
+      *this, kernel_offset,
+      /* is_annotations_offset = */ declaration.IsLibrary() ||
+          declaration.IsNamespace());
+  if (value.IsArray() || value.IsNull()) {
+    ASSERT(value.raw() != Object::empty_array().raw());
+    if (!Compiler::IsBackgroundCompilation()) {
+      MetadataMap map(metadata());
+      map.UpdateOrInsert(declaration, value);
+      set_metadata(map.Release());
     }
   }
-  if (metadata.IsNull()) {
-    // Metadata field exists in order to reference extended metadata.
-    return Object::empty_array().raw();
-  }
-  return metadata.raw();
+  return value.raw();
 #endif  // defined(DART_PRECOMPILED_RUNTIME)
 }
 
@@ -12356,7 +12211,7 @@
     import = ImportAt(i);
     obj = import.Lookup(name);
     if (!obj.IsNull()) {
-      import_lib = import.library();
+      import_lib = import.target();
       import_lib_url = import_lib.url();
       if (found_obj.raw() != obj.raw()) {
         if (first_import_lib_url.IsNull() ||
@@ -12480,7 +12335,7 @@
   raw_ptr()->set_dependencies(deps.raw());
 }
 
-void Library::set_metadata(const GrowableObjectArray& value) const {
+void Library::set_metadata(const Array& value) const {
   raw_ptr()->set_metadata(value.raw());
 }
 
@@ -12489,7 +12344,7 @@
   if (import.IsNull()) {
     return Library::null();
   }
-  return import.library();
+  return import.target();
 }
 
 NamespacePtr Library::ImportAt(intptr_t index) const {
@@ -12511,7 +12366,7 @@
   for (int i = 0; i < imports.Length(); ++i) {
     ns = Namespace::RawCast(imports.At(i));
     if (ns.IsNull()) continue;
-    lib = ns.library();
+    lib = ns.target();
     url = lib.url();
     if (url.StartsWith(Symbols::DartExtensionScheme())) {
       native_import_count++;
@@ -12522,7 +12377,7 @@
   for (int i = 0, j = 0; i < imports.Length(); ++i) {
     ns = Namespace::RawCast(imports.At(i));
     if (ns.IsNull()) continue;
-    lib = ns.library();
+    lib = ns.target();
     url = lib.url();
     if (url.StartsWith(Symbols::DartExtensionScheme())) {
       new_imports.SetAt(j++, ns);
@@ -12640,10 +12495,11 @@
   result.raw_ptr()->set_resolved_names(Array::null());
   result.raw_ptr()->set_exported_names(Array::null());
   result.raw_ptr()->set_dictionary(Object::empty_array().raw());
-  GrowableObjectArray& list = GrowableObjectArray::Handle(zone);
-  list = GrowableObjectArray::New(4, Heap::kOld);
-  result.raw_ptr()->set_metadata(list.raw());
+  Array& array = Array::Handle(zone);
+  array = HashTables::New<MetadataMap>(4, Heap::kOld);
+  result.raw_ptr()->set_metadata(array.raw());
   result.raw_ptr()->set_toplevel_class(Class::null());
+  GrowableObjectArray& list = GrowableObjectArray::Handle(zone);
   list = GrowableObjectArray::New(Object::empty_array(), Heap::kOld);
   result.raw_ptr()->set_used_scripts(list.raw());
   result.raw_ptr()->set_imports(Object::empty_array().raw());
@@ -12673,9 +12529,9 @@
   if (import_core_lib) {
     const Library& core_lib = Library::Handle(zone, Library::CoreLibrary());
     ASSERT(!core_lib.IsNull());
-    const Namespace& ns = Namespace::Handle(
-        zone,
-        Namespace::New(core_lib, Object::null_array(), Object::null_array()));
+    const Namespace& ns =
+        Namespace::Handle(zone, Namespace::New(core_lib, Object::null_array(),
+                                               Object::null_array(), result));
     result.AddImport(ns);
   }
   return result.raw();
@@ -13300,7 +13156,7 @@
     const Array& imports = Array::Handle(this->imports());
     Namespace& import = Namespace::Handle();
     import ^= imports.At(index);
-    return import.library();
+    return import.target();
   }
   return Library::null();
 }
@@ -13370,59 +13226,8 @@
   return prefix.ToCString();
 }
 
-void Namespace::set_metadata_field(const Field& value) const {
-  raw_ptr()->set_metadata_field(value.raw());
-}
-
-void Namespace::AddMetadata(const Object& owner,
-                            TokenPosition token_pos,
-                            intptr_t kernel_offset) {
-  auto thread = Thread::Current();
-  auto zone = thread->zone();
-  auto isolate_group = thread->isolate_group();
-  ASSERT(Field::Handle(zone, metadata_field()).IsNull());
-  Field& field =
-      Field::Handle(zone, Field::NewTopLevel(Symbols::TopLevel(),
-                                             false,  // is_final
-                                             false,  // is_const
-                                             false,  // is_late
-                                             owner, token_pos, token_pos));
-  field.set_is_reflectable(false);
-  field.SetFieldType(Object::dynamic_type());
-  field.set_kernel_offset(kernel_offset);
-  isolate_group->RegisterStaticField(field, Array::empty_array());
-  set_metadata_field(field);
-}
-
-ObjectPtr Namespace::GetMetadata() const {
-#if defined(DART_PRECOMPILED_RUNTIME)
-  return Object::empty_array().raw();
-#else
-  Field& field = Field::Handle(metadata_field());
-  if (field.IsNull()) {
-    // There is no metadata for this object.
-    return Object::empty_array().raw();
-  }
-  Object& metadata = Object::Handle();
-  metadata = field.StaticValue();
-  if (field.StaticValue() == Object::empty_array().raw()) {
-    if (field.kernel_offset() > 0) {
-      metadata =
-          kernel::EvaluateMetadata(field, /* is_annotations_offset = */ true);
-    } else {
-      UNREACHABLE();
-    }
-    if (metadata.IsArray()) {
-      ASSERT(Array::Cast(metadata).raw() != Object::empty_array().raw());
-      field.SetStaticValue(Array::Cast(metadata), true);
-    }
-  }
-  return metadata.raw();
-#endif  // defined(DART_PRECOMPILED_RUNTIME)
-}
-
 const char* Namespace::ToCString() const {
-  const Library& lib = Library::Handle(library());
+  const Library& lib = Library::Handle(target());
   return OS::SCreate(Thread::Current()->zone(), "Namespace for library '%s'",
                      lib.ToCString());
 }
@@ -13476,7 +13281,7 @@
 ObjectPtr Namespace::Lookup(const String& name,
                             ZoneGrowableArray<intptr_t>* trail) const {
   Zone* zone = Thread::Current()->zone();
-  const Library& lib = Library::Handle(zone, library());
+  const Library& lib = Library::Handle(zone, target());
 
   if (trail != NULL) {
     // Look for cycle in reexport graph.
@@ -13537,15 +13342,17 @@
   return static_cast<NamespacePtr>(raw);
 }
 
-NamespacePtr Namespace::New(const Library& library,
+NamespacePtr Namespace::New(const Library& target,
                             const Array& show_names,
-                            const Array& hide_names) {
+                            const Array& hide_names,
+                            const Library& owner) {
   ASSERT(show_names.IsNull() || (show_names.Length() > 0));
   ASSERT(hide_names.IsNull() || (hide_names.Length() > 0));
   const Namespace& result = Namespace::Handle(Namespace::New());
-  result.raw_ptr()->set_library(library.raw());
+  result.raw_ptr()->set_target(target.raw());
   result.raw_ptr()->set_show_names(show_names.raw());
   result.raw_ptr()->set_hide_names(hide_names.raw());
+  result.raw_ptr()->set_owner(owner.raw());
   return result.raw();
 }
 
@@ -25089,9 +24896,13 @@
   const Library& lib = Library::Handle(cls.library());
   switch (kind()) {
     case FunctionLayout::kRegularFunction:
-    case FunctionLayout::kImplicitClosureFunction:
       return dart::VerifyEntryPoint(lib, *this, *this,
                                     {EntryPointPragma::kGetterOnly});
+    case FunctionLayout::kImplicitClosureFunction: {
+      const Function& parent = Function::Handle(parent_function());
+      return dart::VerifyEntryPoint(lib, parent, parent,
+                                    {EntryPointPragma::kGetterOnly});
+    }
     default:
       UNREACHABLE();
   }
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index b729ba3..89bbf00 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -4738,22 +4738,8 @@
 
   void AddExport(const Namespace& ns) const;
 
-  void AddClassMetadata(const Class& cls,
-                        const Object& tl_owner,
-                        TokenPosition token_pos,
-                        intptr_t kernel_offset) const;
-  void AddFieldMetadata(const Field& field,
-                        TokenPosition token_pos,
-                        intptr_t kernel_offset) const;
-  void AddFunctionMetadata(const Function& func,
-                           TokenPosition token_pos,
-                           intptr_t kernel_offset) const;
-  void AddLibraryMetadata(const Object& tl_owner,
-                          TokenPosition token_pos,
-                          intptr_t kernel_offset) const;
-  void AddTypeParameterMetadata(const TypeParameter& param,
-                                TokenPosition token_pos) const;
-  ObjectPtr GetMetadata(const Object& obj) const;
+  void AddMetadata(const Object& declaration, intptr_t kernel_offset) const;
+  ObjectPtr GetMetadata(const Object& declaration) const;
 
   // Tries to finds a @pragma annotation on [object].
   //
@@ -4978,8 +4964,8 @@
   void set_flags(uint8_t flags) const;
   bool HasExports() const;
   ArrayPtr loaded_scripts() const { return raw_ptr()->loaded_scripts(); }
-  GrowableObjectArrayPtr metadata() const { return raw_ptr()->metadata(); }
-  void set_metadata(const GrowableObjectArray& value) const;
+  ArrayPtr metadata() const { return raw_ptr()->metadata(); }
+  void set_metadata(const Array& value) const;
   ArrayPtr dictionary() const { return raw_ptr()->dictionary(); }
   void InitClassDictionary() const;
 
@@ -5007,13 +4993,6 @@
 
   void AllocatePrivateKey() const;
 
-  StringPtr MakeMetadataName(const Object& obj) const;
-  FieldPtr GetMetadataField(const String& metaname) const;
-  void AddMetadata(const Object& owner,
-                   const String& name,
-                   TokenPosition token_pos,
-                   intptr_t kernel_offset) const;
-
   FINAL_HEAP_OBJECT_IMPLEMENTATION(Library, Object);
 
   friend class Bootstrap;
@@ -5031,14 +5010,10 @@
 // the show/hide combinators.
 class Namespace : public Object {
  public:
-  LibraryPtr library() const { return raw_ptr()->library(); }
+  LibraryPtr target() const { return raw_ptr()->target(); }
   ArrayPtr show_names() const { return raw_ptr()->show_names(); }
   ArrayPtr hide_names() const { return raw_ptr()->hide_names(); }
-
-  void AddMetadata(const Object& owner,
-                   TokenPosition token_pos,
-                   intptr_t kernel_offset = 0);
-  ObjectPtr GetMetadata() const;
+  LibraryPtr owner() const { return raw_ptr()->owner(); }
 
   static intptr_t InstanceSize() {
     return RoundedAllocationSize(sizeof(NamespaceLayout));
@@ -5050,14 +5025,12 @@
 
   static NamespacePtr New(const Library& library,
                           const Array& show_names,
-                          const Array& hide_names);
+                          const Array& hide_names,
+                          const Library& owner);
 
  private:
   static NamespacePtr New();
 
-  FieldPtr metadata_field() const { return raw_ptr()->metadata_field(); }
-  void set_metadata_field(const Field& value) const;
-
   FINAL_HEAP_OBJECT_IMPLEMENTATION(Namespace, Object);
   friend class Class;
   friend class Precompiler;
@@ -8265,6 +8238,13 @@
   bool IsFunctionTypeParameter() const {
     return parameterized_function() != Function::null();
   }
+  ObjectPtr Owner() const {
+    if (IsClassTypeParameter()) {
+      return parameterized_class();
+    } else {
+      return parameterized_function();
+    }
+  }
 
   static intptr_t parameterized_class_id_offset() {
     return OFFSET_OF(TypeParameterLayout, parameterized_class_id_);
diff --git a/runtime/vm/object_service.cc b/runtime/vm/object_service.cc
index 08bfa23..1dfe634 100644
--- a/runtime/vm/object_service.cc
+++ b/runtime/vm/object_service.cc
@@ -519,7 +519,7 @@
       jsdep.AddProperty("isDeferred", false);
       jsdep.AddProperty("isExport", false);
       jsdep.AddProperty("isImport", true);
-      target = ns.library();
+      target = ns.target();
       jsdep.AddProperty("target", target);
     }
 
@@ -533,7 +533,7 @@
       jsdep.AddProperty("isDeferred", false);
       jsdep.AddProperty("isExport", true);
       jsdep.AddProperty("isImport", false);
-      target = ns.library();
+      target = ns.target();
       jsdep.AddProperty("target", target);
     }
 
@@ -559,7 +559,7 @@
             prefix_name = prefix.name();
             ASSERT(!prefix_name.IsNull());
             jsdep.AddProperty("prefix", prefix_name.ToCString());
-            target = ns.library();
+            target = ns.target();
             jsdep.AddProperty("target", target);
           }
         }
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index bc02ac3..2e6a24f 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -1409,8 +1409,7 @@
   POINTER_FIELD(StringPtr, url)
   POINTER_FIELD(StringPtr, private_key)
   POINTER_FIELD(ArrayPtr, dictionary)  // Top-level names in this library.
-  POINTER_FIELD(GrowableObjectArrayPtr,
-                metadata)  // Metadata on classes, methods etc.
+  POINTER_FIELD(ArrayPtr, metadata)    // Metadata on classes, methods etc.
   POINTER_FIELD(ClassPtr,
                 toplevel_class)  // Class containing top-level elements.
   POINTER_FIELD(GrowableObjectArrayPtr, used_scripts)
@@ -1462,14 +1461,12 @@
 class NamespaceLayout : public ObjectLayout {
   RAW_HEAP_OBJECT_IMPLEMENTATION(Namespace);
 
-  VISIT_FROM(ObjectPtr, library)
-  POINTER_FIELD(LibraryPtr, library)   // library with name dictionary.
+  VISIT_FROM(ObjectPtr, target)
+  POINTER_FIELD(LibraryPtr, target)    // library with name dictionary.
   POINTER_FIELD(ArrayPtr, show_names)  // list of names that are exported.
   POINTER_FIELD(ArrayPtr, hide_names)  // list of names that are hidden.
-  POINTER_FIELD(FieldPtr,
-                metadata_field)  // remembers the token pos of metadata if any,
-                                 // and the metadata values if computed.
-  VISIT_TO(ObjectPtr, metadata_field)
+  POINTER_FIELD(LibraryPtr, owner)
+  VISIT_TO(ObjectPtr, owner)
   ObjectPtr* to_snapshot(Snapshot::Kind kind) { return to(); }
 };
 
diff --git a/runtime/vm/raw_object_fields.cc b/runtime/vm/raw_object_fields.cc
index cf5625b..103611e 100644
--- a/runtime/vm/raw_object_fields.cc
+++ b/runtime/vm/raw_object_fields.cc
@@ -75,10 +75,10 @@
   F(Library, resolved_names_)                                                  \
   F(Library, exported_names_)                                                  \
   F(Library, loaded_scripts_)                                                  \
-  F(Namespace, library_)                                                       \
+  F(Namespace, target_)                                                        \
   F(Namespace, show_names_)                                                    \
   F(Namespace, hide_names_)                                                    \
-  F(Namespace, metadata_field_)                                                \
+  F(Namespace, owner_)                                                         \
   F(KernelProgramInfo, string_offsets_)                                        \
   F(KernelProgramInfo, string_data_)                                           \
   F(KernelProgramInfo, canonical_names_)                                       \
diff --git a/runtime/vm/vm_sources.gni b/runtime/vm/vm_sources.gni
index 665d37b..0f71c57 100644
--- a/runtime/vm/vm_sources.gni
+++ b/runtime/vm/vm_sources.gni
@@ -21,6 +21,7 @@
   "bootstrap_natives.h",
   "bss_relocs.cc",
   "bss_relocs.h",
+  "canonical_tables.cc",
   "canonical_tables.h",
   "class_finalizer.cc",
   "class_finalizer.h",