Refactor implementation of native and Skylark declared providers.

1) Instead of having a single class for both, split them into
   {Skylark,Native}ClassObjectConstructors
2) Allow NativeClassObjectConstructors to customize their instantiation
   logic.
3) Prepare ClassObjectConstructor.Key to be serializable.

--
PiperOrigin-RevId: 148997553
MOS_MIGRATED_REVID=148997553
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/AbstractConfiguredTarget.java b/src/main/java/com/google/devtools/build/lib/analysis/AbstractConfiguredTarget.java
index b2c20e6..d32b279 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/AbstractConfiguredTarget.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/AbstractConfiguredTarget.java
@@ -23,8 +23,8 @@
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
 import com.google.devtools.build.lib.collect.nestedset.Order;
 import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.packages.ClassObjectConstructor;
 import com.google.devtools.build.lib.packages.PackageSpecification;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
 import com.google.devtools.build.lib.packages.SkylarkProviderIdentifier;
 import com.google.devtools.build.lib.packages.Target;
 import com.google.devtools.build.lib.syntax.ClassObject;
@@ -128,12 +128,12 @@
 
   @Override
   public Object getIndex(Object key, Location loc) throws EvalException {
-    if (!(key instanceof SkylarkClassObjectConstructor)) {
+    if (!(key instanceof ClassObjectConstructor)) {
       throw new EvalException(loc, String.format(
           "Type Target only supports indexing by object constructors, got %s instead",
           EvalUtils.getDataTypeName(key)));
     }
-    SkylarkClassObjectConstructor constructor = (SkylarkClassObjectConstructor) key;
+    ClassObjectConstructor constructor = (ClassObjectConstructor) key;
     SkylarkProviders provider = getProvider(SkylarkProviders.class);
     if (provider != null) {
       Object declaredProvider = provider.getDeclaredProvider(constructor.getKey());
@@ -144,17 +144,17 @@
     // Either provider or declaredProvider is null
     throw new EvalException(loc, String.format(
         "Object of type Target doesn't contain declared provider %s",
-        constructor.getKey().getExportedName()));
+        constructor.getPrintableName()));
   }
 
   @Override
   public boolean containsKey(Object key, Location loc) throws EvalException {
-    if (!(key instanceof SkylarkClassObjectConstructor)) {
+    if (!(key instanceof ClassObjectConstructor)) {
       throw new EvalException(loc, String.format(
           "Type Target only supports querying by object constructors, got %s instead",
           EvalUtils.getDataTypeName(key)));
     }
-    SkylarkClassObjectConstructor constructor = (SkylarkClassObjectConstructor) key;
+    ClassObjectConstructor constructor = (ClassObjectConstructor) key;
     SkylarkProviders provider = getProvider(SkylarkProviders.class);
     if (provider != null) {
       Object declaredProvider = provider.getDeclaredProvider(constructor.getKey());
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/ActionsProvider.java b/src/main/java/com/google/devtools/build/lib/analysis/ActionsProvider.java
index b9cae02..f34bc31 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/ActionsProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/ActionsProvider.java
@@ -16,8 +16,8 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.devtools.build.lib.actions.ActionAnalysisMetadata;
 import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.packages.NativeClassObjectConstructor;
 import com.google.devtools.build.lib.packages.SkylarkClassObject;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -30,8 +30,8 @@
   /**
    * The Actions provider type itself. 
    */
-  public static final SkylarkClassObjectConstructor SKYLARK_CONSTRUCTOR =
-      SkylarkClassObjectConstructor.createNative("Actions");
+  public static final NativeClassObjectConstructor SKYLARK_CONSTRUCTOR =
+      new NativeClassObjectConstructor("Actions") { };
 
   /**
    * Factory method for creating instances of the Actions provider.
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/AnalysisUtils.java b/src/main/java/com/google/devtools/build/lib/analysis/AnalysisUtils.java
index 1abc893..01004d1 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/AnalysisUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/AnalysisUtils.java
@@ -25,8 +25,8 @@
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
 import com.google.devtools.build.lib.collect.nestedset.Order;
 import com.google.devtools.build.lib.packages.BuildType;
+import com.google.devtools.build.lib.packages.ClassObjectConstructor;
 import com.google.devtools.build.lib.packages.SkylarkClassObject;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
 import com.google.devtools.build.lib.packages.TriState;
 import com.google.devtools.build.lib.vfs.PathFragment;
 
@@ -84,7 +84,7 @@
    */
   public static Iterable<SkylarkClassObject> getProviders(
       Iterable<? extends TransitiveInfoCollection> prerequisites,
-      final SkylarkClassObjectConstructor.Key skylarkKey) {
+      final ClassObjectConstructor.Key skylarkKey) {
     ImmutableList.Builder<SkylarkClassObject> result = ImmutableList.builder();
     for (TransitiveInfoCollection prerequisite : prerequisites) {
       SkylarkClassObject prerequisiteProvider = prerequisite.get(skylarkKey);
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredAspect.java b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredAspect.java
index f1dc77d..8578e76 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredAspect.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredAspect.java
@@ -29,8 +29,8 @@
 import com.google.devtools.build.lib.packages.AspectClass;
 import com.google.devtools.build.lib.packages.AspectDescriptor;
 import com.google.devtools.build.lib.packages.AspectParameters;
+import com.google.devtools.build.lib.packages.ClassObjectConstructor;
 import com.google.devtools.build.lib.packages.SkylarkClassObject;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor.Key;
 import com.google.devtools.build.lib.syntax.EvalException;
 import com.google.devtools.build.lib.util.Preconditions;
 import java.util.Arrays;
@@ -212,7 +212,8 @@
       ImmutableMap<String, Object> skylarkProvidersMap = skylarkProviderBuilder.build();
       if (!skylarkProvidersMap.isEmpty()) {
         providers.add(
-            new SkylarkProviders(skylarkProvidersMap, ImmutableMap.<Key, SkylarkClassObject>of()));
+            new SkylarkProviders(skylarkProvidersMap,
+                ImmutableMap.<ClassObjectConstructor.Key, SkylarkClassObject>of()));
       }
 
       addProvider(
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/EnvironmentGroupConfiguredTarget.java b/src/main/java/com/google/devtools/build/lib/analysis/EnvironmentGroupConfiguredTarget.java
index 4a345f1..5d3ca37 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/EnvironmentGroupConfiguredTarget.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/EnvironmentGroupConfiguredTarget.java
@@ -14,9 +14,9 @@
 
 package com.google.devtools.build.lib.analysis;
 
+import com.google.devtools.build.lib.packages.ClassObjectConstructor;
 import com.google.devtools.build.lib.packages.EnvironmentGroup;
 import com.google.devtools.build.lib.packages.SkylarkClassObject;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor.Key;
 import com.google.devtools.build.lib.util.Preconditions;
 import javax.annotation.Nullable;
 
@@ -43,7 +43,7 @@
 
   @Nullable
   @Override
-  public SkylarkClassObject get(Key providerKey) {
+  public SkylarkClassObject get(ClassObjectConstructor.Key providerKey) {
     // No providers.
     return null;
   }
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/FileConfiguredTarget.java b/src/main/java/com/google/devtools/build/lib/analysis/FileConfiguredTarget.java
index 3009d1d..0bc9b53 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/FileConfiguredTarget.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/FileConfiguredTarget.java
@@ -18,9 +18,9 @@
 import com.google.devtools.build.lib.collect.nestedset.NestedSet;
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
 import com.google.devtools.build.lib.collect.nestedset.Order;
+import com.google.devtools.build.lib.packages.ClassObjectConstructor;
 import com.google.devtools.build.lib.packages.FileTarget;
 import com.google.devtools.build.lib.packages.SkylarkClassObject;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor.Key;
 import com.google.devtools.build.lib.rules.fileset.FilesetProvider;
 import com.google.devtools.build.lib.rules.test.InstrumentedFilesProvider;
 import com.google.devtools.build.lib.util.FileType;
@@ -85,7 +85,7 @@
 
   @Nullable
   @Override
-  public SkylarkClassObject get(Key providerKey) {
+  public SkylarkClassObject get(ClassObjectConstructor.Key providerKey) {
     return null;
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/MergedConfiguredTarget.java b/src/main/java/com/google/devtools/build/lib/analysis/MergedConfiguredTarget.java
index c8462980..6240bf8 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/MergedConfiguredTarget.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/MergedConfiguredTarget.java
@@ -15,8 +15,8 @@
 
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
+import com.google.devtools.build.lib.packages.ClassObjectConstructor;
 import com.google.devtools.build.lib.packages.SkylarkClassObject;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor.Key;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -57,7 +57,7 @@
 
   @Nullable
   @Override
-  public SkylarkClassObject get(Key providerKey) {
+  public SkylarkClassObject get(ClassObjectConstructor.Key providerKey) {
     return getProvider(SkylarkProviders.class).getDeclaredProvider(providerKey);
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/PackageGroupConfiguredTarget.java b/src/main/java/com/google/devtools/build/lib/analysis/PackageGroupConfiguredTarget.java
index 343e698..7a3a33a 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/PackageGroupConfiguredTarget.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/PackageGroupConfiguredTarget.java
@@ -18,10 +18,10 @@
 import com.google.devtools.build.lib.collect.nestedset.NestedSet;
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
 import com.google.devtools.build.lib.events.Event;
+import com.google.devtools.build.lib.packages.ClassObjectConstructor;
 import com.google.devtools.build.lib.packages.PackageGroup;
 import com.google.devtools.build.lib.packages.PackageSpecification;
 import com.google.devtools.build.lib.packages.SkylarkClassObject;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor.Key;
 import com.google.devtools.build.lib.util.Preconditions;
 import javax.annotation.Nullable;
 
@@ -75,7 +75,7 @@
 
   @Nullable
   @Override
-  public SkylarkClassObject get(Key providerKey) {
+  public SkylarkClassObject get(ClassObjectConstructor.Key providerKey) {
     // No providers.
     return null;
   }
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTarget.java b/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTarget.java
index 094b10f..4a14b13 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTarget.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTarget.java
@@ -19,10 +19,10 @@
 import com.google.devtools.build.lib.analysis.config.ConfigMatchingProvider;
 import com.google.devtools.build.lib.analysis.config.RunUnder;
 import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.packages.ClassObjectConstructor;
 import com.google.devtools.build.lib.packages.OutputFile;
 import com.google.devtools.build.lib.packages.Rule;
 import com.google.devtools.build.lib.packages.SkylarkClassObject;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
 import com.google.devtools.build.lib.util.Preconditions;
 import javax.annotation.Nullable;
 
@@ -114,7 +114,7 @@
    * Returns a declared provider provided by this target. Only meant to use from Skylark.
    */
   @Override
-  public SkylarkClassObject get(SkylarkClassObjectConstructor.Key providerKey) {
+  public SkylarkClassObject get(ClassObjectConstructor.Key providerKey) {
     return getProvider(SkylarkProviders.class).getDeclaredProvider(providerKey);
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTargetBuilder.java b/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTargetBuilder.java
index 61b8109..fd5bca0 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTargetBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTargetBuilder.java
@@ -29,8 +29,8 @@
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
 import com.google.devtools.build.lib.collect.nestedset.Order;
 import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.packages.ClassObjectConstructor;
 import com.google.devtools.build.lib.packages.SkylarkClassObject;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
 import com.google.devtools.build.lib.packages.Target;
 import com.google.devtools.build.lib.packages.TargetUtils;
 import com.google.devtools.build.lib.rules.test.ExecutionInfoProvider;
@@ -61,7 +61,7 @@
   private final TransitiveInfoProviderMap.Builder providersBuilder =
       TransitiveInfoProviderMap.builder();
   private final ImmutableMap.Builder<String, Object> skylarkProviders = ImmutableMap.builder();
-  private final ImmutableMap.Builder<SkylarkClassObjectConstructor.Key, SkylarkClassObject>
+  private final ImmutableMap.Builder<ClassObjectConstructor.Key, SkylarkClassObject>
       skylarkDeclaredProviders = ImmutableMap.builder();
   private final Map<String, NestedSetBuilder<Artifact>> outputGroupBuilders = new TreeMap<>();
 
@@ -263,7 +263,7 @@
   /**
    * Add a specific provider with a given value.
    *
-   * @deprecated use {@link addProvider}
+   * @deprecated use {@link #addProvider}
    */
   @Deprecated
   public <T extends TransitiveInfoProvider> RuleConfiguredTargetBuilder add(Class<T> key, T value) {
@@ -301,7 +301,7 @@
    */
   public RuleConfiguredTargetBuilder addSkylarkDeclaredProvider(
       SkylarkClassObject provider, Location loc) throws EvalException {
-    SkylarkClassObjectConstructor constructor = provider.getConstructor();
+    ClassObjectConstructor constructor = provider.getConstructor();
     SkylarkProviderValidationUtil.validateAndThrowEvalException(
         constructor.getPrintableName(), provider, loc);
     if (!constructor.isExported()) {
@@ -335,7 +335,7 @@
    * for Skylark rule implementations.
    */
   public RuleConfiguredTargetBuilder addNativeDeclaredProvider(SkylarkClassObject provider) {
-    SkylarkClassObjectConstructor constructor = provider.getConstructor();
+    ClassObjectConstructor constructor = provider.getConstructor();
     Preconditions.checkState(constructor.isExported());
     skylarkDeclaredProviders.put(constructor.getKey(), provider);
     return this;
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
index 76f6aa2..ca00f10 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
@@ -59,6 +59,7 @@
 import com.google.devtools.build.lib.packages.Attribute.SplitTransition;
 import com.google.devtools.build.lib.packages.AttributeMap;
 import com.google.devtools.build.lib.packages.BuildType;
+import com.google.devtools.build.lib.packages.ClassObjectConstructor;
 import com.google.devtools.build.lib.packages.ConfigurationFragmentPolicy;
 import com.google.devtools.build.lib.packages.FileTarget;
 import com.google.devtools.build.lib.packages.FilesetEntry;
@@ -72,7 +73,6 @@
 import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
 import com.google.devtools.build.lib.packages.RuleErrorConsumer;
 import com.google.devtools.build.lib.packages.SkylarkClassObject;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
 import com.google.devtools.build.lib.packages.SkylarkProviderIdentifier;
 import com.google.devtools.build.lib.packages.Target;
 import com.google.devtools.build.lib.packages.TargetUtils;
@@ -848,7 +848,7 @@
    * specified attribute of this target in the BUILD file.
    */
   public Iterable<SkylarkClassObject> getPrerequisites(
-      String attributeName, Mode mode, final SkylarkClassObjectConstructor.Key skylarkKey) {
+      String attributeName, Mode mode, final ClassObjectConstructor.Key skylarkKey) {
     return AnalysisUtils.getProviders(getPrerequisites(attributeName, mode), skylarkKey);
   }
 
@@ -859,7 +859,7 @@
    */
   @Nullable
   public SkylarkClassObject getPrerequisite(
-      String attributeName, Mode mode, final SkylarkClassObjectConstructor.Key skylarkKey) {
+      String attributeName, Mode mode, final ClassObjectConstructor.Key skylarkKey) {
     TransitiveInfoCollection prerequisite = getPrerequisite(attributeName, mode);
     return prerequisite == null ? null : prerequisite.get(skylarkKey);
   }
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/SkylarkProviders.java b/src/main/java/com/google/devtools/build/lib/analysis/SkylarkProviders.java
index e8fe2d5..be75e14 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/SkylarkProviders.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/SkylarkProviders.java
@@ -18,8 +18,8 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.devtools.build.lib.analysis.MergedConfiguredTarget.DuplicateException;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+import com.google.devtools.build.lib.packages.ClassObjectConstructor;
 import com.google.devtools.build.lib.packages.SkylarkClassObject;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
 import com.google.devtools.build.lib.rules.SkylarkApiProvider;
 import com.google.devtools.build.lib.syntax.EvalException;
 import com.google.devtools.build.lib.syntax.SkylarkType;
@@ -34,13 +34,13 @@
  */
 @Immutable
 public final class SkylarkProviders implements TransitiveInfoProvider {
-  private final ImmutableMap<SkylarkClassObjectConstructor.Key, SkylarkClassObject>
+  private final ImmutableMap<ClassObjectConstructor.Key, SkylarkClassObject>
       declaredProviders;
   private final ImmutableMap<String, Object> skylarkProviders;
 
   SkylarkProviders(
       ImmutableMap<String, Object> skylarkProviders,
-      ImmutableMap<SkylarkClassObjectConstructor.Key, SkylarkClassObject> declaredProviders) {
+      ImmutableMap<ClassObjectConstructor.Key, SkylarkClassObject> declaredProviders) {
     this.declaredProviders = Preconditions.checkNotNull(declaredProviders);
     this.skylarkProviders = Preconditions.checkNotNull(skylarkProviders);
   }
@@ -79,7 +79,7 @@
     return type.cast(obj);
   }
 
-  public SkylarkClassObject getDeclaredProvider(SkylarkClassObjectConstructor.Key key) {
+  public SkylarkClassObject getDeclaredProvider(ClassObjectConstructor.Key key) {
     return declaredProviders.get(key);
   }
 
@@ -93,11 +93,11 @@
       };
 
   public static final Function<SkylarkProviders,
-                               Map<SkylarkClassObjectConstructor.Key, SkylarkClassObject>>
+                               Map<ClassObjectConstructor.Key, SkylarkClassObject>>
       DECLARED_PROVIDERS_MAP_FUNCTION =
-      new Function<SkylarkProviders, Map<SkylarkClassObjectConstructor.Key, SkylarkClassObject>>() {
+      new Function<SkylarkProviders, Map<ClassObjectConstructor.Key, SkylarkClassObject>>() {
         @Override
-        public Map<SkylarkClassObjectConstructor.Key, SkylarkClassObject> apply(
+        public Map<ClassObjectConstructor.Key, SkylarkClassObject> apply(
             SkylarkProviders skylarkProviders) {
           return skylarkProviders.declaredProviders;
         }
@@ -125,9 +125,9 @@
         SKYLARK_PROVIDERS_MAP_FUNCTION,
         premergedProviders);
 
-    ImmutableMap<SkylarkClassObjectConstructor.Key, SkylarkClassObject> declaredProviders =
+    ImmutableMap<ClassObjectConstructor.Key, SkylarkClassObject> declaredProviders =
         mergeMaps(providers, DECLARED_PROVIDERS_MAP_FUNCTION,
-            ImmutableMap.<SkylarkClassObjectConstructor.Key, SkylarkClassObject>of());
+            ImmutableMap.<ClassObjectConstructor.Key, SkylarkClassObject>of());
 
     return new SkylarkProviders(skylarkProviders, declaredProviders);
   }
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/TransitiveInfoCollection.java b/src/main/java/com/google/devtools/build/lib/analysis/TransitiveInfoCollection.java
index 58eee47..03f9d95 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/TransitiveInfoCollection.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/TransitiveInfoCollection.java
@@ -16,8 +16,8 @@
 
 import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
 import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.packages.ClassObjectConstructor;
 import com.google.devtools.build.lib.packages.SkylarkClassObject;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
 import com.google.devtools.build.lib.packages.SkylarkProviderIdentifier;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory;
@@ -89,13 +89,13 @@
    * Returns the declared provider requested, or null, if the information is not found.
    * The transitive information has to have been added using the Skylark framework.
    */
-  @Nullable SkylarkClassObject get(SkylarkClassObjectConstructor.Key providerKey);
+  @Nullable SkylarkClassObject get(ClassObjectConstructor.Key providerKey);
 
   /**
    * Returns the provider defined in Skylark, or null, if the information is not found.
    * The transitive information has to have been added using the Skylark framework.
    *
-   * This method dispatches to either {@link #get(SkylarkClassObjectConstructor.Key)} or
+   * This method dispatches to either {@link #get(ClassObjectConstructor.Key)} or
    * {@link #get(String)} depending on whether {@link SkylarkProviderIdentifier} is for
    * legacy or for declared provider.
    */
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryContext.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryContext.java
index 97a8f59..60f23eb 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryContext.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryContext.java
@@ -28,9 +28,9 @@
 import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
 import com.google.devtools.build.lib.events.Location;
 import com.google.devtools.build.lib.packages.Attribute;
+import com.google.devtools.build.lib.packages.NativeClassObjectConstructor;
 import com.google.devtools.build.lib.packages.Rule;
 import com.google.devtools.build.lib.packages.SkylarkClassObject;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
 import com.google.devtools.build.lib.rules.repository.RepositoryFunction.RepositoryFunctionException;
 import com.google.devtools.build.lib.rules.repository.WorkspaceAttributeMapper;
 import com.google.devtools.build.lib.skyframe.FileSymlinkException;
@@ -112,7 +112,7 @@
                 : SkylarkType.convertToSkylark(val, null));
       }
     }
-    attrObject = SkylarkClassObjectConstructor.STRUCT.create(
+    attrObject = NativeClassObjectConstructor.STRUCT.create(
         attrBuilder.build(), "No such attribute '%s'");
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/packages/Attribute.java b/src/main/java/com/google/devtools/build/lib/packages/Attribute.java
index f5e9b95..9077e0f 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/Attribute.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/Attribute.java
@@ -1454,7 +1454,7 @@
     private Object invokeCallback(Map<String, Object> attrValues)
         throws EvalException, InterruptedException {
       ClassObject attrs =
-          SkylarkClassObjectConstructor.STRUCT.create(
+          NativeClassObjectConstructor.STRUCT.create(
               attrValues, "No such regular (non computed) attribute '%s'.");
       Object result = callback.call(attrs);
       try {
diff --git a/src/main/java/com/google/devtools/build/lib/packages/ClassObjectConstructor.java b/src/main/java/com/google/devtools/build/lib/packages/ClassObjectConstructor.java
new file mode 100644
index 0000000..e2e0ccf
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/packages/ClassObjectConstructor.java
@@ -0,0 +1,105 @@
+// Copyright 2017 The Bazel Authors. 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.
+package com.google.devtools.build.lib.packages;
+
+import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
+import com.google.devtools.build.lib.syntax.BaseFunction;
+import com.google.devtools.build.lib.syntax.Environment;
+import com.google.devtools.build.lib.syntax.EvalException;
+import com.google.devtools.build.lib.syntax.FuncallExpression;
+import com.google.devtools.build.lib.syntax.FunctionSignature;
+import com.google.devtools.build.lib.syntax.SkylarkType;
+import javax.annotation.Nullable;
+
+/**
+ * Declared Provider (a constructor for {@link SkylarkClassObject}).
+ *
+ * Declared providers can be declared either natively ({@link NativeClassObjectConstructor}
+ * or in Skylark {@link SkylarkClassObjectConstructor}.
+ *
+ * {@link ClassObjectConstructor} serves both as "type identifier" for declared provider
+ * instances and as a function that can be called to construct a provider.
+ *
+ * Prefer to use {@link Key} as a serializable identifier of {@link ClassObjectConstructor}.
+ * In particular, {@link Key} should be used in all data structures exposed to Skyframe.
+ */
+@SkylarkModule(name = "provider",
+    doc = "A constructor for simple value objects. "
+        + "See the global <a href=\"globals.html#provider\">provider</a> function "
+        + "for more details."
+)
+@Immutable
+public abstract class ClassObjectConstructor extends BaseFunction {
+
+  protected ClassObjectConstructor(String name,
+      FunctionSignature.WithValues<Object, SkylarkType> signature,
+      Location location) {
+    super(name, signature, location);
+  }
+
+
+  /**
+   * Has this {@link ClassObjectConstructor} been exported?
+   * All native constructors are always exported. Skylark constructors are exported
+   * if they are assigned to top-level name in a Skylark module.
+   */
+  public abstract boolean isExported();
+
+  /**
+   * Returns a serializable representation of this constructor.
+   */
+  public abstract Key getKey();
+
+  /**
+   * Returns a name of this constructor that should be used in error messages.
+   */
+  public abstract String getPrintableName();
+
+  /**
+   * Returns an error message format for instances.
+   *
+   * Must contain one '%s' placeholder for field name.
+   */
+  public abstract String getErrorMessageFormatForInstances();
+
+
+  @Override
+  protected Object call(Object[] args, @Nullable FuncallExpression ast, @Nullable Environment env)
+      throws EvalException, InterruptedException {
+    Location loc = ast != null ? ast.getLocation() : Location.BUILTIN;
+    return createInstanceFromSkylark(args, loc);
+  }
+
+  /**
+   * Override this method to provide logic that is used to instantiate a declared provider
+   * from Skylark.
+   *
+   * This is a method that is called when a constructor {@code c} is invoked as<br>
+   * {@code c(arg1 = val1, arg2 = val2, ...)}.
+   *
+   * @param args an array of argument values sorted as per the signature
+   *          ({@see BaseFunction#call})
+   */
+  protected abstract SkylarkClassObject createInstanceFromSkylark(Object[] args, Location loc)
+      throws EvalException;
+
+  /**
+   * A serializable representation of {@link ClassObjectConstructor}.
+   */
+  // todo(vladmos,dslomov): when we allow declared providers in `requiredProviders`,
+  // we will need to serialize this somehow.
+  public abstract static class Key {}
+}
diff --git a/src/main/java/com/google/devtools/build/lib/packages/ImplicitOutputsFunction.java b/src/main/java/com/google/devtools/build/lib/packages/ImplicitOutputsFunction.java
index a2f0b6e..efb6b08 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/ImplicitOutputsFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/ImplicitOutputsFunction.java
@@ -96,7 +96,7 @@
       }
       // Add 'name' explicitly, since its value is not in the attribute map.
       attrValues.put("name", map.getName());
-      ClassObject attrs = SkylarkClassObjectConstructor.STRUCT.create(
+      ClassObject attrs = NativeClassObjectConstructor.STRUCT.create(
           attrValues,
           "Attribute '%s' either doesn't exist "
           + "or uses a select() (i.e. could have multiple values)");
diff --git a/src/main/java/com/google/devtools/build/lib/packages/NativeClassObjectConstructor.java b/src/main/java/com/google/devtools/build/lib/packages/NativeClassObjectConstructor.java
new file mode 100644
index 0000000..011bf3b
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/packages/NativeClassObjectConstructor.java
@@ -0,0 +1,171 @@
+// Copyright 2017 The Bazel Authors. 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.
+package com.google.devtools.build.lib.packages;
+
+import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.syntax.EvalException;
+import com.google.devtools.build.lib.syntax.FunctionSignature;
+import com.google.devtools.build.lib.syntax.SkylarkType;
+import java.util.Map;
+import javax.annotation.Nullable;
+
+/**
+ * Base class for declared providers {@see ClassObjectConstructor} defined
+ * in native code.
+ *
+ * Every non-abstract derived class of {@link NativeClassObjectConstructor}
+ * corresponds to a single declared provider. This is enforced by final
+ * {@link #equals(Object)} and {@link #hashCode()}.
+ *
+ * Typical implementation of a non-constructable from Skylark declared provider is as follows:
+ * <pre>
+ *     public static final ClassObjectConstructor CC_LINK_PARAMS =
+ *       new NativeClassObjectConstructor("link_params") { };
+ * </pre>
+ *
+ * To allow construction from Skylark and custom construction logic, override
+ * {@link #createInstanceFromSkylark(Object[], Location)} (see {@link #STRUCT} for an example.
+ */
+@Immutable
+public abstract class NativeClassObjectConstructor extends ClassObjectConstructor {
+  private final NativeKey key;
+  private final String errorMessageForInstances;
+
+  /**
+   * "struct" function.
+   */
+  public static final StructConstructor STRUCT = new StructConstructor();
+
+  /**
+   * A constructor for default {@code struct}s.
+   *
+   * Singleton, instance is {@link #STRUCT}.
+   */
+  public static final class StructConstructor extends NativeClassObjectConstructor {
+    private StructConstructor() {
+      super("struct");
+    }
+
+    @Override
+    protected SkylarkClassObject createInstanceFromSkylark(Object[] args, Location loc) {
+      @SuppressWarnings("unchecked")
+      Map<String, Object> kwargs = (Map<String, Object>) args[0];
+      return new SkylarkClassObject(this, kwargs, loc);
+    }
+
+    public SkylarkClassObject create(Map<String, Object> values, String message) {
+      return new SkylarkClassObject(this, values, message);
+    }
+  }
+
+  private static final FunctionSignature.WithValues<Object, SkylarkType> SIGNATURE =
+      FunctionSignature.WithValues.create(FunctionSignature.KWARGS);
+
+  protected NativeClassObjectConstructor(String name) {
+    this(name, SIGNATURE);
+  }
+
+  protected NativeClassObjectConstructor(String name,
+      FunctionSignature.WithValues<Object, SkylarkType> signature) {
+    super(name, signature, Location.BUILTIN);
+    key = new NativeKey(name, getClass());
+    errorMessageForInstances = String.format("'%s' object has no attribute '%%s'", name);
+
+  }
+
+  /**
+   * equals() implements singleton class semantics.
+   *
+   * Every non-abstract derived class of {@link NativeClassObjectConstructor}
+   * corresponds to a single declared provider.
+   */
+  @Override
+  public final boolean equals(@Nullable Object other) {
+    return other != null && this.getClass().equals(other.getClass());
+  }
+
+  /**
+   * hashCode() implements singleton class semantics.
+   *
+   * Every non-abstract derived class of {@link NativeClassObjectConstructor}
+   * corresponds to a single declared provider.
+   */
+  @Override
+  public final int hashCode() {
+    return getClass().hashCode();
+  }
+
+  @Override
+  public String getPrintableName() {
+    return getName();
+  }
+
+  @Override
+  public String getErrorMessageFormatForInstances() {
+    return errorMessageForInstances;
+  }
+
+  @Override
+  public boolean isExported() {
+    return true;
+  }
+
+  @Override
+  public NativeKey getKey() {
+    return key;
+  }
+
+  @Override
+  protected SkylarkClassObject createInstanceFromSkylark(Object[] args, Location loc)
+      throws EvalException {
+    throw new EvalException(loc,
+        String.format("'%s' cannot be constructed from Skylark", getPrintableName()));
+  }
+
+  /**
+   * A serializable representation of {@link NativeClassObjectConstructor}.
+   *
+   * Just a wrapper around its class.
+   */
+  // todo(vladmos,dslomov): when we allow declared providers in `requiredProviders`,
+  // we will need to serialize this somehow.
+  @Immutable
+  public static final class NativeKey extends Key {
+    private final String name;
+    private final Class<? extends NativeClassObjectConstructor> aClass;
+
+    private NativeKey(
+        String name,
+        Class<? extends NativeClassObjectConstructor> aClass) {
+      this.name = name;
+      this.aClass = aClass;
+    }
+
+    @Override
+    public int hashCode() {
+      return aClass.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      return obj instanceof NativeKey && aClass.equals(((NativeKey) obj).aClass);
+    }
+
+    @Override
+    public String toString() {
+      return name;
+    }
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java b/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
index 1dc58df..16df4e0 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
@@ -1603,7 +1603,7 @@
         builder.put(function.getName(), function);
       }
     }
-    return SkylarkClassObjectConstructor.STRUCT.create(
+    return NativeClassObjectConstructor.STRUCT.create(
         builder.build(), "no native function or rule '%s'");
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/packages/SkylarkClassObject.java b/src/main/java/com/google/devtools/build/lib/packages/SkylarkClassObject.java
index 6946ea3..1b67b13 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/SkylarkClassObject.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/SkylarkClassObject.java
@@ -21,6 +21,7 @@
 import com.google.common.collect.Sets;
 import com.google.common.collect.Sets.SetView;
 import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.packages.NativeClassObjectConstructor.StructConstructor;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkValue;
@@ -48,31 +49,31 @@
           + "for more details."
 )
 public class SkylarkClassObject implements ClassObject, SkylarkValue, Concatable, Serializable {
-  /** Error message to use when errorMessage argument is null. */
-  private static final String DEFAULT_ERROR_MESSAGE = "'struct' object has no attribute '%s'";
-
-  private final SkylarkClassObjectConstructor constructor;
+  private final ClassObjectConstructor constructor;
   private final ImmutableMap<String, Object> values;
   private final Location creationLoc;
   private final String errorMessage;
 
   /**
-   * Primarily for testing purposes where no location is available and the default
-   * errorMessage suffices.
+   * Creates a built-in struct (i.e. without creation loc).
    */
-  public SkylarkClassObject(SkylarkClassObjectConstructor constructor,
+  public SkylarkClassObject(ClassObjectConstructor constructor,
       Map<String, Object> values) {
     this.constructor = constructor;
     this.values = copyValues(values);
     this.creationLoc = null;
-    this.errorMessage = DEFAULT_ERROR_MESSAGE;
+    this.errorMessage = constructor.getErrorMessageFormatForInstances();
   }
 
   /**
-   * Creates a built-in struct (i.e. without creation loc). The errorMessage has to have
-   * exactly one '%s' parameter to substitute the struct field name.
+   * Creates a built-in struct (i.e. without creation loc).
+   *
+   * Allows to supply a specific error message.
+   * Only used in {@link StructConstructor#create(Map, String)}
+   * If you need to override an error message, preferred way is to create a specific
+   * {@link NativeClassObjectConstructor}.
    */
-  public SkylarkClassObject(SkylarkClassObjectConstructor constructor,
+  SkylarkClassObject(ClassObjectConstructor constructor,
       Map<String, Object> values, String errorMessage) {
     this.constructor = constructor;
     this.values = copyValues(values);
@@ -80,12 +81,12 @@
     this.errorMessage = Preconditions.checkNotNull(errorMessage);
   }
 
-  public SkylarkClassObject(SkylarkClassObjectConstructor constructor,
+  public SkylarkClassObject(ClassObjectConstructor constructor,
       Map<String, Object> values, Location creationLoc) {
     this.constructor = constructor;
     this.values = copyValues(values);
     this.creationLoc = Preconditions.checkNotNull(creationLoc);
-    this.errorMessage = DEFAULT_ERROR_MESSAGE;
+    this.errorMessage = constructor.getErrorMessageFormatForInstances();
   }
 
   // Ensure that values are all acceptable to Skylark before to stuff them in a ClassObject
@@ -130,7 +131,7 @@
     return StructConcatter.INSTANCE;
   }
   
-  public SkylarkClassObjectConstructor getConstructor() {
+  public ClassObjectConstructor getConstructor() {
     return constructor;
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/packages/SkylarkClassObjectConstructor.java b/src/main/java/com/google/devtools/build/lib/packages/SkylarkClassObjectConstructor.java
index 907702a..9a6605d 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/SkylarkClassObjectConstructor.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/SkylarkClassObjectConstructor.java
@@ -16,11 +16,7 @@
 
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.events.Location;
-import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
-import com.google.devtools.build.lib.syntax.BaseFunction;
-import com.google.devtools.build.lib.syntax.Environment;
 import com.google.devtools.build.lib.syntax.EvalException;
-import com.google.devtools.build.lib.syntax.FuncallExpression;
 import com.google.devtools.build.lib.syntax.FunctionSignature;
 import com.google.devtools.build.lib.syntax.SkylarkType;
 import com.google.devtools.build.lib.util.Preconditions;
@@ -29,94 +25,41 @@
 import javax.annotation.Nullable;
 
 /**
- * Declared Provider (a constructor for {@link SkylarkClassObject}).
+ *  Declared provider defined in Skylark.
+ *
+ *  This is a result of calling {@code provider()} function from Skylark
+ *  ({@link com.google.devtools.build.lib.rules.SkylarkRuleClassFunctions#provider}).
  */
-@SkylarkModule(name = "provider",
-    doc = "A constructor for simple value objects. "
-        + "See the global <a href=\"globals.html#provider\">provider</a> function "
-        + "for more details."
-)
-public final class SkylarkClassObjectConstructor extends BaseFunction implements SkylarkExportable {
-  /**
-   * "struct" function.
-   */
-  public static final SkylarkClassObjectConstructor STRUCT =
-      createNativeConstructable("struct");
-
+public final class SkylarkClassObjectConstructor
+    extends ClassObjectConstructor
+    implements SkylarkExportable {
 
   private static final FunctionSignature.WithValues<Object, SkylarkType> SIGNATURE =
       FunctionSignature.WithValues.create(FunctionSignature.KWARGS);
 
   @Nullable
-  private Key key;
+  private SkylarkKey key;
+  @Nullable
+  private String errorMessageFormatForInstances;
 
-  /**
-   * Some native declared providers are not constructable from Skylark.
-   */
-  private final boolean isConstructable;
-
-  private SkylarkClassObjectConstructor(String name, Location location) {
-    super(name, SIGNATURE, location);
-    // All Skylark-defined declared providers are constructable.
-    this.isConstructable = true;
-  }
-
-  private SkylarkClassObjectConstructor(String name, boolean isConstructable) {
-    super(name, SIGNATURE, Location.BUILTIN);
-    this.key = new NativeKey();
-    this.isConstructable = isConstructable;
-  }
-
-  /**
-   * Creates a native Declared Provider ({@link SkylarkClassObject} constructor).
-   * 
-   * The convention for the name of a provider follows the convention for a class name in
-   * Python: CapitalCamelCase.
-   */
-  public static SkylarkClassObjectConstructor createNative(String name) {
-    return new SkylarkClassObjectConstructor(name, false);
-  }
-
-  /**
-   * Creates a native Declared Provider ({@link SkylarkClassObject} constructor)
-   * that can be constructed from Skylark.
-   * 
-   * Use CapitalCamelCase style for the name.
-   */
-  public static SkylarkClassObjectConstructor createNativeConstructable(String name) {
-    return new SkylarkClassObjectConstructor(name, true);
-  }
-
+  private static final String DEFAULT_ERROR_MESSAFE = "Object has no '%s' attribute.";
 
   /**
    * Creates a Skylark-defined Declared Provider ({@link SkylarkClassObject} constructor).
    *
    * Needs to be exported later.
    */
-  public static SkylarkClassObjectConstructor createSkylark(String name, Location location) {
-    return new SkylarkClassObjectConstructor(name, location);
+  public SkylarkClassObjectConstructor(String name, Location location) {
+    super(name, SIGNATURE, location);
+    this.errorMessageFormatForInstances = DEFAULT_ERROR_MESSAFE;
   }
 
   @Override
-  protected Object call(Object[] args, @Nullable FuncallExpression ast, @Nullable Environment env)
-      throws EvalException, InterruptedException {
-    if (!isConstructable) {
-      Location loc = ast != null ? ast.getLocation() : Location.BUILTIN;
-      throw new EvalException(loc,
-          String.format("'%s' cannot be constructed from Skylark", getPrintableName()));
-    }
+  protected SkylarkClassObject createInstanceFromSkylark(Object[] args, Location loc)
+      throws EvalException {
     @SuppressWarnings("unchecked")
     Map<String, Object> kwargs = (Map<String, Object>) args[0];
-    return new SkylarkClassObject(this, kwargs, ast != null ? ast.getLocation() : Location.BUILTIN);
-  }
-
-  /**
-   * Creates a built-in class object (i.e. without creation loc).
-   * 
-   * The errorMessage has to have exactly one '%s' parameter to substitute the field name.
-   */
-  public SkylarkClassObject create(Map<String, Object> values, String message) {
-    return new SkylarkClassObject(this, values, message);
+    return new SkylarkClassObject(this, kwargs, loc);
   }
 
   @Override
@@ -124,19 +67,28 @@
     return key != null;
   }
 
-  public Key getKey() {
+  @Override
+  public SkylarkKey getKey() {
     Preconditions.checkState(isExported());
     return key;
   }
 
+  @Override
   public String getPrintableName() {
     return key != null ? key.getExportedName() : getName();
   }
 
   @Override
+  public String getErrorMessageFormatForInstances() {
+    return errorMessageFormatForInstances;
+  }
+
+  @Override
   public void export(Label extensionLabel, String exportedName) {
     Preconditions.checkState(!isExported());
     this.key = new SkylarkKey(extensionLabel, exportedName);
+    this.errorMessageFormatForInstances = String.format(
+        "'%s' object has no attribute '%%s'", exportedName);
   }
 
   @Override
@@ -168,17 +120,6 @@
   }
 
   /**
-   * A representation of {@link SkylarkClassObjectConstructor}.
-   */
-  // todo(vladmos,dslomov): when we allow declared providers in `requiredProviders`,
-  // we will need to serialize this somehow.
-  public abstract static class Key {
-    private Key() {}
-
-    public abstract String getExportedName();
-  }
-
-  /**
    * A serializable representation of Skylark-defined {@link SkylarkClassObjectConstructor}
    * that uniquely identifies all {@link SkylarkClassObjectConstructor}s that
    * are exposed to SkyFrame.
@@ -196,12 +137,16 @@
       return extensionLabel;
     }
 
-    @Override
     public String getExportedName() {
       return exportedName;
     }
 
     @Override
+    public String toString() {
+      return exportedName;
+    }
+
+    @Override
     public int hashCode() {
       return Objects.hash(extensionLabel, exportedName);
     }
@@ -221,22 +166,4 @@
     }
   }
 
-  /**
-   * A representation of {@link SkylarkClassObjectConstructor} defined in native code.
-   */
-  // todo(vladmos,dslomov): when we allow declared providers in `requiredProviders`,
-  // we will need to serialize this somehow.
-  public final class NativeKey extends Key {
-    private NativeKey() {
-    }
-
-    @Override
-    public String getExportedName() {
-      return SkylarkClassObjectConstructor.this.getName();
-    }
-
-    public SkylarkClassObjectConstructor getConstructor() {
-      return SkylarkClassObjectConstructor.this;
-    }
-  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/packages/SkylarkProviderIdentifier.java b/src/main/java/com/google/devtools/build/lib/packages/SkylarkProviderIdentifier.java
index f34dffd..42684f7 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/SkylarkProviderIdentifier.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/SkylarkProviderIdentifier.java
@@ -28,12 +28,12 @@
   @Nullable
   private final String legacyId;
   @Nullable
-  private final SkylarkClassObjectConstructor.Key key;
+  private final ClassObjectConstructor.Key key;
 
   /**
    * Creates an id for a declared provider with a given key ({@see SkylarkClassObjectConstructor}).
    */
-  public static SkylarkProviderIdentifier forKey(SkylarkClassObjectConstructor.Key key) {
+  public static SkylarkProviderIdentifier forKey(ClassObjectConstructor.Key key) {
     return new SkylarkProviderIdentifier(key);
   }
 
@@ -49,7 +49,7 @@
     this.key = null;
   }
 
-  private SkylarkProviderIdentifier(SkylarkClassObjectConstructor.Key key) {
+  private SkylarkProviderIdentifier(ClassObjectConstructor.Key key) {
     this.legacyId = null;
     this.key = key;
   }
@@ -73,7 +73,7 @@
   /**
    * Returns a key identifying the declared provider (only for non-legacy providers).
    */
-  public SkylarkClassObjectConstructor.Key getKey() {
+  public ClassObjectConstructor.Key getKey() {
     Preconditions.checkState(!isLegacy(), "Check !isLegacy() first");
     return key;
   }
@@ -83,7 +83,7 @@
     if (isLegacy()) {
       return legacyId;
     }
-    return key.getExportedName();
+    return key.toString();
   }
 
   @Override
diff --git a/src/main/java/com/google/devtools/build/lib/packages/WorkspaceFactory.java b/src/main/java/com/google/devtools/build/lib/packages/WorkspaceFactory.java
index d2b872a..abe66c6 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/WorkspaceFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/WorkspaceFactory.java
@@ -475,7 +475,7 @@
     }
 
     builder.put("bazel_version", version);
-    return SkylarkClassObjectConstructor.STRUCT.create(
+    return NativeClassObjectConstructor.STRUCT.create(
         builder.build(), "no native function or rule '%s'");
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/rules/AliasConfiguredTarget.java b/src/main/java/com/google/devtools/build/lib/rules/AliasConfiguredTarget.java
index 458fba1..7be13d2 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/AliasConfiguredTarget.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/AliasConfiguredTarget.java
@@ -27,8 +27,8 @@
 import com.google.devtools.build.lib.collect.nestedset.Order;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
 import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.packages.ClassObjectConstructor;
 import com.google.devtools.build.lib.packages.SkylarkClassObject;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor.Key;
 import com.google.devtools.build.lib.packages.SkylarkProviderIdentifier;
 import com.google.devtools.build.lib.packages.Target;
 import com.google.devtools.build.lib.syntax.ClassObject;
@@ -80,7 +80,7 @@
 
   @Nullable
   @Override
-  public SkylarkClassObject get(Key providerKey) {
+  public SkylarkClassObject get(ClassObjectConstructor.Key providerKey) {
     return actual == null ? null : actual.get(providerKey);
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java
index 5a01e6f..1f71424 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java
@@ -49,8 +49,10 @@
 import com.google.devtools.build.lib.packages.Attribute;
 import com.google.devtools.build.lib.packages.AttributeMap;
 import com.google.devtools.build.lib.packages.AttributeValueSource;
+import com.google.devtools.build.lib.packages.ClassObjectConstructor;
 import com.google.devtools.build.lib.packages.ImplicitOutputsFunction.SkylarkImplicitOutputsFunctionWithCallback;
 import com.google.devtools.build.lib.packages.ImplicitOutputsFunction.SkylarkImplicitOutputsFunctionWithMap;
+import com.google.devtools.build.lib.packages.NativeClassObjectConstructor;
 import com.google.devtools.build.lib.packages.Package.NameConflictException;
 import com.google.devtools.build.lib.packages.PackageFactory;
 import com.google.devtools.build.lib.packages.PackageFactory.PackageContext;
@@ -186,7 +188,7 @@
     extraKeywords = @Param(name = "kwargs", doc = "the struct attributes."),
     useLocation = true
   )
-  private static final SkylarkClassObjectConstructor struct = SkylarkClassObjectConstructor.STRUCT;
+  private static final ClassObjectConstructor struct = NativeClassObjectConstructor.STRUCT;
 
   // TODO(bazel-team): Move to a "testing" namespace module. Normally we'd pass an objectType
   // to @SkylarkSignature to do this, but that doesn't work here because we're exposing an already-
@@ -205,7 +207,7 @@
           + "<a href=\"globals.html#rule._skylark_testable\">_skylark_testable</a> set to "
           + "<code>True</code>."
   )
-  private static final SkylarkClassObjectConstructor actions = ActionsProvider.SKYLARK_CONSTRUCTOR;
+  private static final ClassObjectConstructor actions = ActionsProvider.SKYLARK_CONSTRUCTOR;
 
   @SkylarkSignature(name = "provider", returnType = SkylarkClassObjectConstructor.class, doc =
       "Creates a declared provider 'constructor'. The return value of this"
@@ -218,7 +220,7 @@
   private static final BuiltinFunction provider =
       new BuiltinFunction("provider") {
         public SkylarkClassObjectConstructor invoke(Location location) {
-          return SkylarkClassObjectConstructor.createSkylark(
+          return new SkylarkClassObjectConstructor(
               "<no name>", // name is set on export.
               location);
         }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleConfiguredTargetBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleConfiguredTargetBuilder.java
index f78e96d..3bf2cdc 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleConfiguredTargetBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleConfiguredTargetBuilder.java
@@ -412,8 +412,10 @@
     ImmutableMap.Builder<String, Object> attrBuilder = new ImmutableMap.Builder<>();
     // TODO: Add actual attributes that users expect to access from default providers
     attrBuilder.put("runfiles", runfilesProvider);
-    SkylarkClassObject statelessDefaultProvider = SkylarkRuleContext.getDefaultProvider().create(
-        attrBuilder.build(), "Default provider has no attribute '%s'");
+    SkylarkClassObject statelessDefaultProvider =
+        new SkylarkClassObject(
+            SkylarkRuleContext.getDefaultProvider(),
+            attrBuilder.build());
 
     // Add the default provider
     builder.addSkylarkDeclaredProvider(statelessDefaultProvider, (defaultProvider == null) ? loc
diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java
index def0452..9f3aa43 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java
@@ -42,13 +42,14 @@
 import com.google.devtools.build.lib.packages.Attribute;
 import com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition;
 import com.google.devtools.build.lib.packages.BuildType;
+import com.google.devtools.build.lib.packages.ClassObjectConstructor;
 import com.google.devtools.build.lib.packages.ImplicitOutputsFunction;
 import com.google.devtools.build.lib.packages.ImplicitOutputsFunction.SkylarkImplicitOutputsFunction;
+import com.google.devtools.build.lib.packages.NativeClassObjectConstructor;
 import com.google.devtools.build.lib.packages.OutputFile;
 import com.google.devtools.build.lib.packages.Package;
 import com.google.devtools.build.lib.packages.RawAttributeMapper;
 import com.google.devtools.build.lib.packages.SkylarkClassObject;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
 import com.google.devtools.build.lib.rules.test.InstrumentedFilesCollector;
 import com.google.devtools.build.lib.rules.test.InstrumentedFilesProvider;
 import com.google.devtools.build.lib.shell.ShellUtils;
@@ -234,7 +235,7 @@
 
       this.artifactsLabelMap = artifactLabelMapBuilder.build();
       this.outputsObject =
-          SkylarkClassObjectConstructor.STRUCT.create(
+          NativeClassObjectConstructor.STRUCT.create(
               outputsBuilder,
               "No attribute '%s' in outputs. Make sure you declared a rule output with this name.");
 
@@ -422,7 +423,7 @@
       }
     }
 
-    return SkylarkClassObjectConstructor.STRUCT.create(
+    return NativeClassObjectConstructor.STRUCT.create(
         splitAttrInfos.build(),
         "No attribute '%s' in split_attr. Make sure that this attribute is defined with a "
           + "split configuration.");
@@ -449,21 +450,21 @@
         ImmutableMap<Artifact, FilesToRunProvider> executableRunfilesMap) {
       this.ruleClassName = ruleClassName;
       attrObject =
-          SkylarkClassObjectConstructor.STRUCT.create(
+          NativeClassObjectConstructor.STRUCT.create(
               attrs,
               "No attribute '%s' in attr. Make sure you declared a rule attribute with this name.");
       executableObject =
-          SkylarkClassObjectConstructor.STRUCT.create(
+          NativeClassObjectConstructor.STRUCT.create(
               executables,
               "No attribute '%s' in executable. Make sure there is a label type attribute marked "
                   + "as 'executable' with this name");
       fileObject =
-          SkylarkClassObjectConstructor.STRUCT.create(
+          NativeClassObjectConstructor.STRUCT.create(
               singleFiles,
               "No attribute '%s' in file. Make sure there is a label type attribute marked "
                   + "as 'single_file' with this name");
       filesObject =
-          SkylarkClassObjectConstructor.STRUCT.create(
+          NativeClassObjectConstructor.STRUCT.create(
               files,
               "No attribute '%s' in files. Make sure there is a label or label_list type attribute "
                   + "with this name");
@@ -516,11 +517,18 @@
     return ruleContext;
   }
 
-  private static final SkylarkClassObjectConstructor DEFAULT_PROVIDER =
-      SkylarkClassObjectConstructor.createNativeConstructable("default_provider");
+  private static final ClassObjectConstructor DEFAULT_PROVIDER =
+      new NativeClassObjectConstructor("default_provider") {
+        @Override
+        protected SkylarkClassObject createInstanceFromSkylark(Object[] args, Location loc) {
+          @SuppressWarnings("unchecked")
+          Map<String, Object> kwargs = (Map<String, Object>) args[0];
+          return new SkylarkClassObject(this, kwargs, loc);
+        }
+      };
 
   @SkylarkCallable(name = "default_provider", structField = true)
-  public static SkylarkClassObjectConstructor getDefaultProvider() {
+  public static ClassObjectConstructor getDefaultProvider() {
     return DEFAULT_PROVIDER;
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/rules/apple/Platform.java b/src/main/java/com/google/devtools/build/lib/rules/apple/Platform.java
index a76594c..f5e2bc4 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/apple/Platform.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/apple/Platform.java
@@ -15,8 +15,9 @@
 package com.google.devtools.build.lib.rules.apple;
 
 import com.google.common.collect.ImmutableSet;
+import com.google.devtools.build.lib.packages.ClassObjectConstructor;
+import com.google.devtools.build.lib.packages.NativeClassObjectConstructor;
 import com.google.devtools.build.lib.packages.SkylarkClassObject;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory;
@@ -173,8 +174,7 @@
 
   /** Returns a Skylark struct that contains the instances of this enum. */
   public static SkylarkClassObject getSkylarkStruct() {
-    SkylarkClassObjectConstructor constructor =
-        SkylarkClassObjectConstructor.createNative("platforms");
+    ClassObjectConstructor constructor = new NativeClassObjectConstructor("platforms") { };
     HashMap<String, Object> fields = new HashMap<>();
     for (Platform type : values()) {
       fields.put(type.skylarkKey, type);
@@ -229,8 +229,7 @@
 
     /** Returns a Skylark struct that contains the instances of this enum. */
     public static SkylarkClassObject getSkylarkStruct() {
-      SkylarkClassObjectConstructor constructor =
-          SkylarkClassObjectConstructor.createNative("platform_types");
+      ClassObjectConstructor constructor = new NativeClassObjectConstructor("platform_types") { };
       HashMap<String, Object> fields = new HashMap<>();
       for (PlatformType type : values()) {
         fields.put(type.skylarkKey, type);
diff --git a/src/main/java/com/google/devtools/build/lib/rules/apple/XcodeVersionProperties.java b/src/main/java/com/google/devtools/build/lib/rules/apple/XcodeVersionProperties.java
index 03afc08..e52726f 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/apple/XcodeVersionProperties.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/apple/XcodeVersionProperties.java
@@ -20,8 +20,9 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+import com.google.devtools.build.lib.packages.ClassObjectConstructor;
+import com.google.devtools.build.lib.packages.NativeClassObjectConstructor;
 import com.google.devtools.build.lib.packages.SkylarkClassObject;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
 import java.util.Map;
 import javax.annotation.Nullable;
 
@@ -33,8 +34,8 @@
   public static final String SKYLARK_NAME = "XcodeProperties";
 
   /** Skylark constructor and identifier for XcodeVersionProperties provider. */
-  public static final SkylarkClassObjectConstructor SKYLARK_CONSTRUCTOR =
-      SkylarkClassObjectConstructor.createNative(SKYLARK_NAME);
+  public static final ClassObjectConstructor SKYLARK_CONSTRUCTOR =
+      new NativeClassObjectConstructor(SKYLARK_NAME) { };
 
   @VisibleForTesting public static final String DEFAULT_IOS_SDK_VERSION = "8.4";
   @VisibleForTesting public static final String DEFAULT_WATCHOS_SDK_VERSION = "2.0";
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLinkParamsProvider.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLinkParamsProvider.java
index 2734acf..0f103b7 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLinkParamsProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLinkParamsProvider.java
@@ -19,16 +19,17 @@
 import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
 import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+import com.google.devtools.build.lib.packages.ClassObjectConstructor;
+import com.google.devtools.build.lib.packages.NativeClassObjectConstructor;
 import com.google.devtools.build.lib.packages.SkylarkClassObject;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
 import com.google.devtools.build.lib.rules.cpp.CcLinkParamsStore.CcLinkParamsStoreImpl;
 
 /** A target that provides C linker parameters. */
 @Immutable
 public final class CcLinkParamsProvider extends SkylarkClassObject
     implements TransitiveInfoProvider {
-  public static final SkylarkClassObjectConstructor CC_LINK_PARAMS =
-      SkylarkClassObjectConstructor.createNative("link_params");
+  public static final ClassObjectConstructor CC_LINK_PARAMS =
+      new NativeClassObjectConstructor("link_params") { };
   public static final Function<TransitiveInfoCollection, CcLinkParamsStore> TO_LINK_PARAMS =
       new Function<TransitiveInfoCollection, CcLinkParamsStore>() {
         @Override
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaProvider.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaProvider.java
index d4b2b99..73ba875 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaProvider.java
@@ -21,8 +21,9 @@
 import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
 import com.google.devtools.build.lib.analysis.TransitiveInfoProviderMap;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+import com.google.devtools.build.lib.packages.ClassObjectConstructor;
+import com.google.devtools.build.lib.packages.NativeClassObjectConstructor;
 import com.google.devtools.build.lib.packages.SkylarkClassObject;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
 import com.google.devtools.build.lib.syntax.SkylarkList;
 import java.util.LinkedList;
 import java.util.List;
@@ -33,8 +34,8 @@
 @Immutable
 public final class JavaProvider extends SkylarkClassObject implements TransitiveInfoProvider {
 
-  public static final SkylarkClassObjectConstructor JAVA_PROVIDER =
-      SkylarkClassObjectConstructor.createNative("java_common.provider");
+  public static final ClassObjectConstructor JAVA_PROVIDER =
+      new NativeClassObjectConstructor("java_common.provider") { };
 
   private static final Set<Class<? extends TransitiveInfoProvider>> ALLOWED_PROVIDERS =
       ImmutableSet.of(
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaSkylarkCommon.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaSkylarkCommon.java
index 20f88f9..c7b4134 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaSkylarkCommon.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaSkylarkCommon.java
@@ -25,7 +25,7 @@
 import com.google.devtools.build.lib.collect.nestedset.NestedSet;
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
 import com.google.devtools.build.lib.collect.nestedset.Order;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
+import com.google.devtools.build.lib.packages.ClassObjectConstructor;
 import com.google.devtools.build.lib.rules.SkylarkRuleContext;
 import com.google.devtools.build.lib.rules.java.proto.StrictDepsUtils;
 import com.google.devtools.build.lib.skylarkinterface.Param;
@@ -48,7 +48,7 @@
     structField = true,
     doc = "Returns the Java declared provider."
   )
-  public SkylarkClassObjectConstructor getJavaProvider() {
+  public ClassObjectConstructor getJavaProvider() {
     return JavaProvider.JAVA_PROVIDER;
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/AppleDebugOutputsProvider.java b/src/main/java/com/google/devtools/build/lib/rules/objc/AppleDebugOutputsProvider.java
index d80c323..b25c616 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/AppleDebugOutputsProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/AppleDebugOutputsProvider.java
@@ -18,8 +18,9 @@
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+import com.google.devtools.build.lib.packages.ClassObjectConstructor;
+import com.google.devtools.build.lib.packages.NativeClassObjectConstructor;
 import com.google.devtools.build.lib.packages.SkylarkClassObject;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
 import java.util.HashMap;
 import java.util.Map.Entry;
 
@@ -55,8 +56,8 @@
     }
   }
 
-  public static final SkylarkClassObjectConstructor SKYLARK_PROVIDER =
-      SkylarkClassObjectConstructor.createNative("AppleDebugOutputs");
+  public static final ClassObjectConstructor SKYLARK_PROVIDER =
+      new NativeClassObjectConstructor("AppleDebugOutputs") { };
 
   /**
    * Creates a new provider instance.
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/AppleDylibBinaryProvider.java b/src/main/java/com/google/devtools/build/lib/rules/objc/AppleDylibBinaryProvider.java
index b0f494d..76e04d6 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/AppleDylibBinaryProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/AppleDylibBinaryProvider.java
@@ -17,8 +17,9 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
+import com.google.devtools.build.lib.packages.ClassObjectConstructor;
+import com.google.devtools.build.lib.packages.NativeClassObjectConstructor;
 import com.google.devtools.build.lib.packages.SkylarkClassObject;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
 
 /**
  * Provider containing the executable binary output that was built using an apple_binary target with
@@ -37,8 +38,8 @@
   public static final String SKYLARK_NAME = "AppleDylibBinary";
 
  /** Skylark constructor and identifier for AppleDylibBinaryProvider. */
-  public static final SkylarkClassObjectConstructor SKYLARK_CONSTRUCTOR =
-      SkylarkClassObjectConstructor.createNative(SKYLARK_NAME);
+  public static final ClassObjectConstructor SKYLARK_CONSTRUCTOR =
+     new NativeClassObjectConstructor(SKYLARK_NAME) { };
 
   private final Artifact dylibBinary;
   private final ObjcProvider depsObjcProvider;
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/AppleExecutableBinaryProvider.java b/src/main/java/com/google/devtools/build/lib/rules/objc/AppleExecutableBinaryProvider.java
index 501074f..24002a4 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/AppleExecutableBinaryProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/AppleExecutableBinaryProvider.java
@@ -17,8 +17,9 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
+import com.google.devtools.build.lib.packages.ClassObjectConstructor;
+import com.google.devtools.build.lib.packages.NativeClassObjectConstructor;
 import com.google.devtools.build.lib.packages.SkylarkClassObject;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
 
 /**
  * Provider containing the executable binary output that was built using an apple_binary target with
@@ -37,8 +38,8 @@
   public static final String SKYLARK_NAME = "AppleExecutableBinary";
 
  /** Skylark constructor and identifier for AppleExecutableBinaryProvider. */
-  public static final SkylarkClassObjectConstructor SKYLARK_CONSTRUCTOR =
-      SkylarkClassObjectConstructor.createNative(SKYLARK_NAME);
+  public static final ClassObjectConstructor SKYLARK_CONSTRUCTOR =
+     new NativeClassObjectConstructor(SKYLARK_NAME) { };
 
   private final Artifact appleExecutableBinary;
   private final ObjcProvider depsObjcProvider;
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/AppleLoadableBundleBinaryProvider.java b/src/main/java/com/google/devtools/build/lib/rules/objc/AppleLoadableBundleBinaryProvider.java
index ce7d2d4..f271b73 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/AppleLoadableBundleBinaryProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/AppleLoadableBundleBinaryProvider.java
@@ -17,8 +17,9 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
+import com.google.devtools.build.lib.packages.ClassObjectConstructor;
+import com.google.devtools.build.lib.packages.NativeClassObjectConstructor;
 import com.google.devtools.build.lib.packages.SkylarkClassObject;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
 
 /**
  * Provider containing the executable binary output that was built using an apple_binary target with
@@ -34,8 +35,8 @@
   public static final String SKYLARK_NAME = "AppleLoadableBundleBinary";
 
  /** Skylark constructor and identifier for AppleLoadableBundleBinary. */
-  public static final SkylarkClassObjectConstructor SKYLARK_CONSTRUCTOR =
-      SkylarkClassObjectConstructor.createNative(SKYLARK_NAME);
+  public static final ClassObjectConstructor SKYLARK_CONSTRUCTOR =
+     new NativeClassObjectConstructor(SKYLARK_NAME) { };
 
   private final Artifact appleLoadableBundleBinary;
 
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/AppleSkylarkCommon.java b/src/main/java/com/google/devtools/build/lib/rules/objc/AppleSkylarkCommon.java
index 03317d1..fd9b10b 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/AppleSkylarkCommon.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/AppleSkylarkCommon.java
@@ -16,8 +16,8 @@
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.packages.ClassObjectConstructor;
 import com.google.devtools.build.lib.packages.SkylarkClassObject;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
 import com.google.devtools.build.lib.rules.apple.AppleToolchain;
 import com.google.devtools.build.lib.rules.apple.Platform;
 import com.google.devtools.build.lib.rules.apple.Platform.PlatformType;
@@ -116,7 +116,7 @@
             + "the XcodeVersionProperties provider, use this as the key with which to retrieve it.",
     structField = true
   )
-  public SkylarkClassObjectConstructor getXcodeVersionPropertiesConstructor() {
+  public ClassObjectConstructor getXcodeVersionPropertiesConstructor() {
     return XcodeVersionProperties.SKYLARK_CONSTRUCTOR;
   }
 
@@ -127,7 +127,7 @@
             + "the AppleDylibBinary provider, use this as the key with which to retrieve it.",
     structField = true
   )
-  public SkylarkClassObjectConstructor getAppleDylibBinaryConstructor() {
+  public ClassObjectConstructor getAppleDylibBinaryConstructor() {
     return AppleDylibBinaryProvider.SKYLARK_CONSTRUCTOR;
   }
 
@@ -138,7 +138,7 @@
             + "the AppleExecutableBinary provider, use this as the key with which to retrieve it.",
     structField = true
   )
-  public SkylarkClassObjectConstructor getAppleExecutableBinaryConstructor() {
+  public ClassObjectConstructor getAppleExecutableBinaryConstructor() {
     return AppleExecutableBinaryProvider.SKYLARK_CONSTRUCTOR;
   }
 
@@ -150,7 +150,7 @@
             + "with which to retrieve it.",
     structField = true
   )
-  public SkylarkClassObjectConstructor getAppleLoadableBundleBinaryConstructor() {
+  public ClassObjectConstructor getAppleLoadableBundleBinaryConstructor() {
     return AppleLoadableBundleBinaryProvider.SKYLARK_CONSTRUCTOR;
   }
 
@@ -161,7 +161,7 @@
             + "attributes exposed by ios_device.",
     structField = true
   )
-  public SkylarkClassObjectConstructor getIosDeviceProviderConstructor() {
+  public ClassObjectConstructor getIosDeviceProviderConstructor() {
     return IosDeviceProvider.SKYLARK_CONSTRUCTOR;
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/IosDeviceProvider.java b/src/main/java/com/google/devtools/build/lib/rules/objc/IosDeviceProvider.java
index 810f071..79e62b8 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/IosDeviceProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/IosDeviceProvider.java
@@ -19,8 +19,9 @@
 import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
 import com.google.devtools.build.lib.analysis.actions.TemplateExpansionAction.Substitution;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+import com.google.devtools.build.lib.packages.ClassObjectConstructor;
+import com.google.devtools.build.lib.packages.NativeClassObjectConstructor;
 import com.google.devtools.build.lib.packages.SkylarkClassObject;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
 import com.google.devtools.build.lib.rules.apple.DottedVersion;
 import com.google.devtools.build.lib.util.Preconditions;
 import java.util.Map;
@@ -77,8 +78,8 @@
   public static final String SKYLARK_NAME = "IosDevice";
 
   /** Skylark constructor and identifier for the IosDeviceProvider. */
-  public static final SkylarkClassObjectConstructor SKYLARK_CONSTRUCTOR =
-      SkylarkClassObjectConstructor.createNative(SKYLARK_NAME);
+  public static final ClassObjectConstructor SKYLARK_CONSTRUCTOR =
+      new NativeClassObjectConstructor(SKYLARK_NAME) { };
 
   private final String type;
   private final DottedVersion iosVersion;
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProvider.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProvider.java
index c16a105..a028b09 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProvider.java
@@ -30,8 +30,9 @@
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
 import com.google.devtools.build.lib.collect.nestedset.Order;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+import com.google.devtools.build.lib.packages.ClassObjectConstructor;
+import com.google.devtools.build.lib.packages.NativeClassObjectConstructor;
 import com.google.devtools.build.lib.packages.SkylarkClassObject;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
 import com.google.devtools.build.lib.rules.cpp.CcLinkParamsProvider;
 import com.google.devtools.build.lib.rules.cpp.CppModuleMap;
 import com.google.devtools.build.lib.rules.cpp.LinkerInputs;
@@ -505,15 +506,20 @@
   // Items which should be passed to strictly direct dependers, but not transitive dependers.
   private final ImmutableMap<Key<?>, NestedSet<?>> strictDependencyItems;
 
-  private static final SkylarkClassObjectConstructor OBJC_PROVIDER =
-      SkylarkClassObjectConstructor.createNative("objc_provider");
+  private static final ClassObjectConstructor OBJC_PROVIDER =
+      new NativeClassObjectConstructor("objc_provider") {
+        @Override
+        public String getErrorMessageFormatForInstances() {
+          return "ObjcProvider field %s could not be instantiated";
+        }
+      };
 
   private ObjcProvider(
       ImmutableMap<Key<?>, NestedSet<?>> items,
       ImmutableMap<Key<?>, NestedSet<?>> nonPropagatedItems,
       ImmutableMap<Key<?>, NestedSet<?>> strictDependencyItems,
       ImmutableMap<String, Object> skylarkFields) {
-    super(OBJC_PROVIDER, skylarkFields, "ObjcProvider field %s could not be instantiated");
+    super(OBJC_PROVIDER, skylarkFields);
     this.items = Preconditions.checkNotNull(items);
     this.nonPropagatedItems = Preconditions.checkNotNull(nonPropagatedItems);
     this.strictDependencyItems = Preconditions.checkNotNull(strictDependencyItems);
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProviderSkylarkConverters.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProviderSkylarkConverters.java
index ee8a37c..4666590 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProviderSkylarkConverters.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProviderSkylarkConverters.java
@@ -24,8 +24,8 @@
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.collect.nestedset.NestedSet;
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
+import com.google.devtools.build.lib.packages.NativeClassObjectConstructor;
 import com.google.devtools.build.lib.packages.SkylarkClassObject;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
 import com.google.devtools.build.lib.rules.objc.ObjcProvider.Key;
 import com.google.devtools.build.lib.syntax.EvalException;
 import com.google.devtools.build.lib.syntax.EvalUtils;
@@ -162,7 +162,7 @@
     public Object valueForSkylark(Key<?> javaKey, NestedSet<?> javaValue) {
       NestedSetBuilder<SkylarkClassObject> result = NestedSetBuilder.stableOrder();
       for (BundleableFile bundleableFile : (Iterable<BundleableFile>) javaValue) {
-        result.add(SkylarkClassObjectConstructor.STRUCT.create(
+        result.add(NativeClassObjectConstructor.STRUCT.create(
             ImmutableMap.<String, Object>of(
                 BUNDLED_FIELD, bundleableFile.getBundled(),
                 BUNDLE_PATH_FIELD, bundleableFile.getBundlePath()
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/XcTestAppProvider.java b/src/main/java/com/google/devtools/build/lib/rules/objc/XcTestAppProvider.java
index 3a9656d..3821940 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/XcTestAppProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/XcTestAppProvider.java
@@ -18,8 +18,9 @@
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+import com.google.devtools.build.lib.packages.ClassObjectConstructor;
+import com.google.devtools.build.lib.packages.NativeClassObjectConstructor;
 import com.google.devtools.build.lib.packages.SkylarkClassObject;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory;
 import com.google.devtools.build.lib.util.Preconditions;
@@ -37,8 +38,13 @@
    */
   public static final String XCTEST_APP_SKYLARK_PROVIDER_NAME = "xctest_app";
 
-  private static final SkylarkClassObjectConstructor XCTEST_APP_PROVIDER =
-      SkylarkClassObjectConstructor.createNative("xctest_app_provider");
+  private static final ClassObjectConstructor XCTEST_APP_PROVIDER =
+      new NativeClassObjectConstructor("xctest_app_provider") {
+        @Override
+        public String getErrorMessageFormatForInstances() {
+          return "XcTestAppProvider field %s could not be instantiated";
+        }
+      };
 
   private final Artifact bundleLoader;
   private final Artifact ipa;
@@ -47,8 +53,7 @@
   XcTestAppProvider(Artifact bundleLoader, Artifact ipa, ObjcProvider objcProvider) {
     super(
         XCTEST_APP_PROVIDER,
-        getSkylarkFields(bundleLoader, ipa, objcProvider),
-        "XcTestAppProvider field %s could not be instantiated");
+        getSkylarkFields(bundleLoader, ipa, objcProvider));
     this.bundleLoader = Preconditions.checkNotNull(bundleLoader);
     this.ipa = Preconditions.checkNotNull(ipa);
     this.objcProvider = Preconditions.checkNotNull(objcProvider);
diff --git a/src/main/java/com/google/devtools/build/lib/rules/python/PyCommon.java b/src/main/java/com/google/devtools/build/lib/rules/python/PyCommon.java
index f5875fa..a343883 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/python/PyCommon.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/python/PyCommon.java
@@ -39,9 +39,9 @@
 import com.google.devtools.build.lib.collect.nestedset.Order;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
 import com.google.devtools.build.lib.packages.BuildType;
+import com.google.devtools.build.lib.packages.NativeClassObjectConstructor;
 import com.google.devtools.build.lib.packages.Rule;
 import com.google.devtools.build.lib.packages.SkylarkClassObject;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
 import com.google.devtools.build.lib.rules.cpp.CppFileTypes;
 import com.google.devtools.build.lib.rules.test.InstrumentedFilesCollector;
 import com.google.devtools.build.lib.rules.test.InstrumentedFilesCollector.LocalMetadataCollector;
@@ -159,7 +159,7 @@
    */
   public static SkylarkClassObject createSourceProvider(
       NestedSet<Artifact> transitivePythonSources, boolean isUsingSharedLibrary) {
-    return SkylarkClassObjectConstructor.STRUCT.create(
+    return NativeClassObjectConstructor.STRUCT.create(
         ImmutableMap.<String, Object>of(
             TRANSITIVE_PYTHON_SRCS,
             SkylarkNestedSet.of(Artifact.class, transitivePythonSources),
diff --git a/src/main/java/com/google/devtools/build/lib/rules/test/ExecutionInfoProvider.java b/src/main/java/com/google/devtools/build/lib/rules/test/ExecutionInfoProvider.java
index 37639c6..5567f27 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/test/ExecutionInfoProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/test/ExecutionInfoProvider.java
@@ -16,8 +16,9 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+import com.google.devtools.build.lib.packages.ClassObjectConstructor;
+import com.google.devtools.build.lib.packages.NativeClassObjectConstructor;
 import com.google.devtools.build.lib.packages.SkylarkClassObject;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
 import java.util.Map;
 
 /**
@@ -29,8 +30,9 @@
     implements TransitiveInfoProvider {
 
   /** Skylark constructor and identifier for ExecutionInfoProvider. */
-  public static final SkylarkClassObjectConstructor SKYLARK_CONSTRUCTOR =
-      SkylarkClassObjectConstructor.createNative("ExecutionInfo");
+  public static final ClassObjectConstructor SKYLARK_CONSTRUCTOR =
+      new NativeClassObjectConstructor("ExecutionInfo") {
+      };
 
   private final ImmutableMap<String, String> executionInfo;
 
diff --git a/src/main/java/com/google/devtools/build/lib/rules/test/TestEnvironmentProvider.java b/src/main/java/com/google/devtools/build/lib/rules/test/TestEnvironmentProvider.java
index 9c81c0c..e2b5fc8 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/test/TestEnvironmentProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/test/TestEnvironmentProvider.java
@@ -18,8 +18,9 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+import com.google.devtools.build.lib.packages.ClassObjectConstructor;
+import com.google.devtools.build.lib.packages.NativeClassObjectConstructor;
 import com.google.devtools.build.lib.packages.SkylarkClassObject;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
 import java.util.Map;
 
 /** Provider containing any additional environment variables for use in the test action. */
@@ -28,8 +29,8 @@
     implements TransitiveInfoProvider {
 
   /** Skylark constructor and identifier for TestEnvironmentProvider. */
-  public static final SkylarkClassObjectConstructor SKYLARK_CONSTRUCTOR =
-      SkylarkClassObjectConstructor.createNative("TestEnvironment");
+  public static final ClassObjectConstructor SKYLARK_CONSTRUCTOR =
+      new NativeClassObjectConstructor("TestEnvironment") { };
 
   private final Map<String, String> environment;
 
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkIntegrationTest.java b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkIntegrationTest.java
index bf36646..14f783e 100644
--- a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkIntegrationTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkIntegrationTest.java
@@ -36,6 +36,7 @@
 import com.google.devtools.build.lib.collect.nestedset.NestedSet;
 import com.google.devtools.build.lib.packages.AttributeContainer;
 import com.google.devtools.build.lib.packages.BuildFileContainsErrorsException;
+import com.google.devtools.build.lib.packages.ClassObjectConstructor;
 import com.google.devtools.build.lib.packages.SkylarkClassObject;
 import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
 import com.google.devtools.build.lib.rules.test.InstrumentedFilesProvider;
@@ -932,7 +933,7 @@
     );
 
     ConfiguredTarget configuredTarget = getConfiguredTarget("//test:r");
-    SkylarkClassObjectConstructor.Key key = new SkylarkClassObjectConstructor.SkylarkKey(
+    ClassObjectConstructor.Key key = new SkylarkClassObjectConstructor.SkylarkKey(
         Label.create(configuredTarget.getLabel().getPackageIdentifier(), "extension.bzl"),
         "my_provider");
     SkylarkProviders skylarkProviders = configuredTarget.getProvider(SkylarkProviders.class);
@@ -959,7 +960,7 @@
     );
 
     ConfiguredTarget configuredTarget  = getConfiguredTarget("//test:r");
-    SkylarkClassObjectConstructor.Key key = new SkylarkClassObjectConstructor.SkylarkKey(
+    ClassObjectConstructor.Key key = new SkylarkClassObjectConstructor.SkylarkKey(
         Label.create(configuredTarget.getLabel().getPackageIdentifier(), "extension.bzl"),
         "my_provider");
     SkylarkProviders skylarkProviders = configuredTarget.getProvider(SkylarkProviders.class);
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleClassFunctionsTest.java b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleClassFunctionsTest.java
index 9067b89..2966fc8 100644
--- a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleClassFunctionsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleClassFunctionsTest.java
@@ -32,6 +32,7 @@
 import com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition;
 import com.google.devtools.build.lib.packages.BuildType;
 import com.google.devtools.build.lib.packages.ImplicitOutputsFunction;
+import com.google.devtools.build.lib.packages.NativeClassObjectConstructor;
 import com.google.devtools.build.lib.packages.PredicateWithMessage;
 import com.google.devtools.build.lib.packages.RequiredProviders;
 import com.google.devtools.build.lib.packages.RuleClass;
@@ -1039,17 +1040,17 @@
   }
 
   private static SkylarkClassObject makeStruct(String field, Object value) {
-    return SkylarkClassObjectConstructor.STRUCT.create(
+    return NativeClassObjectConstructor.STRUCT.create(
         ImmutableMap.of(field, value),
         "no field '%'");
   }
 
   private static SkylarkClassObject makeBigStruct(Environment env) {
     // struct(a=[struct(x={1:1}), ()], b=(), c={2:2})
-    return SkylarkClassObjectConstructor.STRUCT.create(
+    return NativeClassObjectConstructor.STRUCT.create(
         ImmutableMap.<String, Object>of(
             "a", MutableList.<Object>of(env,
-                SkylarkClassObjectConstructor.STRUCT.create(ImmutableMap.<String, Object>of(
+                NativeClassObjectConstructor.STRUCT.create(ImmutableMap.<String, Object>of(
                     "x", SkylarkDict.<Object, Object>of(env, 1, 1)),
                     "no field '%s'"),
                 Tuple.of()),
@@ -1138,10 +1139,10 @@
         "data = struct(x = 1)"
     );
     SkylarkClassObject data = (SkylarkClassObject) lookup("data");
-    assertThat(SkylarkClassObjectConstructor.STRUCT.isExported()).isTrue();
-    assertThat(data.getConstructor()).isEqualTo(SkylarkClassObjectConstructor.STRUCT);
+    assertThat(NativeClassObjectConstructor.STRUCT.isExported()).isTrue();
+    assertThat(data.getConstructor()).isEqualTo(NativeClassObjectConstructor.STRUCT);
     assertThat(data.getConstructor().getKey())
-        .isEqualTo(SkylarkClassObjectConstructor.STRUCT.getKey());
+        .isEqualTo(NativeClassObjectConstructor.STRUCT.getKey());
   }
 
   @Test
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleImplementationFunctionsTest.java b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleImplementationFunctionsTest.java
index 36bdd8d..343e03c 100644
--- a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleImplementationFunctionsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleImplementationFunctionsTest.java
@@ -886,7 +886,7 @@
     Object provider = configuredTarget.getProvider(SkylarkProviders.class).getValue("default");
     assertThat(provider).isInstanceOf(SkylarkClassObject.class);
     SkylarkClassObject defaultProvider = (SkylarkClassObject) provider;
-    assertThat((defaultProvider).getConstructor().getKey().getExportedName())
+    assertThat((defaultProvider).getConstructor().getPrintableName())
         .isEqualTo("default_provider");
 
     // Test .runfiles
@@ -936,7 +936,7 @@
     Object provider = configuredTarget.getProvider(SkylarkProviders.class).getValue("default");
     assertThat(provider).isInstanceOf(SkylarkClassObject.class);
     SkylarkClassObject defaultProvider = (SkylarkClassObject) provider;
-    assertThat((defaultProvider).getConstructor().getKey().getExportedName())
+    assertThat((defaultProvider).getConstructor().getPrintableName())
         .isEqualTo("default_provider");
   }
 
@@ -1008,7 +1008,7 @@
     ConfiguredTarget configuredTarget = getConfiguredTarget("//test:my_rule");
     Object provider = configuredTarget.getProvider(SkylarkProviders.class).getValue("proxy");
     assertThat(provider).isInstanceOf(SkylarkClassObject.class);
-    assertThat(((SkylarkClassObject) provider).getConstructor().getKey().getExportedName())
+    assertThat(((SkylarkClassObject) provider).getConstructor().getPrintableName())
         .isEqualTo("foo_provider");
   }
 
@@ -1054,7 +1054,7 @@
     ConfiguredTarget configuredTarget = getConfiguredTarget("//test:my_rule");
     Object provider = configuredTarget.getProvider(SkylarkProviders.class).getValue("proxy");
     assertThat(provider).isInstanceOf(SkylarkClassObject.class);
-    assertThat(((SkylarkClassObject) provider).getConstructor().getKey().getExportedName())
+    assertThat(((SkylarkClassObject) provider).getConstructor().getPrintableName())
         .isEqualTo("foo_provider");
   }
 
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/EvalUtilsTest.java b/src/test/java/com/google/devtools/build/lib/syntax/EvalUtilsTest.java
index e0b48b8..e5f669e 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/EvalUtilsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/EvalUtilsTest.java
@@ -22,7 +22,7 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
-import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
+import com.google.devtools.build.lib.packages.NativeClassObjectConstructor;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
 import com.google.devtools.build.lib.syntax.EvalUtils.ComparisonException;
 import com.google.devtools.build.lib.syntax.SkylarkList.MutableList;
@@ -129,7 +129,7 @@
         SkylarkDict.of(env, "key", 123),
         SkylarkDict.of(env, 123, "value"),
         NestedSetBuilder.stableOrder().add(1).add(2).add(3).build(),
-        SkylarkClassObjectConstructor.STRUCT.create(
+        NativeClassObjectConstructor.STRUCT.create(
             ImmutableMap.of("key", (Object) "value"), "no field %s"),
     };