diff options
Diffstat (limited to 'src/main/java')
20 files changed, 268 insertions, 210 deletions
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 ad2290adb6..f8d10cfab5 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 @@ -25,6 +25,7 @@ import com.google.devtools.build.lib.cmdline.LabelSyntaxException; import com.google.devtools.build.lib.events.Location; import com.google.devtools.build.lib.packages.AggregatingAttributeMapper; import com.google.devtools.build.lib.packages.Rule; +import com.google.devtools.build.lib.packages.SkylarkClassObject; import com.google.devtools.build.lib.rules.repository.RepositoryFunction.RepositoryFunctionException; import com.google.devtools.build.lib.skyframe.FileSymlinkException; import com.google.devtools.build.lib.skyframe.FileValue; @@ -33,7 +34,6 @@ import com.google.devtools.build.lib.skyframe.PackageLookupValue; import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable; import com.google.devtools.build.lib.skylarkinterface.SkylarkModule; import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory; -import com.google.devtools.build.lib.syntax.ClassObject.SkylarkClassObject; import com.google.devtools.build.lib.syntax.EvalException; import com.google.devtools.build.lib.syntax.Runtime; import com.google.devtools.build.lib.syntax.SkylarkType; @@ -46,7 +46,6 @@ import com.google.devtools.build.lib.vfs.RootedPath; import com.google.devtools.build.skyframe.SkyFunction.Environment; import com.google.devtools.build.skyframe.SkyFunctionException.Transience; import com.google.devtools.build.skyframe.SkyKey; - import java.io.File; import java.io.IOException; import java.io.OutputStream; 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 c53b92af54..9088840bd5 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 @@ -29,7 +29,6 @@ import com.google.devtools.build.lib.events.Location; import com.google.devtools.build.lib.skylarkinterface.SkylarkModule; import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory; import com.google.devtools.build.lib.syntax.ClassObject; -import com.google.devtools.build.lib.syntax.ClassObject.SkylarkClassObject; import com.google.devtools.build.lib.syntax.EvalException; import com.google.devtools.build.lib.syntax.SkylarkCallbackFunction; import com.google.devtools.build.lib.syntax.Type; 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 c20cf3bcca..3789091663 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 @@ -27,7 +27,6 @@ import com.google.common.escape.Escapers; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.events.Location; import com.google.devtools.build.lib.syntax.ClassObject; -import com.google.devtools.build.lib.syntax.ClassObject.SkylarkClassObject; import com.google.devtools.build.lib.syntax.EvalException; import com.google.devtools.build.lib.syntax.Runtime; import com.google.devtools.build.lib.syntax.SkylarkCallbackFunction; @@ -35,7 +34,6 @@ import com.google.devtools.build.lib.syntax.Type; import com.google.devtools.build.lib.util.StringUtil; import com.google.devtools.build.lib.vfs.FileSystemUtils; import com.google.devtools.build.lib.vfs.PathFragment; - import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -44,7 +42,6 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; - import javax.annotation.Nullable; /** 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 4c3138a2c6..7b8e57f500 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 @@ -1526,7 +1526,7 @@ public final class PackageFactory { builder.put(function.getName(), function); } } - return new ClassObject.SkylarkClassObject(builder.build(), "no native function or rule '%s'"); + return new SkylarkClassObject(builder.build(), "no native function or rule '%s'"); } private void buildPkgEnv(Environment pkgEnv, PackageContext context, RuleFactory ruleFactory) { 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 new file mode 100644 index 0000000000..d28146a526 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/packages/SkylarkClassObject.java @@ -0,0 +1,184 @@ +// 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.base.Joiner; +import com.google.common.collect.ImmutableCollection; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Ordering; +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.skylarkinterface.SkylarkModule; +import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory; +import com.google.devtools.build.lib.skylarkinterface.SkylarkValue; +import com.google.devtools.build.lib.syntax.ClassObject; +import com.google.devtools.build.lib.syntax.Concatable; +import com.google.devtools.build.lib.syntax.EvalException; +import com.google.devtools.build.lib.syntax.EvalUtils; +import com.google.devtools.build.lib.syntax.Printer; +import com.google.devtools.build.lib.syntax.SkylarkType; +import com.google.devtools.build.lib.util.Preconditions; +import java.io.Serializable; +import java.util.Map; + +/** An implementation class of ClassObject for structs created in Skylark code. */ +@SkylarkModule( + name = "struct", + category = SkylarkModuleCategory.BUILTIN, + doc = + "A special language element to support structs (i.e. simple value objects). " + + "See the global <a href=\"globals.html#struct\">struct</a> function " + + "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 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. + */ + public SkylarkClassObject(Map<String, Object> values) { + this.values = copyValues(values); + this.creationLoc = null; + this.errorMessage = DEFAULT_ERROR_MESSAGE; + } + + /** + * 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. + */ + public SkylarkClassObject(Map<String, Object> values, String errorMessage) { + this.values = copyValues(values); + this.creationLoc = null; + this.errorMessage = Preconditions.checkNotNull(errorMessage); + } + + public SkylarkClassObject(Map<String, Object> values, Location creationLoc) { + this.values = copyValues(values); + this.creationLoc = Preconditions.checkNotNull(creationLoc); + this.errorMessage = DEFAULT_ERROR_MESSAGE; + } + + // Ensure that values are all acceptable to Skylark before to stuff them in a ClassObject + private ImmutableMap<String, Object> copyValues(Map<String, Object> values) { + ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder(); + for (Map.Entry<String, Object> e : values.entrySet()) { + builder.put(e.getKey(), SkylarkType.convertToSkylark(e.getValue(), null)); + } + return builder.build(); + } + + @Override + public Object getValue(String name) { + return values.get(name); + } + + /** + * Returns a value and try to cast it into specified type + */ + public <TYPE> TYPE getValue(String key, Class<TYPE> type) throws EvalException { + Object obj = values.get(key); + if (obj == null) { + return null; + } + SkylarkType.checkType(obj, type, key); + return type.cast(obj); + } + + @Override + public ImmutableCollection<String> getKeys() { + return values.keySet(); + } + + public Location getCreationLoc() { + return Preconditions.checkNotNull(creationLoc, + "This struct was not created in a Skylark code"); + } + + @Override + public Concatter getConcatter() { + return StructConcatter.INSTANCE; + } + + private static class StructConcatter implements Concatter { + private static final StructConcatter INSTANCE = new StructConcatter(); + + private StructConcatter() {} + + @Override + public SkylarkClassObject concat( + Concatable left, Concatable right, Location loc) throws EvalException { + SkylarkClassObject lval = (SkylarkClassObject) left; + SkylarkClassObject rval = (SkylarkClassObject) right; + SetView<String> commonFields = Sets + .intersection(lval.values.keySet(), rval.values.keySet()); + if (!commonFields.isEmpty()) { + throw new EvalException(loc, "Cannot concat structs with common field(s): " + + Joiner.on(",").join(commonFields)); + } + return new SkylarkClassObject(ImmutableMap.<String, Object>builder() + .putAll(lval.values).putAll(rval.values).build(), loc); + } + } + + @Override + public String errorMessage(String name) { + String suffix = + "Available attributes: " + + Joiner.on(", ").join(Ordering.natural().sortedCopy(values.keySet())); + return String.format(errorMessage, name) + "\n" + suffix; + } + + @Override + public boolean isImmutable() { + for (Object item : values.values()) { + if (!EvalUtils.isImmutable(item)) { + return false; + } + } + return true; + } + + /** + * Convert the object to string using Skylark syntax. The output tries to be + * reversible (but there is no guarantee, it depends on the actual values). + */ + @Override + public void write(Appendable buffer, char quotationMark) { + boolean first = true; + Printer.append(buffer, "struct("); + // Sort by key to ensure deterministic output. + for (String key : Ordering.natural().sortedCopy(values.keySet())) { + if (!first) { + Printer.append(buffer, ", "); + } + first = false; + Printer.append(buffer, key); + Printer.append(buffer, " = "); + Printer.write(buffer, values.get(key), quotationMark); + } + Printer.append(buffer, ")"); + } + + @Override + public String toString() { + return Printer.repr(this); + } +} 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 2a9892904a..54b393a7dc 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 @@ -454,7 +454,7 @@ public class WorkspaceFactory { } builder.put("bazel_version", version); - return new ClassObject.SkylarkClassObject(builder.build(), "no native function or rule '%s'"); + return new SkylarkClassObject(builder.build(), "no native function or rule '%s'"); } public static ClassObject newNativeModule(RuleClassProvider ruleClassProvider, String version) { 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 2e6dbb880b..cb22b50ba3 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 @@ -62,6 +62,7 @@ import com.google.devtools.build.lib.packages.RuleFactory; import com.google.devtools.build.lib.packages.RuleFactory.BuildLangTypedAttributeValuesMap; import com.google.devtools.build.lib.packages.RuleFactory.InvalidRuleException; import com.google.devtools.build.lib.packages.SkylarkAspect; +import com.google.devtools.build.lib.packages.SkylarkClassObject; import com.google.devtools.build.lib.packages.TargetUtils; import com.google.devtools.build.lib.packages.TestSize; import com.google.devtools.build.lib.rules.SkylarkAttr.Descriptor; @@ -70,7 +71,6 @@ import com.google.devtools.build.lib.skylarkinterface.SkylarkSignature; import com.google.devtools.build.lib.syntax.BaseFunction; import com.google.devtools.build.lib.syntax.BuiltinFunction; import com.google.devtools.build.lib.syntax.ClassObject; -import com.google.devtools.build.lib.syntax.ClassObject.SkylarkClassObject; import com.google.devtools.build.lib.syntax.Environment; import com.google.devtools.build.lib.syntax.Environment.NoSuchVariableException; import com.google.devtools.build.lib.syntax.EvalException; @@ -88,7 +88,6 @@ import com.google.devtools.build.lib.syntax.Type.ConversionException; import com.google.devtools.build.lib.util.Pair; import com.google.devtools.build.lib.util.Preconditions; import com.google.protobuf.TextFormat; - import java.util.List; import java.util.Map; import java.util.Set; @@ -193,6 +192,22 @@ public class SkylarkRuleClassFunctions { .build(); } + @SkylarkSignature(name = "struct", returnType = SkylarkClassObject.class, doc = + "Creates an immutable struct using the keyword arguments as attributes. It is used to group " + + "multiple values together.Example:<br>" + + "<pre class=\"language-python\">s = struct(x = 2, y = 3)\n" + + "return s.x + getattr(s, \"y\") # returns 5</pre>", + extraKeywords = @Param(name = "kwargs", doc = "the struct attributes"), + useLocation = true) + private static final BuiltinFunction struct = new BuiltinFunction("struct") { + @SuppressWarnings("unchecked") + public SkylarkClassObject invoke(SkylarkDict<String, Object> kwargs, Location loc) + throws EvalException { + return new SkylarkClassObject(kwargs, loc); + } + }; + + // TODO(bazel-team): implement attribute copy and other rule properties @SkylarkSignature(name = "rule", doc = "Creates a new rule. Store it in a global value, so that it can be loaded and called " 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 0cc409fd74..52e4750c7c 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 @@ -28,6 +28,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.events.Location; import com.google.devtools.build.lib.packages.Rule; +import com.google.devtools.build.lib.packages.SkylarkClassObject; import com.google.devtools.build.lib.packages.TargetUtils; import com.google.devtools.build.lib.rules.SkylarkRuleContext.Kind; import com.google.devtools.build.lib.rules.test.InstrumentedFilesCollector; @@ -36,7 +37,6 @@ import com.google.devtools.build.lib.rules.test.InstrumentedFilesProvider; import com.google.devtools.build.lib.skylarkinterface.SkylarkValue; import com.google.devtools.build.lib.syntax.BaseFunction; import com.google.devtools.build.lib.syntax.ClassObject; -import com.google.devtools.build.lib.syntax.ClassObject.SkylarkClassObject; import com.google.devtools.build.lib.syntax.Environment; import com.google.devtools.build.lib.syntax.EvalException; import com.google.devtools.build.lib.syntax.EvalExceptionWithStackTrace; @@ -49,7 +49,6 @@ import com.google.devtools.build.lib.syntax.SkylarkType; import com.google.devtools.build.lib.syntax.Type; import com.google.devtools.build.lib.util.FileType; import com.google.devtools.build.lib.util.FileTypeSet; - import java.util.Collections; import java.util.List; import java.util.Map; 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 8112e02119..248bbfd33b 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,12 +42,12 @@ import com.google.devtools.build.lib.packages.ImplicitOutputsFunction.SkylarkImp 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.shell.ShellUtils; import com.google.devtools.build.lib.shell.ShellUtils.TokenizationException; import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable; import com.google.devtools.build.lib.skylarkinterface.SkylarkModule; import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory; -import com.google.devtools.build.lib.syntax.ClassObject.SkylarkClassObject; import com.google.devtools.build.lib.syntax.EvalException; import com.google.devtools.build.lib.syntax.FuncallExpression.FuncallException; import com.google.devtools.build.lib.syntax.Runtime; @@ -58,7 +58,6 @@ import com.google.devtools.build.lib.syntax.SkylarkType; import com.google.devtools.build.lib.syntax.Type; import com.google.devtools.build.lib.util.Preconditions; import com.google.devtools.build.lib.vfs.PathFragment; - import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -67,7 +66,6 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; - import javax.annotation.Nullable; /** A Skylark API for the ruleContext. */ 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 63ad021223..8249fd121a 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 @@ -26,11 +26,12 @@ 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.concurrent.ThreadSafety.Immutable; +import com.google.devtools.build.lib.packages.SkylarkClassObject; import com.google.devtools.build.lib.rules.cpp.CppModuleMap; import com.google.devtools.build.lib.rules.cpp.LinkerInputs; import com.google.devtools.build.lib.skylarkinterface.SkylarkModule; import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory; -import com.google.devtools.build.lib.syntax.ClassObject.SkylarkClassObject; +import com.google.devtools.build.lib.syntax.Concatable.Concatter; import com.google.devtools.build.lib.syntax.EvalUtils; import com.google.devtools.build.lib.util.Preconditions; import com.google.devtools.build.lib.vfs.PathFragment; @@ -426,6 +427,11 @@ public final class ObjcProvider extends SkylarkClassObject implements Transitive this.strictDependencyItems = Preconditions.checkNotNull(strictDependencyItems); } + @Override + public Concatter getConcatter() { + return null; + } + /** * All artifacts, bundleable files, etc. of the type specified by {@code key}. */ 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 8d379e7f1c..727891e820 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.common.collect.ImmutableMap; 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.SkylarkClassObject; import com.google.devtools.build.lib.rules.objc.ObjcProvider.Key; -import com.google.devtools.build.lib.syntax.ClassObject.SkylarkClassObject; import com.google.devtools.build.lib.syntax.EvalException; import com.google.devtools.build.lib.syntax.EvalUtils; import com.google.devtools.build.lib.syntax.SkylarkNestedSet; 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 74abb33451..842e9b7d7b 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 @@ -43,11 +43,11 @@ 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.Rule; +import com.google.devtools.build.lib.packages.SkylarkClassObject; 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; import com.google.devtools.build.lib.rules.test.InstrumentedFilesProvider; -import com.google.devtools.build.lib.syntax.ClassObject.SkylarkClassObject; import com.google.devtools.build.lib.syntax.EvalException; import com.google.devtools.build.lib.syntax.EvalUtils; import com.google.devtools.build.lib.syntax.SkylarkNestedSet; diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkAspectFactory.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkAspectFactory.java index 33b544bb34..450015804b 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkAspectFactory.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkAspectFactory.java @@ -24,16 +24,15 @@ import com.google.devtools.build.lib.analysis.SkylarkProviderValidationUtil; import com.google.devtools.build.lib.events.Location; import com.google.devtools.build.lib.packages.AspectParameters; import com.google.devtools.build.lib.packages.SkylarkAspect; +import com.google.devtools.build.lib.packages.SkylarkClassObject; import com.google.devtools.build.lib.rules.SkylarkRuleContext; import com.google.devtools.build.lib.rules.SkylarkRuleContext.Kind; -import com.google.devtools.build.lib.syntax.ClassObject.SkylarkClassObject; import com.google.devtools.build.lib.syntax.Environment; import com.google.devtools.build.lib.syntax.EvalException; import com.google.devtools.build.lib.syntax.EvalExceptionWithStackTrace; import com.google.devtools.build.lib.syntax.Mutability; import com.google.devtools.build.lib.syntax.SkylarkNestedSet; import com.google.devtools.build.lib.syntax.SkylarkType; - import java.util.Map; /** diff --git a/src/main/java/com/google/devtools/build/lib/syntax/BinaryOperatorExpression.java b/src/main/java/com/google/devtools/build/lib/syntax/BinaryOperatorExpression.java index a31b037601..131ee54772 100644 --- a/src/main/java/com/google/devtools/build/lib/syntax/BinaryOperatorExpression.java +++ b/src/main/java/com/google/devtools/build/lib/syntax/BinaryOperatorExpression.java @@ -18,7 +18,7 @@ import static com.google.devtools.build.lib.syntax.compiler.ByteCodeUtils.append import com.google.common.base.Strings; import com.google.common.collect.Iterables; import com.google.devtools.build.lib.events.Location; -import com.google.devtools.build.lib.syntax.ClassObject.SkylarkClassObject; +import com.google.devtools.build.lib.syntax.Concatable.Concatter; import com.google.devtools.build.lib.syntax.SkylarkList.MutableList; import com.google.devtools.build.lib.syntax.SkylarkList.Tuple; import com.google.devtools.build.lib.syntax.compiler.ByteCodeMethodCalls; @@ -29,17 +29,15 @@ import com.google.devtools.build.lib.syntax.compiler.Jump; import com.google.devtools.build.lib.syntax.compiler.Jump.PrimitiveComparison; import com.google.devtools.build.lib.syntax.compiler.LabelAdder; import com.google.devtools.build.lib.syntax.compiler.VariableScope; - -import net.bytebuddy.implementation.bytecode.ByteCodeAppender; -import net.bytebuddy.implementation.bytecode.Duplication; -import net.bytebuddy.implementation.bytecode.Removal; -import net.bytebuddy.implementation.bytecode.StackManipulation; - import java.util.ArrayList; import java.util.Collections; import java.util.EnumSet; import java.util.IllegalFormatException; import java.util.List; +import net.bytebuddy.implementation.bytecode.ByteCodeAppender; +import net.bytebuddy.implementation.bytecode.Duplication; +import net.bytebuddy.implementation.bytecode.Removal; +import net.bytebuddy.implementation.bytecode.StackManipulation; /** * Syntax node for a binary operator expression. @@ -338,9 +336,15 @@ public final class BinaryOperatorExpression extends Expression { return SkylarkDict.plus((SkylarkDict<?, ?>) lval, (SkylarkDict<?, ?>) rval, env); } - if (lval instanceof SkylarkClassObject && rval instanceof SkylarkClassObject) { - return SkylarkClassObject.concat( - (SkylarkClassObject) lval, (SkylarkClassObject) rval, location); + if (lval instanceof Concatable && rval instanceof Concatable) { + Concatable lobj = (Concatable) lval; + Concatable robj = (Concatable) rval; + Concatter concatter = lobj.getConcatter(); + if (concatter != null && concatter.equals(robj.getConcatter())) { + return concatter.concat(lobj, robj, location); + } else { + throw typeException(lval, rval, Operator.PLUS, location); + } } // TODO(bazel-team): Remove this case. Union of sets should use '|' instead of '+'. diff --git a/src/main/java/com/google/devtools/build/lib/syntax/ClassObject.java b/src/main/java/com/google/devtools/build/lib/syntax/ClassObject.java index 79c19610e0..8ea9aa1372 100644 --- a/src/main/java/com/google/devtools/build/lib/syntax/ClassObject.java +++ b/src/main/java/com/google/devtools/build/lib/syntax/ClassObject.java @@ -13,21 +13,7 @@ // limitations under the License. package com.google.devtools.build.lib.syntax; -import com.google.common.base.Joiner; import com.google.common.collect.ImmutableCollection; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Ordering; -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.skylarkinterface.SkylarkModule; -import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory; -import com.google.devtools.build.lib.skylarkinterface.SkylarkValue; -import com.google.devtools.build.lib.util.Preconditions; - -import java.io.Serializable; -import java.util.Map; - import javax.annotation.Nullable; /** @@ -53,140 +39,4 @@ public interface ClassObject { * of this struct, or returns null to use the default error message. */ @Nullable String errorMessage(String name); - - /** An implementation class of ClassObject for structs created in Skylark code. */ - // TODO(bazel-team): maybe move the SkylarkModule annotation to the ClassObject interface? - @SkylarkModule( - name = "struct", - category = SkylarkModuleCategory.BUILTIN, - doc = - "A special language element to support structs (i.e. simple value objects). " - + "See the global <a href=\"globals.html#struct\">struct</a> function " - + "for more details." - ) - public class SkylarkClassObject implements ClassObject, SkylarkValue, 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 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. - */ - public SkylarkClassObject(Map<String, Object> values) { - this.values = copyValues(values); - this.creationLoc = null; - this.errorMessage = DEFAULT_ERROR_MESSAGE; - } - - /** - * 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. - */ - public SkylarkClassObject(Map<String, Object> values, String errorMessage) { - this.values = copyValues(values); - this.creationLoc = null; - this.errorMessage = Preconditions.checkNotNull(errorMessage); - } - - public SkylarkClassObject(Map<String, Object> values, Location creationLoc) { - this.values = copyValues(values); - this.creationLoc = Preconditions.checkNotNull(creationLoc); - this.errorMessage = DEFAULT_ERROR_MESSAGE; - } - - // Ensure that values are all acceptable to Skylark before to stuff them in a ClassObject - private ImmutableMap<String, Object> copyValues(Map<String, Object> values) { - ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder(); - for (Map.Entry<String, Object> e : values.entrySet()) { - builder.put(e.getKey(), SkylarkType.convertToSkylark(e.getValue(), null)); - } - return builder.build(); - } - - @Override - public Object getValue(String name) { - return values.get(name); - } - - /** - * Returns a value and try to cast it into specified type - */ - public <TYPE> TYPE getValue(String key, Class<TYPE> type) throws EvalException { - Object obj = values.get(key); - if (obj == null) { - return null; - } - SkylarkType.checkType(obj, type, key); - return type.cast(obj); - } - - @Override - public ImmutableCollection<String> getKeys() { - return values.keySet(); - } - - public Location getCreationLoc() { - return Preconditions.checkNotNull(creationLoc, - "This struct was not created in a Skylark code"); - } - - static SkylarkClassObject concat( - SkylarkClassObject lval, SkylarkClassObject rval, Location loc) throws EvalException { - SetView<String> commonFields = Sets.intersection(lval.values.keySet(), rval.values.keySet()); - if (!commonFields.isEmpty()) { - throw new EvalException(loc, "Cannot concat structs with common field(s): " - + Joiner.on(",").join(commonFields)); - } - return new SkylarkClassObject(ImmutableMap.<String, Object>builder() - .putAll(lval.values).putAll(rval.values).build(), loc); - } - - @Override - public String errorMessage(String name) { - String suffix = - "Available attributes: " - + Joiner.on(", ").join(Ordering.natural().sortedCopy(values.keySet())); - return String.format(errorMessage, name) + "\n" + suffix; - } - - @Override - public boolean isImmutable() { - for (Object item : values.values()) { - if (!EvalUtils.isImmutable(item)) { - return false; - } - } - return true; - } - - /** - * Convert the object to string using Skylark syntax. The output tries to be - * reversible (but there is no guarantee, it depends on the actual values). - */ - @Override - public void write(Appendable buffer, char quotationMark) { - boolean first = true; - Printer.append(buffer, "struct("); - // Sort by key to ensure deterministic output. - for (String key : Ordering.natural().sortedCopy(values.keySet())) { - if (!first) { - Printer.append(buffer, ", "); - } - first = false; - Printer.append(buffer, key); - Printer.append(buffer, " = "); - Printer.write(buffer, values.get(key), quotationMark); - } - Printer.append(buffer, ")"); - } - - @Override - public String toString() { - return Printer.repr(this); - } - } } diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Concatable.java b/src/main/java/com/google/devtools/build/lib/syntax/Concatable.java new file mode 100644 index 0000000000..81f489f7bf --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/syntax/Concatable.java @@ -0,0 +1,36 @@ +// 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.syntax; + +import com.google.devtools.build.lib.events.Location; +import javax.annotation.Nullable; + +/** + * Skylark values that support '+' operator should implement this interface. + */ +public interface Concatable { + + /** + * Implements 'plus' operator on ClassObjects. + */ + interface Concatter { + Concatable concat(Concatable lval, Concatable rval, Location loc) throws EvalException; + } + + /* Returns a concatter for this {@link Concatable}. + * Two {@link Concatable}s can be added together if their concatters are equal. + */ + @Nullable + Concatter getConcatter(); +} diff --git a/src/main/java/com/google/devtools/build/lib/syntax/EvalUtils.java b/src/main/java/com/google/devtools/build/lib/syntax/EvalUtils.java index 4a661896b6..386a9aefeb 100644 --- a/src/main/java/com/google/devtools/build/lib/syntax/EvalUtils.java +++ b/src/main/java/com/google/devtools/build/lib/syntax/EvalUtils.java @@ -26,12 +26,10 @@ import com.google.devtools.build.lib.skylarkinterface.SkylarkValue; import com.google.devtools.build.lib.syntax.compiler.ByteCodeUtils; import com.google.devtools.build.lib.util.Preconditions; import com.google.devtools.build.lib.vfs.PathFragment; - -import net.bytebuddy.implementation.bytecode.StackManipulation; - import java.util.Collection; import java.util.List; import java.util.Map; +import net.bytebuddy.implementation.bytecode.StackManipulation; /** * Utilities used by the evaluator. @@ -280,8 +278,6 @@ public final class EvalUtils { } else if (NestedSet.class.isAssignableFrom(c) || SkylarkNestedSet.class.isAssignableFrom(c)) { // TODO(bazel-team): no one should be seeing naked NestedSet at all. return "set"; - } else if (ClassObject.SkylarkClassObject.class.isAssignableFrom(c)) { - return "struct"; } else { if (c.getSimpleName().isEmpty()) { return c.getName(); diff --git a/src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java b/src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java index f103211379..4b39a7d1c6 100644 --- a/src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java +++ b/src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java @@ -29,11 +29,9 @@ import com.google.devtools.build.lib.skylarkinterface.Param; import com.google.devtools.build.lib.skylarkinterface.SkylarkModule; import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory; import com.google.devtools.build.lib.skylarkinterface.SkylarkSignature; -import com.google.devtools.build.lib.syntax.ClassObject.SkylarkClassObject; import com.google.devtools.build.lib.syntax.SkylarkList.MutableList; import com.google.devtools.build.lib.syntax.SkylarkList.Tuple; import com.google.devtools.build.lib.syntax.Type.ConversionException; - import java.util.ArrayList; import java.util.Comparator; import java.util.Iterator; @@ -1850,21 +1848,6 @@ public class MethodLibrary { } }; - @SkylarkSignature(name = "struct", returnType = SkylarkClassObject.class, doc = - "Creates an immutable struct using the keyword arguments as attributes. It is used to group " - + "multiple values together.Example:<br>" - + "<pre class=\"language-python\">s = struct(x = 2, y = 3)\n" - + "return s.x + getattr(s, \"y\") # returns 5</pre>", - extraKeywords = @Param(name = "kwargs", doc = "the struct attributes"), - useLocation = true) - private static final BuiltinFunction struct = new BuiltinFunction("struct") { - @SuppressWarnings("unchecked") - public SkylarkClassObject invoke(SkylarkDict<String, Object> kwargs, Location loc) - throws EvalException { - return new SkylarkClassObject(kwargs, loc); - } - }; - @SkylarkSignature( name = "set", returnType = SkylarkNestedSet.class, @@ -2345,7 +2328,7 @@ public class MethodLibrary { static final List<BaseFunction> skylarkGlobalFunctions = ImmutableList.<BaseFunction>builder() .addAll(buildGlobalFunctions) - .add(dir, fail, getattr, hasattr, hash, print, struct, type) + .add(dir, fail, getattr, hasattr, hash, print, type) .build(); /** diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkNestedSet.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkNestedSet.java index 0f8a3d31bc..2a64d7931d 100644 --- a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkNestedSet.java +++ b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkNestedSet.java @@ -24,13 +24,11 @@ import com.google.devtools.build.lib.skylarkinterface.SkylarkModule; import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory; import com.google.devtools.build.lib.skylarkinterface.SkylarkValue; import com.google.devtools.build.lib.util.Preconditions; - import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Set; - import javax.annotation.Nullable; /** A generic type safe NestedSet wrapper for Skylark. */ diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkType.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkType.java index f662ea13b4..e73672a93b 100644 --- a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkType.java +++ b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkType.java @@ -27,7 +27,6 @@ import com.google.devtools.build.lib.skylarkinterface.SkylarkValue; import com.google.devtools.build.lib.syntax.SkylarkList.MutableList; import com.google.devtools.build.lib.syntax.SkylarkList.Tuple; import com.google.devtools.build.lib.util.Preconditions; - import java.io.Serializable; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; @@ -37,7 +36,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; - import javax.annotation.Nullable; /** @@ -168,9 +166,6 @@ public abstract class SkylarkType implements Serializable { /** The BOOLEAN type, that contains TRUE and FALSE */ public static final Simple BOOL = Simple.of(Boolean.class); - /** The STRUCT type, for all Struct's */ - public static final Simple STRUCT = Simple.of(ClassObject.SkylarkClassObject.class); - /** The FUNCTION type, that contains all functions, otherwise dynamically typed at call-time */ public static final SkylarkFunctionType FUNCTION = new SkylarkFunctionType("unknown", TOP); |