Import Cobalt 16.140175

Change-Id: I1874a9229c64dffde87b3c3d5664ef915a6b9977
diff --git a/src/build/file_exists.py b/src/build/file_exists.py
new file mode 100644
index 0000000..9af5d79
--- /dev/null
+++ b/src/build/file_exists.py
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+# Copyright 2018 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Writes True if the argument is a file."""
+
+import os.path
+import sys
+
+def main():
+  sys.stdout.write(str(os.path.isfile(sys.argv[1])))
+  return 0
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/src/cobalt/audio/audio_buffer_source_node.cc b/src/cobalt/audio/audio_buffer_source_node.cc
index 49ba2ad..adbfc95 100644
--- a/src/cobalt/audio/audio_buffer_source_node.cc
+++ b/src/cobalt/audio/audio_buffer_source_node.cc
@@ -111,35 +111,38 @@
   size_t channels = static_cast<size_t>(buffer_->number_of_channels());
 
   if (sample_type == kSampleTypeFloat32) {
-    std::vector<float*> audio_buffer(channels, NULL);
+    std::vector<scoped_refptr<dom::Float32Array>> audio_buffer_storages(
+        channels);
+    std::vector<float*> audio_buffers(channels, NULL);
     for (size_t i = 0; i < channels; ++i) {
       scoped_refptr<dom::Float32Array> buffer_data =
           buffer_->GetChannelData(static_cast<uint32>(i), NULL);
-      scoped_refptr<dom::Float32Array> sub_array = buffer_data->Subarray(
+      audio_buffer_storages[i] = buffer_data->Subarray(
           NULL, read_index_, read_index_ + number_of_frames);
-      audio_buffer[i] = sub_array->data();
+      audio_buffers[i] = audio_buffer_storages[i]->data();
     }
 
     read_index_ += number_of_frames;
 
-    scoped_ptr<ShellAudioBus> audio_bus(
-        new ShellAudioBus(static_cast<size_t>(number_of_frames), audio_buffer));
+    scoped_ptr<ShellAudioBus> audio_bus(new ShellAudioBus(
+        static_cast<size_t>(number_of_frames), audio_buffers));
 
     return audio_bus.Pass();
   } else if (sample_type == kSampleTypeInt16) {
-    std::vector<int16*> audio_buffer(channels, NULL);
+    std::vector<scoped_refptr<dom::Int16Array>> audio_buffer_storages(channels);
+    std::vector<int16*> audio_buffers(channels, NULL);
     for (size_t i = 0; i < channels; ++i) {
       scoped_refptr<dom::Int16Array> buffer_data =
           buffer_->GetChannelDataInt16(static_cast<uint32>(i), NULL);
-      scoped_refptr<dom::Int16Array> sub_array = buffer_data->Subarray(
+      audio_buffer_storages[i] = buffer_data->Subarray(
           NULL, read_index_, read_index_ + number_of_frames);
-      audio_buffer[i] = sub_array->data();
+      audio_buffers[i] = audio_buffer_storages[i]->data();
     }
 
     read_index_ += number_of_frames;
 
-    scoped_ptr<ShellAudioBus> audio_bus(
-        new ShellAudioBus(static_cast<size_t>(number_of_frames), audio_buffer));
+    scoped_ptr<ShellAudioBus> audio_bus(new ShellAudioBus(
+        static_cast<size_t>(number_of_frames), audio_buffers));
 
     return audio_bus.Pass();
   }
diff --git a/src/cobalt/audio/audio_device.cc b/src/cobalt/audio/audio_device.cc
index 28d1bce..53d041d 100644
--- a/src/cobalt/audio/audio_device.cc
+++ b/src/cobalt/audio/audio_device.cc
@@ -170,14 +170,14 @@
   *frames_in_buffer = static_cast<int>(frames_rendered_ - frames_consumed_);
 
   if ((kFramesPerChannel - *frames_in_buffer) >= kRenderBufferSizeFrames) {
-    bool silence = false;
-
     // If there was silence last time we were called, then the buffer has
     // already been zeroed out and we don't need to do it again.
     if (!was_silence_last_update_) {
       input_audio_bus_.ZeroAllFrames();
     }
 
+    bool silence = true;
+
     // Fill our temporary buffer with planar PCM float samples.
     render_callback_->FillAudioBus(&input_audio_bus_, &silence);
 
@@ -345,7 +345,7 @@
 
   if ((kFramesPerChannel - *total_frames) >= kRenderBufferSizeFrames) {
     // Fill our temporary buffer with PCM float samples.
-    bool silence = false;
+    bool silence = true;
     render_callback_->FillAudioBus(&audio_bus_, &silence);
 
     if (!silence) {
diff --git a/src/cobalt/audio/audio_device.h b/src/cobalt/audio/audio_device.h
index fec6cea..dd5f898 100644
--- a/src/cobalt/audio/audio_device.h
+++ b/src/cobalt/audio/audio_device.h
@@ -38,6 +38,10 @@
     typedef ::media::ShellAudioBus ShellAudioBus;
 #endif  // defined(COBALT_MEDIA_SOURCE_2016)
 
+    // |silence| will be set to true before calling if |audio_buffer| contains
+    // only silence samples, it will be set to |false| otherwise.  On return
+    // FillAudioBus() will set |silence| to |false| if it has modified
+    // |audio_buffer|.
     virtual void FillAudioBus(ShellAudioBus* audio_buffer, bool* silence) = 0;
 
    protected:
diff --git a/src/cobalt/audio/audio_node_input.cc b/src/cobalt/audio/audio_node_input.cc
index 2e76dcf..529986d 100644
--- a/src/cobalt/audio/audio_node_input.cc
+++ b/src/cobalt/audio/audio_node_input.cc
@@ -231,8 +231,6 @@
   // This is called by Audio thread.
   owner_node_->audio_lock()->AssertLocked();
 
-  *silence = true;
-
   // TODO: Consider computing computedNumberOfChannels and do up-mix or
   // down-mix base on computedNumberOfChannels. The current implementation
   // is based on the fact that the channelCountMode is max.
@@ -251,8 +249,12 @@
         output_audio_bus->sample_type());
 
     if (audio_bus) {
-      MixAudioBuffer(owner_node_->channel_interpretation(), audio_bus.get(),
-                     output_audio_bus);
+      if (*silence && audio_bus->channels() == output_audio_bus->channels()) {
+        output_audio_bus->Assign(*audio_bus);
+      } else {
+        MixAudioBuffer(owner_node_->channel_interpretation(), audio_bus.get(),
+                       output_audio_bus);
+      }
       *silence = false;
     }
   }
diff --git a/src/cobalt/base/accessibility_caption_settings_changed_event.h b/src/cobalt/base/accessibility_caption_settings_changed_event.h
new file mode 100644
index 0000000..d0c0a69
--- /dev/null
+++ b/src/cobalt/base/accessibility_caption_settings_changed_event.h
@@ -0,0 +1,35 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_BASE_ACCESSIBILITY_CAPTION_SETTINGS_CHANGED_EVENT_H_
+#define COBALT_BASE_ACCESSIBILITY_CAPTION_SETTINGS_CHANGED_EVENT_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/string_util.h"
+#include "cobalt/base/event.h"
+
+namespace base {
+
+class AccessibilityCaptionSettingsChangedEvent : public Event {
+ public:
+  AccessibilityCaptionSettingsChangedEvent() {}
+
+  BASE_EVENT_SUBCLASS(AccessibilityCaptionSettingsChangedEvent);
+};
+
+}  // namespace base
+
+#endif  // COBALT_BASE_ACCESSIBILITY_CAPTION_SETTINGS_CHANGED_EVENT_H_
diff --git a/src/cobalt/base/base.gyp b/src/cobalt/base/base.gyp
index 2f25385..c5eadee 100644
--- a/src/cobalt/base/base.gyp
+++ b/src/cobalt/base/base.gyp
@@ -78,6 +78,8 @@
         'unused.h',
         'user_log.cc',
         'user_log.h',
+        'version_compatibility.cc',
+        'version_compatibility.h',
       ],
       'dependencies': [
         '<(DEPTH)/base/base.gyp:base',
diff --git a/src/cobalt/base/version_compatibility.cc b/src/cobalt/base/version_compatibility.cc
new file mode 100644
index 0000000..b912f6f
--- /dev/null
+++ b/src/cobalt/base/version_compatibility.cc
@@ -0,0 +1,52 @@
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#if defined(COBALT_ENABLE_VERSION_COMPATIBILITY_VALIDATIONS)
+
+#include "cobalt/base/version_compatibility.h"
+
+#include "base/logging.h"
+
+namespace base {
+
+VersionCompatibility* VersionCompatibility::GetInstance() {
+  return Singleton<VersionCompatibility,
+                   StaticMemorySingletonTraits<VersionCompatibility> >::get();
+}
+
+VersionCompatibility::VersionCompatibility()
+    : violation_count(
+          "Count.VersionCompatibilityViolation", 0,
+          "The number of Cobalt version compatibility violations encountered."),
+      minimum_version_(0) {}
+
+void VersionCompatibility::SetMinimumVersion(int minimum_version) {
+  base::subtle::NoBarrier_Store(&minimum_version_, minimum_version);
+}
+
+int VersionCompatibility::GetMinimumVersion() const {
+  return base::subtle::NoBarrier_Load(&minimum_version_);
+}
+
+void VersionCompatibility::ReportViolation(
+    const std::string& violation_message) {
+  const std::string kVersionCompatibilityViolationTag =
+      "[VERSION COMPATIBILITY VIOLATION]";
+  LOG(ERROR) << kVersionCompatibilityViolationTag << " " << violation_message;
+  ++violation_count;
+}
+
+}  // namespace base
+
+#endif  // defined(COBALT_ENABLE_VERSION_COMPATIBILITY_VALIDATIONS)
diff --git a/src/cobalt/base/version_compatibility.h b/src/cobalt/base/version_compatibility.h
new file mode 100644
index 0000000..4a776d7
--- /dev/null
+++ b/src/cobalt/base/version_compatibility.h
@@ -0,0 +1,63 @@
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_BASE_VERSION_COMPATIBILITY_H_
+#define COBALT_BASE_VERSION_COMPATIBILITY_H_
+
+#if defined(COBALT_ENABLE_VERSION_COMPATIBILITY_VALIDATIONS)
+
+#include <string>
+
+#include "base/atomicops.h"
+#include "base/basictypes.h"
+#include "base/memory/singleton.h"
+#include "cobalt/base/c_val.h"
+
+namespace base {
+
+// This thread-safe singleton class provides basic support for Cobalt version
+// compatibility validations.
+// NOTE: This class exists for convenience and can be removed when we create a
+// better mechanism for dependency injection in Cobalt.
+class VersionCompatibility {
+ public:
+  // Method to get the singleton instance of this class.
+  static VersionCompatibility* GetInstance();
+
+  void SetMinimumVersion(int minimum_version);
+  int GetMinimumVersion() const;
+
+  // Called when a version compatibility violation is encountered.
+  void ReportViolation(const std::string& violation_info);
+
+ private:
+  friend struct StaticMemorySingletonTraits<VersionCompatibility>;
+
+  VersionCompatibility();
+  ~VersionCompatibility() = default;
+
+  // The number of violations that have been encountered.
+  base::CVal<int, base::CValDebug> violation_count;
+
+  // The minimum version of Cobalt to validate compatibility against.
+  base::subtle::Atomic32 minimum_version_;
+
+  DISALLOW_COPY_AND_ASSIGN(VersionCompatibility);
+};
+
+}  // namespace base
+
+#endif  // defined(COBALT_ENABLE_VERSION_COMPATIBILITY_VALIDATIONS)
+
+#endif  // COBALT_BASE_VERSION_COMPATIBILITY_H_
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_single_operation_interface.h b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_single_operation_interface.h
index 01fdd20..82fa889 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_single_operation_interface.h
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_single_operation_interface.h
@@ -41,9 +41,11 @@
       const scoped_refptr<script::Wrappable>& callback_this,
       const scoped_refptr<ArbitraryInterface>& value,
       bool* had_exception) const override;
+
   JSObject* handle() const { return implementing_object_.GetObject(); }
   const JS::Value& value() const { return implementing_object_.GetValue(); }
   bool WasCollected() const { return implementing_object_.WasCollected(); }
+  void Trace(JSTracer* js_tracer) { implementing_object_.Trace(js_tracer); }
 
  private:
   JSContext* context_;
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_indexed_getter_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_indexed_getter_interface.cc
index 7b85687..47af2a9 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_indexed_getter_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_indexed_getter_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,113 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 0;
+
+
+
+
+
+void IndexedPropertyGetterCallback(
+    uint32_t index,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  AnonymousIndexedGetterInterface* impl =
+      wrapper_private->wrappable<AnonymousIndexedGetterInterface>().get();
+  if (index >= impl->length()) {
+    // |index| is out of bounds, so return undefined.
+    return;
+  }
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(isolate,
+              impl->AnonymousIndexedGetter(index),
+              &result_value);
+  }
+  info.GetReturnValue().Set(result_value);
+}
+
+void IndexedPropertyDescriptorCallback(
+    uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
+  // TODO: Figure out under what conditions this gets called.  It's not
+  // getting called in our tests.
+  NOTIMPLEMENTED();
+}
+
+void IndexedPropertyEnumeratorCallback(
+    const v8::PropertyCallbackInfo<v8::Array>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  AnonymousIndexedGetterInterface* impl =
+      wrapper_private->wrappable<AnonymousIndexedGetterInterface>().get();
+  const uint32_t length = impl->length();
+  v8::Local<v8::Array> array = v8::Array::New(isolate, length);
+  for (uint32_t i = 0; i < length; ++i) {
+    array->Set(i, v8::Integer::New(isolate, i));
+  }
+  info.GetReturnValue().Set(array);
+}
+
+void IndexedPropertyDefinerCallback(
+    uint32_t index, const v8::PropertyDescriptor& desc,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  // TODO: Figure out under what conditions this gets called.  It's not
+  // getting called in our tests.
+  NOTIMPLEMENTED();
+}
+
+
+void IndexedPropertySetterCallback(
+    uint32_t index,
+    v8::Local<v8::Value> value,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  AnonymousIndexedGetterInterface* impl =
+      wrapper_private->wrappable<AnonymousIndexedGetterInterface>().get();
+  if (index >= impl->length()) {
+    return;
+  }
+  TypeTraits<uint32_t>::ConversionType native_value;
+  FromJSValue(isolate, value, kNoConversionFlags,
+              &exception_state, &native_value);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+
+  impl->AnonymousIndexedSetter(index, native_value);
+  result_value = v8::Undefined(isolate);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+  info.GetReturnValue().Set(value);
+}
+
+
 
 
 void lengthAttributeGetter(
@@ -127,52 +231,141 @@
 
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "AnonymousIndexedGetterInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "AnonymousIndexedGetterInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "length",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    lengthAttributeGetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "length");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        lengthAttributeGetter,
+        0,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 0;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+  }
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "AnonymousIndexedGetterInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
+  {
+    v8::IndexedPropertyHandlerConfiguration indexed_property_handler_configuration = {
+      IndexedPropertyGetterCallback,
+      IndexedPropertySetterCallback,
+      IndexedPropertyDescriptorCallback,
+      nullptr,
+      IndexedPropertyEnumeratorCallback,
+      IndexedPropertyDefinerCallback
+    };
+    instance_template->SetHandler(indexed_property_handler_configuration);
+  }
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cAnonymousIndexedGetterInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cAnonymousIndexedGetterInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -182,14 +375,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cAnonymousIndexedGetterInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cAnonymousIndexedGetterInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_indexed_getter_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_indexed_getter_interface.h
index 8d168df..4af4572 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_indexed_getter_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_indexed_getter_interface.h
@@ -39,8 +39,7 @@
 class V8cAnonymousIndexedGetterInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_getter_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_getter_interface.cc
