|  | // Copyright 2016 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.common.collect.ImmutableList; | 
|  | import com.google.common.collect.ImmutableMap; | 
|  | import com.google.devtools.build.lib.cmdline.Label; | 
|  | import com.google.devtools.build.lib.events.Location; | 
|  | import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter; | 
|  | 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 com.google.devtools.build.lib.util.Preconditions; | 
|  | import java.util.Map; | 
|  | import java.util.Objects; | 
|  | import javax.annotation.Nullable; | 
|  |  | 
|  | /** | 
|  | * Declared provider defined in Skylark. | 
|  | * | 
|  | * <p>This is a result of calling {@code provider()} function from Skylark ({@link | 
|  | * com.google.devtools.build.lib.analysis.skylark.SkylarkRuleClassFunctions#provider}). | 
|  | */ | 
|  | public class SkylarkProvider extends Provider implements SkylarkExportable { | 
|  |  | 
|  | private static final FunctionSignature.WithValues<Object, SkylarkType> SIGNATURE = | 
|  | FunctionSignature.WithValues.create(FunctionSignature.KWARGS); | 
|  |  | 
|  | @Nullable private SkylarkKey key; | 
|  | @Nullable private String errorMessageFormatForInstances; | 
|  |  | 
|  | private static final String DEFAULT_ERROR_MESSAFE = "Object has no '%s' attribute."; | 
|  |  | 
|  | /** | 
|  | * Creates a Skylark-defined Declared Provider ({@link Info} constructor). | 
|  | * | 
|  | * <p>Needs to be exported later. | 
|  | */ | 
|  | public SkylarkProvider(String name, | 
|  | @Nullable Iterable<String> fields, | 
|  | Location location) { | 
|  | this(name, buildSignature(fields), location); | 
|  | } | 
|  |  | 
|  | private SkylarkProvider( | 
|  | String name, | 
|  | FunctionSignature.WithValues<Object, SkylarkType> signature, Location location) { | 
|  | super(name, signature, location); | 
|  | this.errorMessageFormatForInstances = DEFAULT_ERROR_MESSAFE; | 
|  | } | 
|  |  | 
|  | private static FunctionSignature.WithValues<Object, SkylarkType> buildSignature( | 
|  | @Nullable  Iterable<String> fields) { | 
|  | if (fields == null) { | 
|  | return SIGNATURE; | 
|  | } | 
|  | return | 
|  | FunctionSignature.WithValues.create( | 
|  | FunctionSignature.namedOnly(0, ImmutableList.copyOf(fields).toArray(new String[0])) | 
|  | ); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | protected Info createInstanceFromSkylark(Object[] args, Location loc) throws EvalException { | 
|  | if (signature.getSignature().getShape().hasKwArg()) { | 
|  | @SuppressWarnings("unchecked") | 
|  | Map<String, Object> kwargs = (Map<String, Object>) args[0]; | 
|  | return new SkylarkInfo(this, kwargs, loc); | 
|  | } else { | 
|  | // todo(dslomov): implement shape sharing. | 
|  | ImmutableList<String> names = signature.getSignature().getNames(); | 
|  | Preconditions.checkState(names.size() == args.length); | 
|  | ImmutableMap.Builder<String, Object> fields = ImmutableMap.builder(); | 
|  | for (int i = 0; i < args.length; i++) { | 
|  | if (args[i] != null) { | 
|  | fields.put(names.get(i), args[i]); | 
|  | } | 
|  | } | 
|  | return new SkylarkInfo(this, fields.build(), loc); | 
|  | } | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isExported() { | 
|  | return key != null; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public SkylarkKey getKey() { | 
|  | Preconditions.checkState(isExported()); | 
|  | return key; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String getName() { | 
|  | return key != null ? key.getExportedName() : "<no name>"; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String getPrintableName() { | 
|  | return 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 | 
|  | public int hashCode() { | 
|  | if (isExported()) { | 
|  | return getKey().hashCode(); | 
|  | } | 
|  | return System.identityHashCode(this); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean equals(@Nullable Object otherObject) { | 
|  | if (!(otherObject instanceof SkylarkProvider)) { | 
|  | return false; | 
|  | } | 
|  | SkylarkProvider other = (SkylarkProvider) otherObject; | 
|  |  | 
|  | if (this.isExported() && other.isExported()) { | 
|  | return this.getKey().equals(other.getKey()); | 
|  | } else { | 
|  | return this == other; | 
|  | } | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isImmutable() { | 
|  | // Hash code for non exported constructors may be changed | 
|  | return isExported(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void repr(SkylarkPrinter printer) { | 
|  | printer.append("<provider>"); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * A serializable representation of Skylark-defined {@link SkylarkProvider} that uniquely | 
|  | * identifies all {@link SkylarkProvider}s that are exposed to SkyFrame. | 
|  | */ | 
|  | public static class SkylarkKey extends Key { | 
|  | private final Label extensionLabel; | 
|  | private final String exportedName; | 
|  |  | 
|  | public SkylarkKey(Label extensionLabel, String exportedName) { | 
|  | this.extensionLabel = Preconditions.checkNotNull(extensionLabel); | 
|  | this.exportedName = Preconditions.checkNotNull(exportedName); | 
|  | } | 
|  |  | 
|  | public Label getExtensionLabel() { | 
|  | return extensionLabel; | 
|  | } | 
|  |  | 
|  | public String getExportedName() { | 
|  | return exportedName; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toString() { | 
|  | return exportedName; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public int hashCode() { | 
|  | return Objects.hash(extensionLabel, exportedName); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean equals(Object obj) { | 
|  | if (this == obj) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (!(obj instanceof SkylarkKey)) { | 
|  | return false; | 
|  | } | 
|  | SkylarkKey other = (SkylarkKey) obj; | 
|  | return Objects.equals(this.extensionLabel, other.extensionLabel) | 
|  | && Objects.equals(this.exportedName, other.exportedName); | 
|  | } | 
|  | } | 
|  | } |