index 587bce5..ab85a2f 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_getter_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_getter_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,49 +84,226 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 1;
+
+
+void NamedPropertyGetterCallback(
+    v8::Local<v8::Name> property,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  AnonymousNamedGetterInterface* impl =
+      wrapper_private->wrappable<AnonymousNamedGetterInterface>().get();
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  if (!impl->CanQueryNamedProperty(property_name)) {
+    return;
+  }
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(isolate,
+              impl->AnonymousNamedGetter(property_name),
+              &result_value);
+  }
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+  info.GetReturnValue().Set(result_value);
+  DCHECK(!exception_state.is_exception_set());
+}
+
+void NamedPropertyQueryCallback(
+    v8::Local<v8::Name> property,
+    const v8::PropertyCallbackInfo<v8::Integer>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  AnonymousNamedGetterInterface* impl =
+      wrapper_private->wrappable<AnonymousNamedGetterInterface>().get();
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  bool result = impl->CanQueryNamedProperty(property_name);
+  if (!result) {
+    return;
+  }
+  // https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty
+  // 2.7. If |O| implements an interface with a named property setter, then set
+  //      desc.[[Writable]] to true, otherwise set it to false.
+  // 2.8. If |O| implements an interface with the
+  //      [LegacyUnenumerableNamedProperties] extended attribute, then set
+  //      desc.[[Enumerable]] to false, otherwise set it to true.
+  info.GetReturnValue().Set(v8::DontEnum | v8::ReadOnly);
+}
+
+void NamedPropertyEnumeratorCallback(
+    const v8::PropertyCallbackInfo<v8::Array>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  AnonymousNamedGetterInterface* impl =
+      wrapper_private->wrappable<AnonymousNamedGetterInterface>().get();
+  v8::Local<v8::Array> array = v8::Array::New(isolate);
+  V8cPropertyEnumerator property_enumerator(isolate, &array);
+  impl->EnumerateNamedProperties(&property_enumerator);
+  info.GetReturnValue().Set(array);
+}
+
+
+void NamedPropertySetterCallback(
+    v8::Local<v8::Name> property,
+    v8::Local<v8::Value> value,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  AnonymousNamedGetterInterface* impl =
+      wrapper_private->wrappable<AnonymousNamedGetterInterface>().get();
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  TypeTraits<std::string>::ConversionType native_value;
+  FromJSValue(isolate, value, kNoConversionFlags,
+              &exception_state, &native_value);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+
+  impl->AnonymousNamedSetter(property_name, native_value);
+  result_value = v8::Undefined(isolate);
+  DCHECK(!exception_state.is_exception_set());
+}
 
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "AnonymousNamedGetterInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+
+
+
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "AnonymousNamedGetterInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "AnonymousNamedGetterInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+  {
+    v8::NamedPropertyHandlerConfiguration named_property_handler_configuration = {
+      NamedPropertyGetterCallback,
+      NamedPropertySetterCallback,
+      NamedPropertyQueryCallback,
+      nullptr,
+      NamedPropertyEnumeratorCallback,
+      v8::Local<v8::Value>(),
+      static_cast<v8::PropertyHandlerFlags>(int(v8::PropertyHandlerFlags::kNonMasking) | int(v8::PropertyHandlerFlags::kOnlyInterceptStrings))
+    };
+    instance_template->SetHandler(named_property_handler_configuration);
+  }
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
-
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 1;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cAnonymousNamedGetterInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cAnonymousNamedGetterInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -139,14 +313,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cAnonymousNamedGetterInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cAnonymousNamedGetterInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_getter_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_getter_interface.h
index 6e3f21b..190de7f 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_getter_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_getter_interface.h
@@ -39,8 +39,7 @@
 class V8cAnonymousNamedGetterInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_indexed_getter_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_indexed_getter_interface.cc
index 1d2d4c1..3947292 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_indexed_getter_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_indexed_getter_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,221 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 2;
+
+
+void NamedPropertyGetterCallback(
+    v8::Local<v8::Name> property,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  AnonymousNamedIndexedGetterInterface* impl =
+      wrapper_private->wrappable<AnonymousNamedIndexedGetterInterface>().get();
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  if (!impl->CanQueryNamedProperty(property_name)) {
+    return;
+  }
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(isolate,
+              impl->AnonymousNamedGetter(property_name),
+              &result_value);
+  }
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+  info.GetReturnValue().Set(result_value);
+  DCHECK(!exception_state.is_exception_set());
+}
+
+void NamedPropertyQueryCallback(
+    v8::Local<v8::Name> property,
+    const v8::PropertyCallbackInfo<v8::Integer>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  AnonymousNamedIndexedGetterInterface* impl =
+      wrapper_private->wrappable<AnonymousNamedIndexedGetterInterface>().get();
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  bool result = impl->CanQueryNamedProperty(property_name);
+  if (!result) {
+    return;
+  }
+  // https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty
+  // 2.7. If |O| implements an interface with a named property setter, then set
+  //      desc.[[Writable]] to true, otherwise set it to false.
+  // 2.8. If |O| implements an interface with the
+  //      [LegacyUnenumerableNamedProperties] extended attribute, then set
+  //      desc.[[Enumerable]] to false, otherwise set it to true.
+  info.GetReturnValue().Set(v8::DontEnum | v8::ReadOnly);
+}
+
+void NamedPropertyEnumeratorCallback(
+    const v8::PropertyCallbackInfo<v8::Array>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  AnonymousNamedIndexedGetterInterface* impl =
+      wrapper_private->wrappable<AnonymousNamedIndexedGetterInterface>().get();
+  v8::Local<v8::Array> array = v8::Array::New(isolate);
+  V8cPropertyEnumerator property_enumerator(isolate, &array);
+  impl->EnumerateNamedProperties(&property_enumerator);
+  info.GetReturnValue().Set(array);
+}
+
+
+void NamedPropertySetterCallback(
+    v8::Local<v8::Name> property,
+    v8::Local<v8::Value> value,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  AnonymousNamedIndexedGetterInterface* impl =
+      wrapper_private->wrappable<AnonymousNamedIndexedGetterInterface>().get();
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  TypeTraits<std::string>::ConversionType native_value;
+  FromJSValue(isolate, value, kNoConversionFlags,
+              &exception_state, &native_value);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+
+  impl->AnonymousNamedSetter(property_name, native_value);
+  result_value = v8::Undefined(isolate);
+  DCHECK(!exception_state.is_exception_set());
+}
+
+
+
+void IndexedPropertyGetterCallback(
+    uint32_t index,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  AnonymousNamedIndexedGetterInterface* impl =
+      wrapper_private->wrappable<AnonymousNamedIndexedGetterInterface>().get();
+  if (index >= impl->length()) {
+    // |index| is out of bounds, so return undefined.
+    return;
+  }
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(isolate,
+              impl->AnonymousIndexedGetter(index),
+              &result_value);
+  }
+  info.GetReturnValue().Set(result_value);
+}
+
+void IndexedPropertyDescriptorCallback(
+    uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
+  // TODO: Figure out under what conditions this gets called.  It's not
+  // getting called in our tests.
+  NOTIMPLEMENTED();
+}
+
+void IndexedPropertyEnumeratorCallback(
+    const v8::PropertyCallbackInfo<v8::Array>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  AnonymousNamedIndexedGetterInterface* impl =
+      wrapper_private->wrappable<AnonymousNamedIndexedGetterInterface>().get();
+  const uint32_t length = impl->length();
+  v8::Local<v8::Array> array = v8::Array::New(isolate, length);
+  for (uint32_t i = 0; i < length; ++i) {
+    array->Set(i, v8::Integer::New(isolate, i));
+  }
+  info.GetReturnValue().Set(array);
+}
+
+void IndexedPropertyDefinerCallback(
+    uint32_t index, const v8::PropertyDescriptor& desc,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  // TODO: Figure out under what conditions this gets called.  It's not
+  // getting called in our tests.
+  NOTIMPLEMENTED();
+}
+
+
+void IndexedPropertySetterCallback(
+    uint32_t index,
+    v8::Local<v8::Value> value,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  AnonymousNamedIndexedGetterInterface* impl =
+      wrapper_private->wrappable<AnonymousNamedIndexedGetterInterface>().get();
+  if (index >= impl->length()) {
+    return;
+  }
+  TypeTraits<uint32_t>::ConversionType native_value;
+  FromJSValue(isolate, value, kNoConversionFlags,
+              &exception_state, &native_value);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+
+  impl->AnonymousIndexedSetter(index, native_value);
+  result_value = v8::Undefined(isolate);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+  info.GetReturnValue().Set(value);
+}
+
+
 
 
 void lengthAttributeGetter(
@@ -127,52 +339,153 @@
 
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "AnonymousNamedIndexedGetterInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "AnonymousNamedIndexedGetterInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "length",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    lengthAttributeGetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "length");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        lengthAttributeGetter,
+        0,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 2;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+  }
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "AnonymousNamedIndexedGetterInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+  {
+    v8::NamedPropertyHandlerConfiguration named_property_handler_configuration = {
+      NamedPropertyGetterCallback,
+      NamedPropertySetterCallback,
+      NamedPropertyQueryCallback,
+      nullptr,
+      NamedPropertyEnumeratorCallback,
+      v8::Local<v8::Value>(),
+      static_cast<v8::PropertyHandlerFlags>(int(v8::PropertyHandlerFlags::kNonMasking) | int(v8::PropertyHandlerFlags::kOnlyInterceptStrings))
+    };
+    instance_template->SetHandler(named_property_handler_configuration);
+  }
+
+  {
+    v8::IndexedPropertyHandlerConfiguration indexed_property_handler_configuration = {
+      IndexedPropertyGetterCallback,
+      IndexedPropertySetterCallback,
+      IndexedPropertyDescriptorCallback,
+      nullptr,
+      IndexedPropertyEnumeratorCallback,
+      IndexedPropertyDefinerCallback
+    };
+    instance_template->SetHandler(indexed_property_handler_configuration);
+  }
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cAnonymousNamedIndexedGetterInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cAnonymousNamedIndexedGetterInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -182,14 +495,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cAnonymousNamedIndexedGetterInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cAnonymousNamedIndexedGetterInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_indexed_getter_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_indexed_getter_interface.h
index 59b1d60..fd83275 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_indexed_getter_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_indexed_getter_interface.h
@@ -39,8 +39,7 @@
 class V8cAnonymousNamedIndexedGetterInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_arbitrary_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_arbitrary_interface.cc
index f644082..627c95d 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_arbitrary_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_arbitrary_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 3;
+
+
+
+
+
+
+
 
 
 void Constructor(const v8::FunctionCallbackInfo<v8::Value>& info) {
@@ -211,64 +216,164 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "ArbitraryInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          Constructor,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetLength(0);
+  function_template->SetClassName(NewInternalString(isolate, "ArbitraryInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "arbitraryProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    arbitraryPropertyAttributeGetter
-    ,arbitraryPropertyAttributeSetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, arbitraryFunctionMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "arbitraryFunction",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "arbitraryProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        arbitraryPropertyAttributeGetter,
+        arbitraryPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
   }
 
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "arbitraryFunction");
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 3;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, arbitraryFunctionMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "arbitraryFunction"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "ArbitraryInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cArbitraryInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cArbitraryInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -278,14 +383,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cArbitraryInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cArbitraryInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_arbitrary_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_arbitrary_interface.h
index 89dfb9a..1ac26dd 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_arbitrary_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_arbitrary_interface.h
@@ -39,8 +39,7 @@
 class V8cArbitraryInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_base_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_base_interface.cc
index fd2ca14..e447a9c 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_base_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_base_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 4;
+
+
+
+
+
+
+
 
 
 void Constructor(const v8::FunctionCallbackInfo<v8::Value>& info) {
@@ -174,63 +179,164 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "BaseInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          Constructor,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetLength(0);
+  function_template->SetClassName(NewInternalString(isolate, "BaseInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "baseAttribute",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    baseAttributeAttributeGetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, baseOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "baseOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "baseAttribute");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        baseAttributeAttributeGetter,
+        0,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
   }
 
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "baseOperation");
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 4;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, baseOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "baseOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "BaseInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cBaseInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cBaseInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -240,14 +346,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cBaseInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cBaseInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_base_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_base_interface.h
index aaecf17..64c0f77 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_base_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_base_interface.h
@@ -39,8 +39,7 @@
 class V8cBaseInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_boolean_type_test_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_boolean_type_test_interface.cc
index 4425931..a3706ab 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_boolean_type_test_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_boolean_type_test_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 5;
+
+
+
+
+
+
+
 
 
 void booleanPropertyAttributeGetter(
@@ -246,75 +251,196 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "BooleanTypeTestInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "BooleanTypeTestInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "booleanProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    booleanPropertyAttributeGetter
-    ,booleanPropertyAttributeSetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, booleanArgumentOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "booleanArgumentOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "booleanProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        booleanPropertyAttributeGetter,
+        booleanPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
   }
 
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, booleanReturnOperationMethod);
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "booleanArgumentOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, booleanArgumentOperationMethod);
     method_template->RemovePrototype();
+    method_template->SetLength(1);
     prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "booleanReturnOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
+        NewInternalString(isolate, "booleanArgumentOperation"),
         method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "booleanReturnOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, booleanReturnOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "booleanReturnOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
   }
 
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "BooleanTypeTestInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
-  interface_data->function_template.Set(isolate, function_template);
-}
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 5;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cBooleanTypeTestInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cBooleanTypeTestInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -324,14 +450,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cBooleanTypeTestInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cBooleanTypeTestInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_boolean_type_test_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_boolean_type_test_interface.h
index d2af8d7..64ffe39 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_boolean_type_test_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_boolean_type_test_interface.h
@@ -39,8 +39,7 @@
 class V8cBooleanTypeTestInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_function_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_function_interface.cc
index 74dbe19..1328e44 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_function_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_function_interface.cc
@@ -35,12 +35,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -62,7 +64,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -70,18 +71,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -91,6 +88,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 6;
+
+
+
+
+
+
+
 
 
 void callbackAttributeAttributeGetter(
@@ -167,6 +172,7 @@
   return;
 }
 
+
 void nullableCallbackAttributeAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -472,115 +478,329 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "CallbackFunctionInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "CallbackFunctionInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "callbackAttribute",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    callbackAttributeAttributeGetter
-    ,callbackAttributeAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "nullableCallbackAttribute",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    nullableCallbackAttributeAttributeGetter
-    ,nullableCallbackAttributeAttributeSetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, takesFunctionThatReturnsStringMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "takesFunctionThatReturnsString",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "callbackAttribute");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        callbackAttributeAttributeGetter,
+        callbackAttributeAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "nullableCallbackAttribute");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        nullableCallbackAttributeAttributeGetter,
+        nullableCallbackAttributeAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
   }
 
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, takesFunctionWithNullableParametersMethod);
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "takesFunctionThatReturnsString");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, takesFunctionThatReturnsStringMethod);
     method_template->RemovePrototype();
+    method_template->SetLength(1);
     prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "takesFunctionWithNullableParameters",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
+        NewInternalString(isolate, "takesFunctionThatReturnsString"),
         method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "takesFunctionWithNullableParameters");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, takesFunctionWithNullableParametersMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "takesFunctionWithNullableParameters"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "takesFunctionWithOneParameter");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, takesFunctionWithOneParameterMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "takesFunctionWithOneParameter"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "takesFunctionWithSeveralParameters");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, takesFunctionWithSeveralParametersMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "takesFunctionWithSeveralParameters"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "takesVoidFunction");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, takesVoidFunctionMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "takesVoidFunction"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
   }
 
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, takesFunctionWithOneParameterMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "takesFunctionWithOneParameter",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, takesFunctionWithSeveralParametersMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "takesFunctionWithSeveralParameters",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, takesVoidFunctionMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "takesVoidFunction",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "CallbackFunctionInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 6;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cCallbackFunctionInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cCallbackFunctionInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -590,14 +810,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cCallbackFunctionInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cCallbackFunctionInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_function_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_function_interface.h
index a6cf0f1..f54da19 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_function_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_function_interface.h
@@ -39,8 +39,7 @@
 class V8cCallbackFunctionInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_interface_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_interface_interface.cc
index c582b3b..6f254dc 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_interface_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_interface_interface.cc
@@ -35,12 +35,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -62,7 +64,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -70,18 +71,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -91,6 +88,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 7;
+
+
+
+
+
+
+
 
 
 void callbackAttributeAttributeGetter(
@@ -244,75 +249,196 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "CallbackInterfaceInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "CallbackInterfaceInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "callbackAttribute",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    callbackAttributeAttributeGetter
-    ,callbackAttributeAttributeSetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, registerCallbackMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "registerCallback",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "callbackAttribute");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        callbackAttributeAttributeGetter,
+        callbackAttributeAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
   }
 
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, someOperationMethod);
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "registerCallback");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, registerCallbackMethod);
     method_template->RemovePrototype();
+    method_template->SetLength(1);
     prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "someOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
+        NewInternalString(isolate, "registerCallback"),
         method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "someOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, someOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "someOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
   }
 
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "CallbackInterfaceInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
-  interface_data->function_template.Set(isolate, function_template);
-}
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 7;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cCallbackInterfaceInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cCallbackInterfaceInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -322,14 +448,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cCallbackInterfaceInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cCallbackInterfaceInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_interface_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_interface_interface.h
index 64a407e..9fc8f8f 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_interface_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_interface_interface.h
@@ -39,8 +39,7 @@
 class V8cCallbackInterfaceInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_conditional_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_conditional_interface.cc
index 42eb6bb..28b4236 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_conditional_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_conditional_interface.cc
@@ -35,12 +35,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -60,7 +62,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -68,18 +69,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -89,9 +86,17 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 8;
+
+
+
+
+
+
 
 
 #if defined(ENABLE_CONDITIONAL_PROPERTY)
+
 void enabledAttributeAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -168,6 +173,7 @@
 
 #endif  // ENABLE_CONDITIONAL_PROPERTY
 #if defined(NO_ENABLE_CONDITIONAL_PROPERTY)
+
 void disabledAttributeAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -308,90 +314,238 @@
 
 #endif  // ENABLE_CONDITIONAL_PROPERTY
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "ConditionalInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "ConditionalInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-#if defined(ENABLE_CONDITIONAL_PROPERTY)
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "enabledAttribute",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    enabledAttributeAttributeGetter
-    ,enabledAttributeAttributeSetter
-  );
-#endif  // defined(ENABLE_CONDITIONAL_PROPERTY)
-#if defined(NO_ENABLE_CONDITIONAL_PROPERTY)
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "disabledAttribute",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    disabledAttributeAttributeGetter
-    ,disabledAttributeAttributeSetter
-  );
-#endif  // defined(NO_ENABLE_CONDITIONAL_PROPERTY)
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
-#if defined(NO_ENABLE_CONDITIONAL_PROPERTY)
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, disabledOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "disabledOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-#endif  // defined(NO_ENABLE_CONDITIONAL_PROPERTY)
-
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
 #if defined(ENABLE_CONDITIONAL_PROPERTY)
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, enabledOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "enabledOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "enabledAttribute");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        enabledAttributeAttributeGetter,
+        enabledAttributeAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
   }
-#endif  // defined(ENABLE_CONDITIONAL_PROPERTY)
+#endif  // ENABLE_CONDITIONAL_PROPERTY
+#if defined(NO_ENABLE_CONDITIONAL_PROPERTY)
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "disabledAttribute");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        disabledAttributeAttributeGetter,
+        disabledAttributeAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 8;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+  }
+#endif  // NO_ENABLE_CONDITIONAL_PROPERTY
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+#if defined(NO_ENABLE_CONDITIONAL_PROPERTY)
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "disabledOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, disabledOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "disabledOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+#endif  // NO_ENABLE_CONDITIONAL_PROPERTY
+#if defined(ENABLE_CONDITIONAL_PROPERTY)
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "enabledOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, enabledOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "enabledOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+#endif  // ENABLE_CONDITIONAL_PROPERTY
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "ConditionalInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cConditionalInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cConditionalInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -401,14 +555,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cConditionalInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cConditionalInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_conditional_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_conditional_interface.h
index a98041d..212639b 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_conditional_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_conditional_interface.h
@@ -41,8 +41,7 @@
 class V8cConditionalInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constants_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constants_interface.cc
index 4077c2d..ea5e9af 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constants_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constants_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,73 +84,158 @@
 
 namespace {
 
-
-void INTEGER_CONSTANTAttributeGetter(
-    v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
-  // TODO: Consider just attaching a constant instead.
-  v8::Local<v8::Value> result_value;
-  ToJSValue(info.GetIsolate(), 5, &result_value);
-  info.GetReturnValue().Set(result_value);
-}
-void DOUBLE_CONSTANTAttributeGetter(
-    v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
-  // TODO: Consider just attaching a constant instead.
-  v8::Local<v8::Value> result_value;
-  ToJSValue(info.GetIsolate(), 2.718, &result_value);
-  info.GetReturnValue().Set(result_value);
-}
+const int kInterfaceUniqueId = 9;
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "ConstantsInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+
+
+
+
+
+
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "ConstantsInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
 
-  prototype_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "INTEGER_CONSTANT",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    INTEGER_CONSTANTAttributeGetter);
-  prototype_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "DOUBLE_CONSTANT",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    DOUBLE_CONSTANTAttributeGetter);
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+  {
+    // The name of the property is the identifier of the constant.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "INTEGER_CONSTANT");
+
+    // The value of the property is that which is obtained by converting the
+    // constant's IDL value to an ECMAScript value.
+    v8::Local<v8::Value> constant_value;
+    ToJSValue(isolate, 5, &constant_value);
+
+    // The property has attributes { [[Writable]]: false, [[Enumerable]]: true,
+    // [[Configurable]]: false }.
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        v8::ReadOnly | v8::DontDelete);
+
+    // The location of the property is determined as follows:
+    // Otherwise, if the interface has an interface prototype object, then the
+    // property exists on it.
+    prototype_template->Set(name, constant_value, attributes);
+
+    // In addition, a property with the same characteristics must exist on the
+    // interface object or the legacy callback interface object, if either of
+    // those objects exists.
+    function_template->Set(name, constant_value, attributes);
+  }
+  {
+    // The name of the property is the identifier of the constant.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "DOUBLE_CONSTANT");
+
+    // The value of the property is that which is obtained by converting the
+    // constant's IDL value to an ECMAScript value.
+    v8::Local<v8::Value> constant_value;
+    ToJSValue(isolate, 2.718, &constant_value);
+
+    // The property has attributes { [[Writable]]: false, [[Enumerable]]: true,
+    // [[Configurable]]: false }.
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        v8::ReadOnly | v8::DontDelete);
+
+    // The location of the property is determined as follows:
+    // Otherwise, if the interface has an interface prototype object, then the
+    // property exists on it.
+    prototype_template->Set(name, constant_value, attributes);
+
+    // In addition, a property with the same characteristics must exist on the
+    // interface object or the legacy callback interface object, if either of
+    // those objects exists.
+    function_template->Set(name, constant_value, attributes);
+  }
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "ConstantsInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
-
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 9;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cConstantsInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cConstantsInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -163,14 +245,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cConstantsInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cConstantsInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constants_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constants_interface.h
index 9ea42b7..c30024c 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constants_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constants_interface.h
@@ -39,8 +39,7 @@
 class V8cConstantsInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_interface.cc
index 0aef9a1..48c0906 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 10;
+
+
+
+
+
+
+
 
 void Constructor1(
     const v8::FunctionCallbackInfo<v8::Value>& info) {
@@ -172,46 +177,96 @@
 
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "ConstructorInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          Constructor,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetLength(0);
+  function_template->SetClassName(NewInternalString(isolate, "ConstructorInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "ConstructorInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
 
 
-
-  interface_data->function_template.Set(isolate, function_template);
-}
-
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 10;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cConstructorInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cConstructorInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -221,14 +276,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cConstructorInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cConstructorInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_interface.h
index 21cc81d..857b2cf 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_interface.h
@@ -39,8 +39,7 @@
 class V8cConstructorInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_with_arguments_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_with_arguments_interface.cc
index 6723e45..0903cc0 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_with_arguments_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_with_arguments_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 11;
+
+
+
+
+
+
+
 
 
 void Constructor(const v8::FunctionCallbackInfo<v8::Value>& info) {
@@ -184,6 +189,7 @@
 }
 
 
+
 void booleanArgAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -221,6 +227,7 @@
 }
 
 
+
 void stringArgAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -259,64 +266,199 @@
 
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "ConstructorWithArgumentsInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          Constructor,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          2);
+  function_template->SetLength(2);
+  function_template->SetClassName(NewInternalString(isolate, "ConstructorWithArgumentsInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "longArg",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    longArgAttributeGetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "booleanArg",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    booleanArgAttributeGetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "stringArg",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    stringArgAttributeGetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "longArg");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        longArgAttributeGetter,
+        0,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 11;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "booleanArg");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        booleanArgAttributeGetter,
+        0,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "stringArg");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        stringArgAttributeGetter,
+        0,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "ConstructorWithArgumentsInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cConstructorWithArgumentsInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cConstructorWithArgumentsInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -326,14 +468,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cConstructorWithArgumentsInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cConstructorWithArgumentsInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_with_arguments_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_with_arguments_interface.h
index 6c01a6e..a5b004c 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_with_arguments_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_with_arguments_interface.h
@@ -39,8 +39,7 @@
 class V8cConstructorWithArgumentsInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_getter_setter_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_getter_setter_interface.cc
index 2acd721..f2d42d6 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_getter_setter_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_getter_setter_interface.cc
@@ -35,12 +35,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -62,7 +64,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -70,18 +71,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -91,6 +88,221 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 14;
+
+
+void NamedPropertyGetterCallback(
+    v8::Local<v8::Name> property,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  DerivedGetterSetterInterface* impl =
+      wrapper_private->wrappable<DerivedGetterSetterInterface>().get();
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  if (!impl->CanQueryNamedProperty(property_name)) {
+    return;
+  }
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(isolate,
+              impl->AnonymousNamedGetter(property_name),
+              &result_value);
+  }
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+  info.GetReturnValue().Set(result_value);
+  DCHECK(!exception_state.is_exception_set());
+}
+
+void NamedPropertyQueryCallback(
+    v8::Local<v8::Name> property,
+    const v8::PropertyCallbackInfo<v8::Integer>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  DerivedGetterSetterInterface* impl =
+      wrapper_private->wrappable<DerivedGetterSetterInterface>().get();
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  bool result = impl->CanQueryNamedProperty(property_name);
+  if (!result) {
+    return;
+  }
+  // https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty
+  // 2.7. If |O| implements an interface with a named property setter, then set
+  //      desc.[[Writable]] to true, otherwise set it to false.
+  // 2.8. If |O| implements an interface with the
+  //      [LegacyUnenumerableNamedProperties] extended attribute, then set
+  //      desc.[[Enumerable]] to false, otherwise set it to true.
+  info.GetReturnValue().Set(v8::DontEnum | v8::ReadOnly);
+}
+
+void NamedPropertyEnumeratorCallback(
+    const v8::PropertyCallbackInfo<v8::Array>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  DerivedGetterSetterInterface* impl =
+      wrapper_private->wrappable<DerivedGetterSetterInterface>().get();
+  v8::Local<v8::Array> array = v8::Array::New(isolate);
+  V8cPropertyEnumerator property_enumerator(isolate, &array);
+  impl->EnumerateNamedProperties(&property_enumerator);
+  info.GetReturnValue().Set(array);
+}
+
+
+void NamedPropertySetterCallback(
+    v8::Local<v8::Name> property,
+    v8::Local<v8::Value> value,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  DerivedGetterSetterInterface* impl =
+      wrapper_private->wrappable<DerivedGetterSetterInterface>().get();
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  TypeTraits<std::string>::ConversionType native_value;
+  FromJSValue(isolate, value, kNoConversionFlags,
+              &exception_state, &native_value);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+
+  impl->AnonymousNamedSetter(property_name, native_value);
+  result_value = v8::Undefined(isolate);
+  DCHECK(!exception_state.is_exception_set());
+}
+
+
+
+void IndexedPropertyGetterCallback(
+    uint32_t index,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  DerivedGetterSetterInterface* impl =
+      wrapper_private->wrappable<DerivedGetterSetterInterface>().get();
+  if (index >= impl->length()) {
+    // |index| is out of bounds, so return undefined.
+    return;
+  }
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(isolate,
+              impl->DerivedIndexedGetter(index),
+              &result_value);
+  }
+  info.GetReturnValue().Set(result_value);
+}
+
+void IndexedPropertyDescriptorCallback(
+    uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
+  // TODO: Figure out under what conditions this gets called.  It's not
+  // getting called in our tests.
+  NOTIMPLEMENTED();
+}
+
+void IndexedPropertyEnumeratorCallback(
+    const v8::PropertyCallbackInfo<v8::Array>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  DerivedGetterSetterInterface* impl =
+      wrapper_private->wrappable<DerivedGetterSetterInterface>().get();
+  const uint32_t length = impl->length();
+  v8::Local<v8::Array> array = v8::Array::New(isolate, length);
+  for (uint32_t i = 0; i < length; ++i) {
+    array->Set(i, v8::Integer::New(isolate, i));
+  }
+  info.GetReturnValue().Set(array);
+}
+
+void IndexedPropertyDefinerCallback(
+    uint32_t index, const v8::PropertyDescriptor& desc,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  // TODO: Figure out under what conditions this gets called.  It's not
+  // getting called in our tests.
+  NOTIMPLEMENTED();
+}
+
+
+void IndexedPropertySetterCallback(
+    uint32_t index,
+    v8::Local<v8::Value> value,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  DerivedGetterSetterInterface* impl =
+      wrapper_private->wrappable<DerivedGetterSetterInterface>().get();
+  if (index >= impl->length()) {
+    return;
+  }
+  TypeTraits<uint32_t>::ConversionType native_value;
+  FromJSValue(isolate, value, kNoConversionFlags,
+              &exception_state, &native_value);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+
+  impl->DerivedIndexedSetter(index, native_value);
+  result_value = v8::Undefined(isolate);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+  info.GetReturnValue().Set(value);
+}
+
+
 
 
 void lengthAttributeGetter(
@@ -130,6 +342,7 @@
 }
 
 
+
 void propertyOnDerivedClassAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -343,94 +556,297 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "DerivedGetterSetterInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "DerivedGetterSetterInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-  v8::Local<v8::FunctionTemplate> parent_template = V8cNamedIndexedGetterInterface::CreateTemplate(isolate);
-  function_template->Inherit(parent_template);
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
-
-
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "length",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    lengthAttributeGetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "propertyOnDerivedClass",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    propertyOnDerivedClassAttributeGetter
-    ,propertyOnDerivedClassAttributeSetter
-  );
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, derivedIndexedGetterMethod);
+    // An interface can be defined to inherit from another interface. If the
+    // identifier of the interface is followed by a U+003A COLON (":") character
+    // and an identifier, then that identifier identifies the inherited
+    // interface. An object that implements an interface that inherits from
+    // another also implements that inherited interface. The object therefore
+    // will also have members that correspond to the interface members from the
+    // inherited interface.
+    v8::Local<v8::FunctionTemplate> parent_template = V8cNamedIndexedGetterInterface::GetTemplate(isolate);
+    function_template->Inherit(parent_template);
+  }
+
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "length");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        lengthAttributeGetter,
+        0,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "propertyOnDerivedClass");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        propertyOnDerivedClassAttributeGetter,
+        propertyOnDerivedClassAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "derivedIndexedGetter");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, derivedIndexedGetterMethod);
     method_template->RemovePrototype();
+    method_template->SetLength(1);
     prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "derivedIndexedGetter",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
+        NewInternalString(isolate, "derivedIndexedGetter"),
         method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "derivedIndexedSetter");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, derivedIndexedSetterMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(2);
+    prototype_template->Set(
+        NewInternalString(isolate, "derivedIndexedSetter"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "operationOnDerivedClass");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, operationOnDerivedClassMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "operationOnDerivedClass"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "DerivedGetterSetterInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+  {
+    v8::NamedPropertyHandlerConfiguration named_property_handler_configuration = {
+      NamedPropertyGetterCallback,
+      NamedPropertySetterCallback,
+      NamedPropertyQueryCallback,
+      nullptr,
+      NamedPropertyEnumeratorCallback,
+      v8::Local<v8::Value>(),
+      static_cast<v8::PropertyHandlerFlags>(int(v8::PropertyHandlerFlags::kNonMasking) | int(v8::PropertyHandlerFlags::kOnlyInterceptStrings))
+    };
+    instance_template->SetHandler(named_property_handler_configuration);
   }
 
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, derivedIndexedSetterMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "derivedIndexedSetter",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    v8::IndexedPropertyHandlerConfiguration indexed_property_handler_configuration = {
+      IndexedPropertyGetterCallback,
+      IndexedPropertySetterCallback,
+      IndexedPropertyDescriptorCallback,
+      nullptr,
+      IndexedPropertyEnumeratorCallback,
+      IndexedPropertyDefinerCallback
+    };
+    instance_template->SetHandler(indexed_property_handler_configuration);
   }
 
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, operationOnDerivedClassMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "operationOnDerivedClass",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-
-  interface_data->function_template.Set(isolate, function_template);
-}
-
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 14;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cDerivedGetterSetterInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cDerivedGetterSetterInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -440,14 +856,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cDerivedGetterSetterInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cDerivedGetterSetterInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_getter_setter_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_getter_setter_interface.h
index a938db4..1c50e6d 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_getter_setter_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_getter_setter_interface.h
@@ -40,8 +40,7 @@
 class V8cDerivedGetterSetterInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_interface.cc
index 9e13e18..db63e88 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_interface.cc
@@ -35,12 +35,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -62,7 +64,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -70,18 +71,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -91,6 +88,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 15;
+
+
+
+
+
+
+
 
 
 void Constructor(const v8::FunctionCallbackInfo<v8::Value>& info) {
@@ -178,65 +183,175 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "DerivedInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          Constructor,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetLength(0);
+  function_template->SetClassName(NewInternalString(isolate, "DerivedInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-  v8::Local<v8::FunctionTemplate> parent_template = V8cBaseInterface::CreateTemplate(isolate);
-  function_template->Inherit(parent_template);
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
-
-
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "derivedAttribute",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    derivedAttributeAttributeGetter
-  );
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, derivedOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "derivedOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    // An interface can be defined to inherit from another interface. If the
+    // identifier of the interface is followed by a U+003A COLON (":") character
+    // and an identifier, then that identifier identifies the inherited
+    // interface. An object that implements an interface that inherits from
+    // another also implements that inherited interface. The object therefore
+    // will also have members that correspond to the interface members from the
+    // inherited interface.
+    v8::Local<v8::FunctionTemplate> parent_template = V8cBaseInterface::GetTemplate(isolate);
+    function_template->Inherit(parent_template);
   }
 
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "derivedAttribute");
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 15;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        derivedAttributeAttributeGetter,
+        0,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "derivedOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, derivedOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "derivedOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "DerivedInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cDerivedInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cDerivedInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -246,14 +361,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cDerivedInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cDerivedInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_interface.h
index ff15ad0..1814d02 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_interface.h
@@ -40,8 +40,7 @@
 class V8cDerivedInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dictionary_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dictionary_interface.cc
index a2de658..48ee0bd 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dictionary_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dictionary_interface.cc
@@ -36,12 +36,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -64,7 +66,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -72,18 +73,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -93,6 +90,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 16;
+
+
+
+
+
+
+
 
 
 void dictionarySequenceAttributeGetter(
@@ -308,86 +313,229 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "DictionaryInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "DictionaryInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "dictionarySequence",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    dictionarySequenceAttributeGetter
-    ,dictionarySequenceAttributeSetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, derivedDictionaryOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "derivedDictionaryOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "dictionarySequence");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        dictionarySequenceAttributeGetter,
+        dictionarySequenceAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
   }
 
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, dictionaryOperationMethod);
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "derivedDictionaryOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, derivedDictionaryOperationMethod);
     method_template->RemovePrototype();
+    method_template->SetLength(1);
     prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "dictionaryOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
+        NewInternalString(isolate, "derivedDictionaryOperation"),
         method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "dictionaryOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, dictionaryOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "dictionaryOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "testOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, testOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "testOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
   }
 
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, testOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "testOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "DictionaryInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 16;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cDictionaryInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cDictionaryInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -397,14 +545,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cDictionaryInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cDictionaryInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dictionary_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dictionary_interface.h
index 7c976d4..ef0a08a 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dictionary_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dictionary_interface.h
@@ -39,8 +39,7 @@
 class V8cDictionaryInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_disabled_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_disabled_interface.cc
index d800dbc..d14ac28 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_disabled_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_disabled_interface.cc
@@ -35,12 +35,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -60,7 +62,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -68,18 +69,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -89,6 +86,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 18;
+
+
+
+
+
+
+
 
 
 void disabledPropertyAttributeGetter(
@@ -196,64 +201,163 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "DisabledInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "DisabledInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "disabledProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    disabledPropertyAttributeGetter
-    ,disabledPropertyAttributeSetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, disabledFunctionMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "disabledFunction",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "disabledProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        disabledPropertyAttributeGetter,
+        disabledPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
   }
 
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "disabledFunction");
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 18;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, disabledFunctionMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "disabledFunction"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "DisabledInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cDisabledInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cDisabledInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -263,14 +367,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cDisabledInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cDisabledInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_disabled_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_disabled_interface.h
index ff9b4ee..e5734ce 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_disabled_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_disabled_interface.h
@@ -41,8 +41,7 @@
 class V8cDisabledInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dom_string_test_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dom_string_test_interface.cc
index ac89400..d9fb0da 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dom_string_test_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dom_string_test_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 12;
+
+
+
+
+
+
+
 
 
 void propertyAttributeGetter(
@@ -163,6 +168,7 @@
   return;
 }
 
+
 void readOnlyPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -200,6 +206,7 @@
 }
 
 
+
 void readOnlyTokenPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -237,6 +244,7 @@
 }
 
 
+
 void nullIsEmptyPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -311,6 +319,7 @@
   return;
 }
 
+
 void undefinedIsEmptyPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -385,6 +394,7 @@
   return;
 }
 
+
 void nullableUndefinedIsEmptyPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -460,86 +470,300 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "DOMStringTestInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "DOMStringTestInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "property",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    propertyAttributeGetter
-    ,propertyAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "readOnlyProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    readOnlyPropertyAttributeGetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "readOnlyTokenProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    readOnlyTokenPropertyAttributeGetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "nullIsEmptyProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    nullIsEmptyPropertyAttributeGetter
-    ,nullIsEmptyPropertyAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "undefinedIsEmptyProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    undefinedIsEmptyPropertyAttributeGetter
-    ,undefinedIsEmptyPropertyAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "nullableUndefinedIsEmptyProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    nullableUndefinedIsEmptyPropertyAttributeGetter
-    ,nullableUndefinedIsEmptyPropertyAttributeSetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "property");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        propertyAttributeGetter,
+        propertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 12;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "readOnlyProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        readOnlyPropertyAttributeGetter,
+        0,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "readOnlyTokenProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        readOnlyTokenPropertyAttributeGetter,
+        0,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "nullIsEmptyProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        nullIsEmptyPropertyAttributeGetter,
+        nullIsEmptyPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "undefinedIsEmptyProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        undefinedIsEmptyPropertyAttributeGetter,
+        undefinedIsEmptyPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "nullableUndefinedIsEmptyProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        nullableUndefinedIsEmptyPropertyAttributeGetter,
+        nullableUndefinedIsEmptyPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "DOMStringTestInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cDOMStringTestInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cDOMStringTestInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -549,14 +773,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cDOMStringTestInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cDOMStringTestInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dom_string_test_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dom_string_test_interface.h
index fbda377..e8fd6c3 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dom_string_test_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dom_string_test_interface.h
@@ -39,8 +39,7 @@
 class V8cDOMStringTestInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_enumeration_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_enumeration_interface.cc
index 68e0e21..ba0038b 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_enumeration_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_enumeration_interface.cc
@@ -34,12 +34,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -60,7 +62,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -68,18 +69,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -89,6 +86,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 19;
+
+
+
+
+
+
+
 
 
 void Constructor(const v8::FunctionCallbackInfo<v8::Value>& info) {
@@ -228,64 +233,164 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "EnumerationInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          Constructor,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetLength(0);
+  function_template->SetClassName(NewInternalString(isolate, "EnumerationInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "enumProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    enumPropertyAttributeGetter
-    ,enumPropertyAttributeSetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, optionalEnumWithDefaultMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "optionalEnumWithDefault",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "enumProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        enumPropertyAttributeGetter,
+        enumPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
   }
 
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "optionalEnumWithDefault");
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 19;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, optionalEnumWithDefaultMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "optionalEnumWithDefault"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "EnumerationInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cEnumerationInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cEnumerationInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -295,14 +400,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cEnumerationInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cEnumerationInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_enumeration_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_enumeration_interface.h
index 677d83d..7554cfc 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_enumeration_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_enumeration_interface.h
@@ -39,8 +39,7 @@
 class V8cEnumerationInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exception_object_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exception_object_interface.cc
index a389979..8c19687 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exception_object_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exception_object_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 20;
+
+
+
+
+
+
+
 
 
 void errorAttributeGetter(
@@ -126,6 +131,7 @@
 }
 
 
+
 void messageAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -164,59 +170,175 @@
 
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "ExceptionObjectInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "ExceptionObjectInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-  NOTIMPLEMENTED();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  {
+    // A spicy hack from Chromium in order to achieve
+    // https://heycam.github.io/webidl/#es-DOMException-specialness
+    // See https://cs.chromium.org/chromium/src/third_party/WebKit/Source/bindings/templates/interface_base.cpp.tmpl?l=630&rcl=0f7c2c752bb24ad08c17017e4e68401093fe76a0
+    v8::Local<v8::FunctionTemplate> intrinsic_error_prototype_interface_template =
+        v8::FunctionTemplate::New(isolate);
+    intrinsic_error_prototype_interface_template->RemovePrototype();
+    intrinsic_error_prototype_interface_template->SetIntrinsicDataProperty(
+        NewInternalString(isolate, "prototype"), v8::kErrorPrototype);
+    function_template->Inherit(intrinsic_error_prototype_interface_template);
+  }
+
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "error");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "error",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    errorAttributeGetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "message",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    messageAttributeGetter
-  );
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        errorAttributeGetter,
+        0,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "message");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        messageAttributeGetter,
+        0,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 20;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+  }
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "ExceptionObjectInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cExceptionObjectInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cExceptionObjectInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -226,14 +348,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cExceptionObjectInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cExceptionObjectInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exception_object_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exception_object_interface.h
index 7e8832a..0ec5d1b 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exception_object_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exception_object_interface.h
@@ -39,8 +39,7 @@
 class V8cExceptionObjectInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exceptions_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exceptions_interface.cc
index 574bb64..2476ba1 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exceptions_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exceptions_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 21;
+
+
+
+
+
+
+
 
 
 void Constructor(const v8::FunctionCallbackInfo<v8::Value>& info) {
@@ -215,64 +220,164 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "ExceptionsInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          Constructor,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetLength(0);
+  function_template->SetClassName(NewInternalString(isolate, "ExceptionsInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "attributeThrowsException",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    attributeThrowsExceptionAttributeGetter
-    ,attributeThrowsExceptionAttributeSetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, functionThrowsExceptionMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "functionThrowsException",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "attributeThrowsException");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        attributeThrowsExceptionAttributeGetter,
+        attributeThrowsExceptionAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
   }
 
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "functionThrowsException");
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 21;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, functionThrowsExceptionMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "functionThrowsException"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "ExceptionsInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cExceptionsInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cExceptionsInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -282,14 +387,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cExceptionsInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cExceptionsInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exceptions_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exceptions_interface.h
index 32e436b..13eb669 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exceptions_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exceptions_interface.h
@@ -39,8 +39,7 @@
 class V8cExceptionsInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_extended_idl_attributes_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_extended_idl_attributes_interface.cc
index 65e53a8..5de530d 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_extended_idl_attributes_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_extended_idl_attributes_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 22;
+
+
+
+
+
+
+
 
 
 void defaultAttributeGetter(
@@ -241,75 +246,196 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "ExtendedIDLAttributesInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "ExtendedIDLAttributesInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "default",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    defaultAttributeGetter
-    ,defaultAttributeSetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, callWithSettingsMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "callWithSettings",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "default");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        defaultAttributeGetter,
+        defaultAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
   }
 
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, clampArgumentMethod);
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "callWithSettings");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, callWithSettingsMethod);
     method_template->RemovePrototype();
+    method_template->SetLength(0);
     prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "clampArgument",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
+        NewInternalString(isolate, "callWithSettings"),
         method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "clampArgument");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, clampArgumentMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "clampArgument"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
   }
 
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "ExtendedIDLAttributesInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
-  interface_data->function_template.Set(isolate, function_template);
-}
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 22;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cExtendedIDLAttributesInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cExtendedIDLAttributesInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -319,14 +445,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cExtendedIDLAttributesInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cExtendedIDLAttributesInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_extended_idl_attributes_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_extended_idl_attributes_interface.h
index a23c50d..ae2015d 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_extended_idl_attributes_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_extended_idl_attributes_interface.h
@@ -39,8 +39,7 @@
 class V8cExtendedIDLAttributesInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_garbage_collection_test_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_garbage_collection_test_interface.cc
index 95e9107..d6ded64 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_garbage_collection_test_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_garbage_collection_test_interface.cc
@@ -35,12 +35,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -62,7 +64,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -70,18 +71,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -91,6 +88,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 23;
+
+
+
+
+
+
+
 
 
 void Constructor(const v8::FunctionCallbackInfo<v8::Value>& info) {
@@ -184,6 +189,7 @@
   return;
 }
 
+
 void nextAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -259,60 +265,165 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "GarbageCollectionTestInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          Constructor,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetLength(0);
+  function_template->SetClassName(NewInternalString(isolate, "GarbageCollectionTestInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "previous",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    previousAttributeGetter
-    ,previousAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "next",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    nextAttributeGetter
-    ,nextAttributeSetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "previous");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        previousAttributeGetter,
+        previousAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 23;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "next");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        nextAttributeGetter,
+        nextAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "GarbageCollectionTestInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cGarbageCollectionTestInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cGarbageCollectionTestInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -322,14 +433,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cGarbageCollectionTestInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cGarbageCollectionTestInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_garbage_collection_test_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_garbage_collection_test_interface.h
index 8b5f975..b452e3f 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_garbage_collection_test_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_garbage_collection_test_interface.h
@@ -39,8 +39,7 @@
 class V8cGarbageCollectionTestInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_global_interface_parent.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_global_interface_parent.cc
index c1f2c66..d25a0d6 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_global_interface_parent.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_global_interface_parent.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,13 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 24;
+
+
+
+
+
+
 
 
 
@@ -120,57 +124,129 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "GlobalInterfaceParent",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "GlobalInterfaceParent"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, parentOperationMethod);
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "parentOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, parentOperationMethod);
     method_template->RemovePrototype();
+    method_template->SetLength(0);
     prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "parentOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
+        NewInternalString(isolate, "parentOperation"),
         method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
   }
 
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "GlobalInterfaceParent"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
-  interface_data->function_template.Set(isolate, function_template);
-}
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 24;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cGlobalInterfaceParent::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cGlobalInterfaceParent::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -180,14 +256,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cGlobalInterfaceParent::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cGlobalInterfaceParent::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_global_interface_parent.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_global_interface_parent.h
index 5ad1e1e..bbccc96 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_global_interface_parent.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_global_interface_parent.h
@@ -39,8 +39,7 @@
 class V8cGlobalInterfaceParent {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_indexed_getter_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_indexed_getter_interface.cc
index a59977c..92f94ea 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_indexed_getter_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_indexed_getter_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,140 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 26;
+
+
+
+
+
+void IndexedPropertyGetterCallback(
+    uint32_t index,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  IndexedGetterInterface* impl =
+      wrapper_private->wrappable<IndexedGetterInterface>().get();
+  if (index >= impl->length()) {
+    // |index| is out of bounds, so return undefined.
+    return;
+  }
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(isolate,
+              impl->IndexedGetter(index),
+              &result_value);
+  }
+  info.GetReturnValue().Set(result_value);
+}
+
+void IndexedPropertyDescriptorCallback(
+    uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
+  // TODO: Figure out under what conditions this gets called.  It's not
+  // getting called in our tests.
+  NOTIMPLEMENTED();
+}
+
+void IndexedPropertyEnumeratorCallback(
+    const v8::PropertyCallbackInfo<v8::Array>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  IndexedGetterInterface* impl =
+      wrapper_private->wrappable<IndexedGetterInterface>().get();
+  const uint32_t length = impl->length();
+  v8::Local<v8::Array> array = v8::Array::New(isolate, length);
+  for (uint32_t i = 0; i < length; ++i) {
+    array->Set(i, v8::Integer::New(isolate, i));
+  }
+  info.GetReturnValue().Set(array);
+}
+
+void IndexedPropertyDefinerCallback(
+    uint32_t index, const v8::PropertyDescriptor& desc,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  // TODO: Figure out under what conditions this gets called.  It's not
+  // getting called in our tests.
+  NOTIMPLEMENTED();
+}
+
+
+void IndexedPropertySetterCallback(
+    uint32_t index,
+    v8::Local<v8::Value> value,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  IndexedGetterInterface* impl =
+      wrapper_private->wrappable<IndexedGetterInterface>().get();
+  if (index >= impl->length()) {
+    return;
+  }
+  TypeTraits<uint32_t>::ConversionType native_value;
+  FromJSValue(isolate, value, kNoConversionFlags,
+              &exception_state, &native_value);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+
+  impl->IndexedSetter(index, native_value);
+  result_value = v8::Undefined(isolate);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+  info.GetReturnValue().Set(value);
+}
+
+void IndexedPropertyDeleterCallback(
+    uint32_t index,
+    const v8::PropertyCallbackInfo<v8::Boolean>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  IndexedGetterInterface* impl =
+      wrapper_private->wrappable<IndexedGetterInterface>().get();
+  if (index >= impl->length()) {
+    return;
+  }
+
+  impl->IndexedDeleter(index);
+  result_value = v8::Undefined(isolate);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+  info.GetReturnValue().Set(v8::Boolean::New(isolate, true));
+}
+
 
 
 void lengthAttributeGetter(
@@ -281,85 +412,240 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "IndexedGetterInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "IndexedGetterInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "length",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    lengthAttributeGetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, indexedDeleterMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "indexedDeleter",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "length");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        lengthAttributeGetter,
+        0,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
   }
 
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, indexedGetterMethod);
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "indexedDeleter");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, indexedDeleterMethod);
     method_template->RemovePrototype();
+    method_template->SetLength(1);
     prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "indexedGetter",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
+        NewInternalString(isolate, "indexedDeleter"),
         method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "indexedGetter");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, indexedGetterMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "indexedGetter"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "indexedSetter");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, indexedSetterMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(2);
+    prototype_template->Set(
+        NewInternalString(isolate, "indexedSetter"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
   }
 
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "IndexedGetterInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, indexedSetterMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "indexedSetter",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    v8::IndexedPropertyHandlerConfiguration indexed_property_handler_configuration = {
+      IndexedPropertyGetterCallback,
+      IndexedPropertySetterCallback,
+      IndexedPropertyDescriptorCallback,
+      IndexedPropertyDeleterCallback,
+      IndexedPropertyEnumeratorCallback,
+      IndexedPropertyDefinerCallback
+    };
+    instance_template->SetHandler(indexed_property_handler_configuration);
   }
 
-
-  interface_data->function_template.Set(isolate, function_template);
-}
-
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 26;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cIndexedGetterInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cIndexedGetterInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -369,14 +655,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cIndexedGetterInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cIndexedGetterInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_indexed_getter_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_indexed_getter_interface.h
index 530f38a..f80fdc4 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_indexed_getter_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_indexed_getter_interface.h
@@ -39,8 +39,7 @@
 class V8cIndexedGetterInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any.cc
index d150da6..22fbc8e 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 27;
+
+
+
+
+
+
+
 
 
 void Constructor(const v8::FunctionCallbackInfo<v8::Value>& info) {
@@ -107,7 +112,6 @@
 
 
 
-
 void getAnyMethod(const v8::FunctionCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
   v8::Local<v8::Object> object = info.This();
@@ -189,68 +193,163 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "InterfaceWithAny",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          Constructor,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetLength(0);
+  function_template->SetClassName(NewInternalString(isolate, "InterfaceWithAny"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, getAnyMethod);
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "getAny");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, getAnyMethod);
     method_template->RemovePrototype();
+    method_template->SetLength(0);
     prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "getAny",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
+        NewInternalString(isolate, "getAny"),
         method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "setAny");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, setAnyMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "setAny"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
   }
 
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, setAnyMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "setAny",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "InterfaceWithAny"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 27;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cInterfaceWithAny::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cInterfaceWithAny::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -260,14 +359,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cInterfaceWithAny::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cInterfaceWithAny::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any.h
index c088986..7f029c4 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any.h
@@ -39,8 +39,7 @@
 class V8cInterfaceWithAny {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any_dictionary.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any_dictionary.cc
index 27fb410..d707a52 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any_dictionary.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any_dictionary.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 28;
+
+
+
+
+
+
+
 
 
 void Constructor(const v8::FunctionCallbackInfo<v8::Value>& info) {
@@ -107,7 +112,6 @@
 
 
 
-
 void getAnyMethod(const v8::FunctionCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
   v8::Local<v8::Object> object = info.This();
@@ -261,90 +265,229 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "InterfaceWithAnyDictionary",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          Constructor,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetLength(0);
+  function_template->SetClassName(NewInternalString(isolate, "InterfaceWithAnyDictionary"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, getAnyMethod);
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "getAny");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, getAnyMethod);
     method_template->RemovePrototype();
+    method_template->SetLength(0);
     prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "getAny",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
+        NewInternalString(isolate, "getAny"),
         method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "hasAny");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, hasAnyMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "hasAny"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "hasAnyDefault");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, hasAnyDefaultMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "hasAnyDefault"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "setAny");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, setAnyMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "setAny"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
   }
 
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, hasAnyMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "hasAny",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, hasAnyDefaultMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "hasAnyDefault",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, setAnyMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "setAny",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "InterfaceWithAnyDictionary"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 28;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cInterfaceWithAnyDictionary::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cInterfaceWithAnyDictionary::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -354,14 +497,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cInterfaceWithAnyDictionary::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cInterfaceWithAnyDictionary::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any_dictionary.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any_dictionary.h
index af10b0a..124c7ab 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any_dictionary.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any_dictionary.h
@@ -39,8 +39,7 @@
 class V8cInterfaceWithAnyDictionary {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_date.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_date.cc
index 0451ae2..4539d83 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_date.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_date.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 29;
+
+
+
+
+
+
+
 
 
 void Constructor(const v8::FunctionCallbackInfo<v8::Value>& info) {
@@ -107,7 +112,6 @@
 
 
 
-
 void getDateMethod(const v8::FunctionCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
   v8::Local<v8::Object> object = info.This();
@@ -189,68 +193,163 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "InterfaceWithDate",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          Constructor,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetLength(0);
+  function_template->SetClassName(NewInternalString(isolate, "InterfaceWithDate"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, getDateMethod);
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "getDate");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, getDateMethod);
     method_template->RemovePrototype();
+    method_template->SetLength(0);
     prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "getDate",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
+        NewInternalString(isolate, "getDate"),
         method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "setDate");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, setDateMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "setDate"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
   }
 
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, setDateMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "setDate",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "InterfaceWithDate"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 29;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cInterfaceWithDate::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cInterfaceWithDate::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -260,14 +359,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cInterfaceWithDate::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cInterfaceWithDate::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_date.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_date.h
index 9154494..49a1bc0 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_date.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_date.h
@@ -39,8 +39,7 @@
 class V8cInterfaceWithDate {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_unsupported_properties.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_unsupported_properties.cc
index fd54ff0..c815e50 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_unsupported_properties.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_unsupported_properties.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 30;
+
+
+
+
+
+
+
 
 
 void supportedAttributeAttributeGetter(
@@ -127,52 +132,130 @@
 
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "InterfaceWithUnsupportedProperties",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "InterfaceWithUnsupportedProperties"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "supportedAttribute",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    supportedAttributeAttributeGetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "supportedAttribute");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        supportedAttributeAttributeGetter,
+        0,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 30;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+  }
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "InterfaceWithUnsupportedProperties"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cInterfaceWithUnsupportedProperties::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cInterfaceWithUnsupportedProperties::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -182,14 +265,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cInterfaceWithUnsupportedProperties::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cInterfaceWithUnsupportedProperties::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_unsupported_properties.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_unsupported_properties.h
index 5d68181..dfd519f 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_unsupported_properties.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_unsupported_properties.h
@@ -39,8 +39,7 @@
 class V8cInterfaceWithUnsupportedProperties {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_constructor_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_constructor_interface.cc
index fffa517..692da5b 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_constructor_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_constructor_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 31;
+
+
+
+
+
+
+
 
 
 void Constructor(const v8::FunctionCallbackInfo<v8::Value>& info) {
@@ -107,46 +112,96 @@
 
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "NamedConstructorInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          Constructor,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetLength(0);
+  function_template->SetClassName(NewInternalString(isolate, "NamedConstructorInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "NamedConstructorInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
 
 
-
-  interface_data->function_template.Set(isolate, function_template);
-}
-
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 31;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cNamedConstructorInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cNamedConstructorInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -156,14 +211,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cNamedConstructorInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cNamedConstructorInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_constructor_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_constructor_interface.h
index b08e649..76fcdb2 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_constructor_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_constructor_interface.h
@@ -39,8 +39,7 @@
 class V8cNamedConstructorInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_getter_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_getter_interface.cc
index 4c63633..9d39173 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_getter_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_getter_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,146 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 32;
+
+
+void NamedPropertyGetterCallback(
+    v8::Local<v8::Name> property,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  NamedGetterInterface* impl =
+      wrapper_private->wrappable<NamedGetterInterface>().get();
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  if (!impl->CanQueryNamedProperty(property_name)) {
+    return;
+  }
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(isolate,
+              impl->NamedGetter(property_name),
+              &result_value);
+  }
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+  info.GetReturnValue().Set(result_value);
+  DCHECK(!exception_state.is_exception_set());
+}
+
+void NamedPropertyQueryCallback(
+    v8::Local<v8::Name> property,
+    const v8::PropertyCallbackInfo<v8::Integer>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  NamedGetterInterface* impl =
+      wrapper_private->wrappable<NamedGetterInterface>().get();
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  bool result = impl->CanQueryNamedProperty(property_name);
+  if (!result) {
+    return;
+  }
+  // https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty
+  // 2.7. If |O| implements an interface with a named property setter, then set
+  //      desc.[[Writable]] to true, otherwise set it to false.
+  // 2.8. If |O| implements an interface with the
+  //      [LegacyUnenumerableNamedProperties] extended attribute, then set
+  //      desc.[[Enumerable]] to false, otherwise set it to true.
+  info.GetReturnValue().Set(v8::DontEnum | v8::ReadOnly);
+}
+
+void NamedPropertyEnumeratorCallback(
+    const v8::PropertyCallbackInfo<v8::Array>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  NamedGetterInterface* impl =
+      wrapper_private->wrappable<NamedGetterInterface>().get();
+  v8::Local<v8::Array> array = v8::Array::New(isolate);
+  V8cPropertyEnumerator property_enumerator(isolate, &array);
+  impl->EnumerateNamedProperties(&property_enumerator);
+  info.GetReturnValue().Set(array);
+}
+
+
+void NamedPropertySetterCallback(
+    v8::Local<v8::Name> property,
+    v8::Local<v8::Value> value,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  NamedGetterInterface* impl =
+      wrapper_private->wrappable<NamedGetterInterface>().get();
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  TypeTraits<std::string>::ConversionType native_value;
+  FromJSValue(isolate, value, kNoConversionFlags,
+              &exception_state, &native_value);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+
+  impl->NamedSetter(property_name, native_value);
+  result_value = v8::Undefined(isolate);
+  DCHECK(!exception_state.is_exception_set());
+}
+
+void NamedPropertyDeleterCallback(
+    v8::Local<v8::Name> property,
+    const v8::PropertyCallbackInfo<v8::Boolean>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  NamedGetterInterface* impl =
+      wrapper_private->wrappable<NamedGetterInterface>().get();
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  if (!impl->CanQueryNamedProperty(property_name)) {
+    return;
+  }
+
+  impl->NamedDeleter(property_name);
+  result_value = v8::Undefined(isolate);
+  DCHECK(!exception_state.is_exception_set());
+}
+
+
+
 
 
 
@@ -244,79 +381,207 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "NamedGetterInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "NamedGetterInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, namedDeleterMethod);
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "namedDeleter");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, namedDeleterMethod);
     method_template->RemovePrototype();
+    method_template->SetLength(1);
     prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "namedDeleter",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
+        NewInternalString(isolate, "namedDeleter"),
         method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "namedGetter");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, namedGetterMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "namedGetter"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "namedSetter");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, namedSetterMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(2);
+    prototype_template->Set(
+        NewInternalString(isolate, "namedSetter"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
   }
 
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, namedGetterMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "namedGetter",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "NamedGetterInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, namedSetterMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "namedSetter",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    v8::NamedPropertyHandlerConfiguration named_property_handler_configuration = {
+      NamedPropertyGetterCallback,
+      NamedPropertySetterCallback,
+      NamedPropertyQueryCallback,
+      NamedPropertyDeleterCallback,
+      NamedPropertyEnumeratorCallback,
+      v8::Local<v8::Value>(),
+      static_cast<v8::PropertyHandlerFlags>(int(v8::PropertyHandlerFlags::kNonMasking) | int(v8::PropertyHandlerFlags::kOnlyInterceptStrings))
+    };
+    instance_template->SetHandler(named_property_handler_configuration);
   }
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
-
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 32;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cNamedGetterInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cNamedGetterInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -326,14 +591,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cNamedGetterInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cNamedGetterInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_getter_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_getter_interface.h
index 2160453..12f0ec7 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_getter_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_getter_interface.h
@@ -39,8 +39,7 @@
 class V8cNamedGetterInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_indexed_getter_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_indexed_getter_interface.cc
index f90afa4..ff6c171 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_indexed_getter_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_indexed_getter_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,6 +84,221 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 33;
+
+
+void NamedPropertyGetterCallback(
+    v8::Local<v8::Name> property,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  NamedIndexedGetterInterface* impl =
+      wrapper_private->wrappable<NamedIndexedGetterInterface>().get();
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  if (!impl->CanQueryNamedProperty(property_name)) {
+    return;
+  }
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(isolate,
+              impl->NamedGetter(property_name),
+              &result_value);
+  }
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+  info.GetReturnValue().Set(result_value);
+  DCHECK(!exception_state.is_exception_set());
+}
+
+void NamedPropertyQueryCallback(
+    v8::Local<v8::Name> property,
+    const v8::PropertyCallbackInfo<v8::Integer>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  NamedIndexedGetterInterface* impl =
+      wrapper_private->wrappable<NamedIndexedGetterInterface>().get();
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  bool result = impl->CanQueryNamedProperty(property_name);
+  if (!result) {
+    return;
+  }
+  // https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty
+  // 2.7. If |O| implements an interface with a named property setter, then set
+  //      desc.[[Writable]] to true, otherwise set it to false.
+  // 2.8. If |O| implements an interface with the
+  //      [LegacyUnenumerableNamedProperties] extended attribute, then set
+  //      desc.[[Enumerable]] to false, otherwise set it to true.
+  info.GetReturnValue().Set(v8::DontEnum | v8::ReadOnly);
+}
+
+void NamedPropertyEnumeratorCallback(
+    const v8::PropertyCallbackInfo<v8::Array>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  NamedIndexedGetterInterface* impl =
+      wrapper_private->wrappable<NamedIndexedGetterInterface>().get();
+  v8::Local<v8::Array> array = v8::Array::New(isolate);
+  V8cPropertyEnumerator property_enumerator(isolate, &array);
+  impl->EnumerateNamedProperties(&property_enumerator);
+  info.GetReturnValue().Set(array);
+}
+
+
+void NamedPropertySetterCallback(
+    v8::Local<v8::Name> property,
+    v8::Local<v8::Value> value,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  NamedIndexedGetterInterface* impl =
+      wrapper_private->wrappable<NamedIndexedGetterInterface>().get();
+  std::string property_name = *v8::String::Utf8Value(isolate, property);
+  TypeTraits<std::string>::ConversionType native_value;
+  FromJSValue(isolate, value, kNoConversionFlags,
+              &exception_state, &native_value);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+
+  impl->NamedSetter(property_name, native_value);
+  result_value = v8::Undefined(isolate);
+  DCHECK(!exception_state.is_exception_set());
+}
+
+
+
+void IndexedPropertyGetterCallback(
+    uint32_t index,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  NamedIndexedGetterInterface* impl =
+      wrapper_private->wrappable<NamedIndexedGetterInterface>().get();
+  if (index >= impl->length()) {
+    // |index| is out of bounds, so return undefined.
+    return;
+  }
+
+  if (!exception_state.is_exception_set()) {
+    ToJSValue(isolate,
+              impl->IndexedGetter(index),
+              &result_value);
+  }
+  info.GetReturnValue().Set(result_value);
+}
+
+void IndexedPropertyDescriptorCallback(
+    uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
+  // TODO: Figure out under what conditions this gets called.  It's not
+  // getting called in our tests.
+  NOTIMPLEMENTED();
+}
+
+void IndexedPropertyEnumeratorCallback(
+    const v8::PropertyCallbackInfo<v8::Array>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  NamedIndexedGetterInterface* impl =
+      wrapper_private->wrappable<NamedIndexedGetterInterface>().get();
+  const uint32_t length = impl->length();
+  v8::Local<v8::Array> array = v8::Array::New(isolate, length);
+  for (uint32_t i = 0; i < length; ++i) {
+    array->Set(i, v8::Integer::New(isolate, i));
+  }
+  info.GetReturnValue().Set(array);
+}
+
+void IndexedPropertyDefinerCallback(
+    uint32_t index, const v8::PropertyDescriptor& desc,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  // TODO: Figure out under what conditions this gets called.  It's not
+  // getting called in our tests.
+  NOTIMPLEMENTED();
+}
+
+
+void IndexedPropertySetterCallback(
+    uint32_t index,
+    v8::Local<v8::Value> value,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::Local<v8::Object> object = info.This();
+  V8cExceptionState exception_state{isolate};
+  v8::Local<v8::Value> result_value;
+
+  WrapperPrivate* wrapper_private =
+      WrapperPrivate::GetFromWrapperObject(object);
+  if (!wrapper_private) {
+    NOTIMPLEMENTED();
+    return;
+  }
+  NamedIndexedGetterInterface* impl =
+      wrapper_private->wrappable<NamedIndexedGetterInterface>().get();
+  if (index >= impl->length()) {
+    return;
+  }
+  TypeTraits<uint32_t>::ConversionType native_value;
+  FromJSValue(isolate, value, kNoConversionFlags,
+              &exception_state, &native_value);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+
+  impl->IndexedSetter(index, native_value);
+  result_value = v8::Undefined(isolate);
+  if (exception_state.is_exception_set()) {
+    return;
+  }
+  info.GetReturnValue().Set(value);
+}
+
+
 
 
 void lengthAttributeGetter(
@@ -126,6 +338,7 @@
 }
 
 
+
 void propertyOnBaseClassAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -447,114 +660,352 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "NamedIndexedGetterInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "NamedIndexedGetterInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "length",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    lengthAttributeGetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "propertyOnBaseClass",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    propertyOnBaseClassAttributeGetter
-    ,propertyOnBaseClassAttributeSetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "length");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        lengthAttributeGetter,
+        0,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "propertyOnBaseClass");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        propertyOnBaseClassAttributeGetter,
+        propertyOnBaseClassAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "indexedGetter");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, indexedGetterMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "indexedGetter"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "indexedSetter");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, indexedSetterMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(2);
+    prototype_template->Set(
+        NewInternalString(isolate, "indexedSetter"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "namedGetter");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, namedGetterMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "namedGetter"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "namedSetter");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, namedSetterMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(2);
+    prototype_template->Set(
+        NewInternalString(isolate, "namedSetter"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "operationOnBaseClass");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, operationOnBaseClassMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "operationOnBaseClass"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "NamedIndexedGetterInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, indexedGetterMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "indexedGetter",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    v8::NamedPropertyHandlerConfiguration named_property_handler_configuration = {
+      NamedPropertyGetterCallback,
+      NamedPropertySetterCallback,
+      NamedPropertyQueryCallback,
+      nullptr,
+      NamedPropertyEnumeratorCallback,
+      v8::Local<v8::Value>(),
+      static_cast<v8::PropertyHandlerFlags>(int(v8::PropertyHandlerFlags::kNonMasking) | int(v8::PropertyHandlerFlags::kOnlyInterceptStrings))
+    };
+    instance_template->SetHandler(named_property_handler_configuration);
   }
 
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, indexedSetterMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "indexedSetter",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    v8::IndexedPropertyHandlerConfiguration indexed_property_handler_configuration = {
+      IndexedPropertyGetterCallback,
+      IndexedPropertySetterCallback,
+      IndexedPropertyDescriptorCallback,
+      nullptr,
+      IndexedPropertyEnumeratorCallback,
+      IndexedPropertyDefinerCallback
+    };
+    instance_template->SetHandler(indexed_property_handler_configuration);
   }
 
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, namedGetterMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "namedGetter",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, namedSetterMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "namedSetter",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, operationOnBaseClassMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "operationOnBaseClass",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-
-  interface_data->function_template.Set(isolate, function_template);
-}
-
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 33;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cNamedIndexedGetterInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cNamedIndexedGetterInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -564,14 +1015,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cNamedIndexedGetterInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cNamedIndexedGetterInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_indexed_getter_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_indexed_getter_interface.h
index f0d55dc..ea787aa 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_indexed_getter_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_indexed_getter_interface.h
@@ -39,8 +39,7 @@
 class V8cNamedIndexedGetterInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nested_put_forwards_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nested_put_forwards_interface.cc
index e61240f..e0f5669 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nested_put_forwards_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nested_put_forwards_interface.cc
@@ -35,12 +35,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -62,7 +64,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -70,18 +71,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -91,6 +88,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 34;
+
+
+
+
+
+
+
 
 
 void nestedForwardingAttributeAttributeGetter(
@@ -190,53 +195,130 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "NestedPutForwardsInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "NestedPutForwardsInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "nestedForwardingAttribute",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    nestedForwardingAttributeAttributeGetter
-    ,nestedForwardingAttributeAttributeSetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "nestedForwardingAttribute");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
 
 
-  interface_data->function_template.Set(isolate, function_template);
-}
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        nestedForwardingAttributeAttributeGetter,
+        nestedForwardingAttributeAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
 
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 34;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
+  }
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "NestedPutForwardsInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
+
+
+
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cNestedPutForwardsInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cNestedPutForwardsInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -246,14 +328,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cNestedPutForwardsInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cNestedPutForwardsInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nested_put_forwards_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nested_put_forwards_interface.h
index d052834..25633dc 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nested_put_forwards_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nested_put_forwards_interface.h
@@ -39,8 +39,7 @@
 class V8cNestedPutForwardsInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_constructor_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_constructor_interface.cc
index ccda656..570064a 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_constructor_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_constructor_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,49 +84,106 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 35;
 
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "NoConstructorInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+
+
+
+
+
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "NoConstructorInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "NoConstructorInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
 
 
-
-  interface_data->function_template.Set(isolate, function_template);
-}
-
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 35;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cNoConstructorInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cNoConstructorInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -139,14 +193,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cNoConstructorInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cNoConstructorInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_constructor_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_constructor_interface.h
index b5065a6..7b08cf2 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_constructor_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_constructor_interface.h
@@ -39,8 +39,7 @@
 class V8cNoConstructorInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
-  static v8::Local<v8::Object> GetInterfaceObject(v8::Isolate* isolate, v8::Local<v8::Object> global_object);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_interface_object_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_interface_object_interface.cc
index 640f64e..3bc962c 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_interface_object_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_interface_object_interface.cc
@@ -33,12 +33,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -58,7 +60,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -66,18 +67,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -87,49 +84,106 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 36;
 
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "NoInterfaceObjectInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+
+
+
+
+
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "NoInterfaceObjectInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
+
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
+
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
+
+  // https://heycam.github.io/webidl/#es-stringifier
+  // 3.6.8.2. Stringifiers
+  prototype_template->Set(
+      v8::Symbol::GetToStringTag(isolate),
+      NewInternalString(isolate, "NoInterfaceObjectInterface"),
+      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum));
 
 
 
-
-  interface_data->function_template.Set(isolate, function_template);
-}
-
-inline InterfaceData* GetInterfaceData(V8cGlobalEnvironment* global_environment) {
-  const int kInterfaceUniqueId = 36;
-  // By convention, the |V8cGlobalEnvironment| that we are associated with
-  // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked
-  // for it to be there in the first place, and could not have conflicted with
-  // any other interface.
-  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 }  // namespace
 
-v8::Local<v8::Object> V8cNoInterfaceObjectInterface::CreateWrapper(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
+
+v8::Local<v8::Object> V8cNoInterfaceObjectInterface::CreateWrapper(
+    v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) {
   EscapableEntryScope entry_scope(isolate);
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
   }
-  DCHECK(!interface_data->function_template.IsEmpty());
+  v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId);
 
-  v8::Local<v8::FunctionTemplate> function_template = interface_data->function_template.Get(isolate);
   DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
   v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked();
   DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount);
@@ -139,14 +193,13 @@
   return entry_scope.Escape(object);
 }
 
-v8::Local<v8::FunctionTemplate> V8cNoInterfaceObjectInterface::CreateTemplate(v8::Isolate* isolate) {
-  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
-  InterfaceData* interface_data = GetInterfaceData(global_environment);
-  if (interface_data->function_template.IsEmpty()) {
-    InitializeTemplateAndInterfaceObject(isolate, interface_data);
-  }
 
-  return interface_data->function_template.Get(isolate);
+v8::Local<v8::FunctionTemplate> V8cNoInterfaceObjectInterface::GetTemplate(v8::Isolate* isolate) {
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) {
+    InitializeTemplate(isolate);
+  }
+  return global_environment->GetInterfaceData(kInterfaceUniqueId);
 }
 
 
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_interface_object_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_interface_object_interface.h
index cad6f66..77e220c 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_interface_object_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_interface_object_interface.h
@@ -39,7 +39,7 @@
 class V8cNoInterfaceObjectInterface {
  public:
   static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable);
-  static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate);
+  static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate);
 };
 
 }  // namespace testing
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nullable_types_test_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nullable_types_test_interface.cc
index 7cc380e..8ab1042 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nullable_types_test_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nullable_types_test_interface.cc
@@ -35,12 +35,14 @@
 #include "cobalt/script/v8c/callback_function_conversion.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
 #include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/helpers.h"
 #include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/script/v8c/type_traits.h"
 #include "cobalt/script/v8c/v8c_callback_function.h"
 #include "cobalt/script/v8c/v8c_callback_interface_holder.h"
 #include "cobalt/script/v8c/v8c_exception_state.h"
 #include "cobalt/script/v8c/v8c_global_environment.h"
+#include "cobalt/script/v8c/v8c_property_enumerator.h"
 #include "cobalt/script/v8c/v8c_value_handle.h"
 #include "cobalt/script/v8c/wrapper_private.h"
 #include "v8/include/v8.h"
@@ -62,7 +64,6 @@
 using cobalt::script::v8c::EntryScope;
 using cobalt::script::v8c::EscapableEntryScope;
 using cobalt::script::v8c::FromJSValue;
-using cobalt::script::v8c::InterfaceData;
 using cobalt::script::v8c::kConversionFlagClamped;
 using cobalt::script::v8c::kConversionFlagNullable;
 using cobalt::script::v8c::kConversionFlagObjectOnly;
@@ -70,18 +71,14 @@
 using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString;
 using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString;
 using cobalt::script::v8c::kNoConversionFlags;
+using cobalt::script::v8c::NewInternalString;
 using cobalt::script::v8c::ToJSValue;
 using cobalt::script::v8c::TypeTraits;
 using cobalt::script::v8c::V8cExceptionState;
 using cobalt::script::v8c::V8cGlobalEnvironment;
+using cobalt::script::v8c::V8cPropertyEnumerator;
 using cobalt::script::v8c::WrapperFactory;
 using cobalt::script::v8c::WrapperPrivate;
-
-v8::Local<v8::Object> DummyFunctor(v8::Isolate*, const scoped_refptr<Wrappable>&) {
-  NOTIMPLEMENTED();
-  return {};
-}
-
 }  // namespace
 
 namespace cobalt {
@@ -91,6 +88,14 @@
 
 namespace {
 
+const int kInterfaceUniqueId = 37;
+
+
+
+
+
+
+
 
 
 void nullableBooleanPropertyAttributeGetter(
@@ -167,6 +172,7 @@
   return;
 }
 
+
 void nullableNumericPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -241,6 +247,7 @@
   return;
 }
 
+
 void nullableStringPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -315,6 +322,7 @@
   return;
 }
 
+
 void nullableObjectPropertyAttributeGetter(
     v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
   v8::Isolate* isolate = info.GetIsolate();
@@ -718,162 +726,496 @@
 }
 
 
-void InitializeTemplateAndInterfaceObject(v8::Isolate* isolate, InterfaceData* interface_data) {
-  v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(isolate);
-  function_template->SetClassName(
-    v8::String::NewFromUtf8(isolate, "NullableTypesTestInterface",
-        v8::NewStringType::kInternalized).ToLocalChecked());
+
+void InitializeTemplate(v8::Isolate* isolate) {
+  // https://heycam.github.io/webidl/#interface-object
+  // 3.6.1. Interface object
+  //
+  // The interface object for a given interface is a built-in function object.
+  // It has properties that correspond to the constants and static operations
+  // defined on that interface, as described in sections 3.6.6 Constants and
+  // 3.6.8 Operations.
+  //
+  // If the interface is declared with a [Constructor] extended attribute,
+  // then the interface object can be called as a constructor to create an
+  // object that implements that interface. Calling that interface as a
+  // function will throw an exception.
+  //
+  // Interface objects whose interfaces are not declared with a [Constructor]
+  // extended attribute will throw when called, both as a function and as a
+  // constructor.
+  //
+  // An interface object for a non-callback interface has an associated object
+  // called the interface prototype object. This object has properties that
+  // correspond to the regular attributes and regular operations defined on
+  // the interface, and is described in more detail in 3.6.3 Interface
+  // prototype object.
+  v8::Local<v8::FunctionTemplate> function_template =
+      v8::FunctionTemplate::New(
+          isolate,
+          nullptr,
+          v8::Local<v8::Value>(),
+          v8::Local<v8::Signature>(),
+          0);
+  function_template->SetClassName(NewInternalString(isolate, "NullableTypesTestInterface"));
+  function_template->ReadOnlyPrototype();
+
+  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
   v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate();
   instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount);
 
-
-  v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate();
+  V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
+  global_environment->AddInterfaceData(kInterfaceUniqueId, function_template);
 
 
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "nullableBooleanProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    nullableBooleanPropertyAttributeGetter
-    ,nullableBooleanPropertyAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "nullableNumericProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    nullableNumericPropertyAttributeGetter
-    ,nullableNumericPropertyAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "nullableStringProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    nullableStringPropertyAttributeGetter
-    ,nullableStringPropertyAttributeSetter
-  );
-  instance_template->SetAccessor(
-    v8::String::NewFromUtf8(isolate, "nullableObjectProperty",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked(),
-    nullableObjectPropertyAttributeGetter
-    ,nullableObjectPropertyAttributeSetter
-  );
+  // https://heycam.github.io/webidl/#es-constants
+  // 3.6.6. Constants
+  //
+  // For each exposed constant defined on an interface A, there must be a
+  // corresponding property. The property has the following characteristics:
 
+  // https://heycam.github.io/webidl/#es-attributes
+  // 3.6.7. Attributes
+  //
+  // For each exposed attribute of the interface there must exist a
+  // corresponding property. The characteristics of this property are as
+  // follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, nullableBooleanArgumentMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "nullableBooleanArgument",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "nullableBooleanProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        nullableBooleanPropertyAttributeGetter,
+        nullableBooleanPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "nullableNumericProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        nullableNumericPropertyAttributeGetter,
+        nullableNumericPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "nullableStringProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        nullableStringPropertyAttributeGetter,
+        nullableStringPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
+  }
+  {
+    // The name of the property is the identifier of the attribute.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "nullableObjectProperty");
+
+    // The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]:
+    // true, [[Configurable]]: configurable }, where: configurable is false if
+    // the attribute was declared with the [Unforgeable] extended attribute and
+    // true otherwise;
+    bool configurable = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        configurable ? v8::None : v8::DontDelete);
+
+    // G is the attribute getter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property;
+    // and
+    //
+    // S is the attribute setter created given the attribute, the interface, and
+    // the relevant Realm of the object that is the location of the property.
+
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    prototype_template->SetAccessor(
+        name,
+        nullableObjectPropertyAttributeGetter,
+        nullableObjectPropertyAttributeSetter,
+        v8::Local<v8::Value>(),
+        v8::DEFAULT,
+        attributes);
+
   }
 
+  // https://heycam.github.io/webidl/#es-operations
+  // 3.6.8. Operations
+  //
+  // For each unique identifier of an exposed operation defined on the
+  // interface, there must exist a corresponding property, unless the effective
+  // overload set for that identifier and operation and with an argument count
+  // of 0 has no entries.
+  //
+  // The characteristics of this property are as follows:
   {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, nullableBooleanOperationMethod);
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "nullableBooleanArgument");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, nullableBooleanArgumentMethod);
     method_template->RemovePrototype();
+    method_template->SetLength(1);
     prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "nullableBooleanOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
+        NewInternalString(isolate, "nullableBooleanArgument"),
         method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "nullableBooleanOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, nullableBooleanOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "nullableBooleanOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "nullableNumericArgument");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, nullableNumericArgumentMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "nullableNumericArgument"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "nullableNumericOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, nullableNumericOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "nullableNumericOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "nullableObjectArgument");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, nullableObjectArgumentMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "nullableObjectArgument"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "nullableObjectOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, nullableObjectOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "nullableObjectOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "nullableStringArgument");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, nullableStringArgumentMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(1);
+    prototype_template->Set(
+        NewInternalString(isolate, "nullableStringArgument"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
+  }
+  {
+    // The name of the property is the identifier.
+    v8::Local<v8::String> name = NewInternalString(
+        isolate,
+        "nullableStringOperation");
+
+    // The property has attributes { [[Writable]]: B, [[Enumerable]]: true,
+    // [[Configurable]]: B }, where B is false if the operation is unforgeable
+    // on the interface, and true otherwise.
+    bool B = true;
+    v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(
+        B ? v8::None : (v8::ReadOnly | v8::DontDelete));
+
+    // The location of the property is determined as follows:
+    // Otherwise, the property exists solely on the interface's interface
+    // prototype object.
+    v8::Local<v8::FunctionTemplate> method_template =
+        v8::FunctionTemplate::New(isolate, nullableStringOperationMethod);
+    method_template->RemovePrototype();
+    method_template->SetLength(0);
+    prototype_template->Set(
+        NewInternalString(isolate, "nullableStringOperation"),
+        method_template);
+
+    // The value of the property is the result of creating an operation function
+    // given the operation, the interface, and the relevant Realm of the object
+    // that is the location of the property.
+
+    // Note: that is, even if an includes statement was used to make an
+    // operation available on the interface, we pass in the interface which
+    // includes the interface mixin, and not the interface mixin on which the
+    // operation was originally declared.
   }
 
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, nullableNumericArgumentMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "nullableNumericArgument",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, nullableNumericOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "nullableNumericOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, nullableObjectArgumentMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "nullableObjectArgument",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, nullableObjectOperationMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "nullableObjectOperation",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, nullableStringArgumentMethod);
-    method_template->RemovePrototype();
-    prototype_template->Set(
-        v8::String::NewFromUtf8(
-            isolate,
-            "nullableStringArgument",
-            v8::NewStringType::kInternalized).ToLocalChecked(),
-        method_template);
-  }
-
-  {
-    v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, nullableStringOperationMethod);
-    method_template->RemovePrototype();
-    protot