diff options
author | 2016-08-05 08:38:26 +0000 | |
---|---|---|
committer | 2016-08-05 13:32:33 +0000 | |
commit | cdb6ef554397c1d3da7ee9921305f646bec5ae16 (patch) | |
tree | 23cde8c143ecf6496570bf56ace1c6f405acbd35 /src | |
parent | 6df3e71eb05493fa421121fa73e607a6fbf458c3 (diff) |
Make SkylarkClassObject "Bazel-specific".
This in preparation to DeclaredProviders implementation.
--
MOS_MIGRATED_REVID=129420617
Diffstat (limited to 'src')
27 files changed, 497 insertions, 416 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); 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 65d0c48ab0..07a8c6b232 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 @@ -22,6 +22,7 @@ import static org.junit.Assert.assertTrue; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.devtools.build.lib.cmdline.Label; @@ -33,15 +34,23 @@ import com.google.devtools.build.lib.packages.PredicateWithMessage; import com.google.devtools.build.lib.packages.RuleClass; import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType; import com.google.devtools.build.lib.packages.SkylarkAspect; +import com.google.devtools.build.lib.packages.SkylarkClassObject; import com.google.devtools.build.lib.rules.SkylarkAttr; import com.google.devtools.build.lib.rules.SkylarkFileType; import com.google.devtools.build.lib.rules.SkylarkRuleClassFunctions; import com.google.devtools.build.lib.rules.SkylarkRuleClassFunctions.RuleFunction; import com.google.devtools.build.lib.skylark.util.SkylarkTestCase; +import com.google.devtools.build.lib.syntax.ClassObject; +import com.google.devtools.build.lib.syntax.Environment; import com.google.devtools.build.lib.syntax.EvalException; +import com.google.devtools.build.lib.syntax.EvalUtils; +import com.google.devtools.build.lib.syntax.SkylarkDict; +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.SkylarkNestedSet; import com.google.devtools.build.lib.syntax.Type; import com.google.devtools.build.lib.util.FileTypeSet; - +import java.util.Collection; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -748,4 +757,213 @@ public class SkylarkRuleClassFunctionsTest extends SkylarkTestCase { // Should succeed without a "licenses attribute is potentially configurable" loading error: createRuleContext("//third_party/foo:r"); } + + @Test + public void testStructCreation() throws Exception { + // TODO(fwe): cannot be handled by current testing suite + eval("x = struct(a = 1, b = 2)"); + assertThat(lookup("x")).isInstanceOf(ClassObject.class); + } + + @Test + public void testStructFields() throws Exception { + // TODO(fwe): cannot be handled by current testing suite + eval("x = struct(a = 1, b = 2)"); + ClassObject x = (ClassObject) lookup("x"); + assertEquals(1, x.getValue("a")); + assertEquals(2, x.getValue("b")); + } + + @Test + public void testStructAccessingFieldsFromSkylark() throws Exception { + eval("x = struct(a = 1, b = 2)", "x1 = x.a", "x2 = x.b"); + assertThat(lookup("x1")).isEqualTo(1); + assertThat(lookup("x2")).isEqualTo(2); + } + + @Test + public void testStructAccessingUnknownField() throws Exception { + checkErrorContains( + "'struct' object has no attribute 'c'\n" + "Available attributes: a, b", + "x = struct(a = 1, b = 2)", + "y = x.c"); + } + + @Test + public void testStructAccessingUnknownFieldWithArgs() throws Exception { + checkErrorContains( + "struct has no method 'c'", "x = struct(a = 1, b = 2)", "y = x.c()"); + } + + @Test + public void testStructAccessingNonFunctionFieldWithArgs() throws Exception { + checkErrorContains( + "struct field 'a' is not a function", "x = struct(a = 1, b = 2)", "x1 = x.a(1)"); + } + + @Test + public void testStructAccessingFunctionFieldWithArgs() throws Exception { + eval("def f(x): return x+5", "x = struct(a = f, b = 2)", "x1 = x.a(1)"); + assertThat(lookup("x1")).isEqualTo(6); + } + + @Test + public void testStructPosArgs() throws Exception { + checkErrorContains( + "struct(**kwargs) does not accept positional arguments, but got 1", "x = struct(1, b = 2)"); + } + + @Test + public void testStructConcatenationFieldNames() throws Exception { + // TODO(fwe): cannot be handled by current testing suite + eval("x = struct(a = 1, b = 2)", + "y = struct(c = 1, d = 2)", + "z = x + y\n"); + SkylarkClassObject z = (SkylarkClassObject) lookup("z"); + assertEquals(ImmutableSet.of("a", "b", "c", "d"), z.getKeys()); + } + + @Test + public void testStructConcatenationFieldValues() throws Exception { + // TODO(fwe): cannot be handled by current testing suite + eval("x = struct(a = 1, b = 2)", + "y = struct(c = 1, d = 2)", + "z = x + y\n"); + SkylarkClassObject z = (SkylarkClassObject) lookup("z"); + assertEquals(1, z.getValue("a")); + assertEquals(2, z.getValue("b")); + assertEquals(1, z.getValue("c")); + assertEquals(2, z.getValue("d")); + } + + @Test + public void testStructConcatenationCommonFields() throws Exception { + checkErrorContains("Cannot concat structs with common field(s): a", + "x = struct(a = 1, b = 2)", "y = struct(c = 1, a = 2)", "z = x + y\n"); + } + + @Test + public void testConditionalStructConcatenation() throws Exception { + // TODO(fwe): cannot be handled by current testing suite + eval("def func():", + " x = struct(a = 1, b = 2)", + " if True:", + " x += struct(c = 1, d = 2)", + " return x", + "x = func()"); + SkylarkClassObject x = (SkylarkClassObject) lookup("x"); + assertEquals(1, x.getValue("a")); + assertEquals(2, x.getValue("b")); + assertEquals(1, x.getValue("c")); + assertEquals(2, x.getValue("d")); + } + + @Test + public void testGetattrNoAttr() throws Exception { + checkErrorContains("Object of type 'struct' has no attribute \"b\"", + "s = struct(a='val')", "getattr(s, 'b')"); + } + + @Test + public void testGetattr() throws Exception { + eval( + "s = struct(a='val')", + "x = getattr(s, 'a')", + "y = getattr(s, 'b', 'def')", + "z = getattr(s, 'b', default = 'def')", + "w = getattr(s, 'a', default='ignored')"); + assertThat(lookup("x")).isEqualTo("val"); + assertThat(lookup("y")).isEqualTo("def"); + assertThat(lookup("z")).isEqualTo("def"); + assertThat(lookup("w")).isEqualTo("val"); + } + + @Test + public void testHasattr() throws Exception { + eval("s = struct(a=1)", + "x = hasattr(s, 'a')", + "y = hasattr(s, 'b')\n"); + assertThat(lookup("x")).isEqualTo(true); + assertThat(lookup("y")).isEqualTo(false); + } + + @Test + public void testStructStr() throws Exception { + assertThat(eval("str(struct(x = 2, y = 3, z = 4))")) + .isEqualTo("struct(x = 2, y = 3, z = 4)"); + } + + @Test + public void testStructsInSets() throws Exception { + eval("set([struct(a='a')])"); + } + + @Test + public void testStructMembersAreImmutable() throws Exception { + checkErrorContains( + "can only assign to variables and tuples, not to 's.x'", + "s = struct(x = 'a')", + "s.x = 'b'\n"); + } + + @Test + public void testStructDictMembersAreImmutable() throws Exception { + checkErrorContains( + "can only assign to variables and tuples, not to 's.x['b']'", + "s = struct(x = {'a' : 1})", + "s.x['b'] = 2\n"); + } + + @Test + public void testNsetGoodCompositeItem() throws Exception { + eval("def func():", + " return set([struct(a='a')])", + "s = func()"); + Collection<Object> result = ((SkylarkNestedSet) lookup("s")).toCollection(); + assertThat(result).hasSize(1); + assertThat(result.iterator().next()).isInstanceOf(SkylarkClassObject.class); + } + + @Test + public void testNsetBadMutableItem() throws Exception { + checkEvalError("sets cannot contain mutable items", "set([([],)])"); + checkEvalError("sets cannot contain mutable items", "set([struct(a=[])])"); + } + + private static SkylarkClassObject makeStruct(String field, Object value) { + return new SkylarkClassObject(ImmutableMap.of(field, value)); + } + + private static SkylarkClassObject makeBigStruct(Environment env) { + // struct(a=[struct(x={1:1}), ()], b=(), c={2:2}) + return new SkylarkClassObject(ImmutableMap.<String, Object>of( + "a", MutableList.<Object>of(env, + new SkylarkClassObject(ImmutableMap.<String, Object>of( + "x", SkylarkDict.<Object, Object>of(env, 1, 1))), + Tuple.of()), + "b", Tuple.of(), + "c", SkylarkDict.<Object, Object>of(env, 2, 2))); + } + + @Test + public void testStructMutabilityShallow() throws Exception { + assertTrue(EvalUtils.isImmutable(makeStruct("a", 1))); + } + + private static MutableList<Object> makeList(Environment env) { + return MutableList.<Object>of(env, 1, 2, 3); + } + + + @Test + public void testStructMutabilityDeep() throws Exception { + assertTrue(EvalUtils.isImmutable(Tuple.<Object>of(makeList(null)))); + assertTrue(EvalUtils.isImmutable(makeStruct("a", makeList(null)))); + assertTrue(EvalUtils.isImmutable(makeBigStruct(null))); + + assertFalse(EvalUtils.isImmutable(Tuple.<Object>of(makeList(ev.getEnvironment())))); + assertFalse(EvalUtils.isImmutable(makeStruct("a", makeList(ev.getEnvironment())))); + assertFalse(EvalUtils.isImmutable(makeBigStruct(ev.getEnvironment()))); + } + } diff --git a/src/test/java/com/google/devtools/build/lib/skylark/util/SkylarkTestCase.java b/src/test/java/com/google/devtools/build/lib/skylark/util/SkylarkTestCase.java index cf17c9dbb3..5efd8a8209 100644 --- a/src/test/java/com/google/devtools/build/lib/skylark/util/SkylarkTestCase.java +++ b/src/test/java/com/google/devtools/build/lib/skylark/util/SkylarkTestCase.java @@ -149,6 +149,7 @@ public abstract class SkylarkTestCase extends BuildViewTestCase { } protected void checkErrorContains(String errorMsg, String... lines) throws Exception { + ev.setFailFast(false); try { eval(lines); fail("checkErrorContains(String, String...): There was no error"); 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 26763ea796..7ce40aa90c 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 @@ -19,8 +19,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import com.google.common.collect.ImmutableMap; -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.util.EvaluationTestCase; @@ -44,21 +42,6 @@ public class EvalUtilsTest extends EvaluationTestCase { return SkylarkDict.<Object, Object>of(env, 1, 1, 2, 2); } - private static SkylarkClassObject makeStruct(String field, Object value) { - return new SkylarkClassObject(ImmutableMap.of(field, value)); - } - - private static SkylarkClassObject makeBigStruct(Environment env) { - // struct(a=[struct(x={1:1}), ()], b=(), c={2:2}) - return new SkylarkClassObject(ImmutableMap.<String, Object>of( - "a", MutableList.<Object>of(env, - new SkylarkClassObject(ImmutableMap.<String, Object>of( - "x", SkylarkDict.<Object, Object>of(env, 1, 1))), - Tuple.of()), - "b", Tuple.of(), - "c", SkylarkDict.<Object, Object>of(env, 2, 2))); - } - @Test public void testEmptyStringToIterable() throws Exception { assertThat(EvalUtils.toIterable("", null)).isEmpty(); @@ -88,7 +71,6 @@ public class EvalUtilsTest extends EvaluationTestCase { @Test public void testDatatypeMutabilityShallow() throws Exception { assertTrue(EvalUtils.isImmutable(Tuple.of(1, 2, 3))); - assertTrue(EvalUtils.isImmutable(makeStruct("a", 1))); // Mutability depends on the environment. assertTrue(EvalUtils.isImmutable(makeList(null))); @@ -100,12 +82,8 @@ public class EvalUtilsTest extends EvaluationTestCase { @Test public void testDatatypeMutabilityDeep() throws Exception { assertTrue(EvalUtils.isImmutable(Tuple.<Object>of(makeList(null)))); - assertTrue(EvalUtils.isImmutable(makeStruct("a", makeList(null)))); - assertTrue(EvalUtils.isImmutable(makeBigStruct(null))); assertFalse(EvalUtils.isImmutable(Tuple.<Object>of(makeList(env)))); - assertFalse(EvalUtils.isImmutable(makeStruct("a", makeList(env)))); - assertFalse(EvalUtils.isImmutable(makeBigStruct(env))); } @Test diff --git a/src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java b/src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java index 1eaecb7e46..5f994dbd1c 100644 --- a/src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java +++ b/src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java @@ -22,7 +22,6 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.devtools.build.lib.syntax.SkylarkList.MutableList; import com.google.devtools.build.lib.syntax.util.EvaluationTestCase; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -1661,9 +1660,6 @@ public class MethodLibraryTest extends EvaluationTestCase { .testStatement("str(False)", "False") .testStatement("str(None)", "None") .testStatement("str(str)", "<function str>"); - - new SkylarkTest() - .testStatement("str(struct(x = 2, y = 3, z = 4))", "struct(x = 2, y = 3, z = 4)"); } @Test diff --git a/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java b/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java index 2b6ebfc884..235968f7f2 100644 --- a/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java +++ b/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java @@ -20,7 +20,6 @@ import static org.junit.Assert.assertTrue; import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.actions.Artifact.SpecialArtifact; import com.google.devtools.build.lib.analysis.FileConfiguredTarget; @@ -30,7 +29,6 @@ import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.skylarkinterface.Param; import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable; import com.google.devtools.build.lib.skylarkinterface.SkylarkModule; -import com.google.devtools.build.lib.syntax.ClassObject.SkylarkClassObject; import com.google.devtools.build.lib.syntax.SkylarkList.MutableList; import com.google.devtools.build.lib.testutil.TestMode; import org.junit.Before; @@ -712,22 +710,6 @@ public class SkylarkEvaluationTest extends EvaluationTest { } @Test - public void testConditionalStructConcatenation() throws Exception { - // TODO(fwe): cannot be handled by current testing suite - eval("def func():", - " x = struct(a = 1, b = 2)", - " if True:", - " x += struct(c = 1, d = 2)", - " return x", - "x = func()"); - SkylarkClassObject x = (SkylarkClassObject) lookup("x"); - assertEquals(1, x.getValue("a")); - assertEquals(2, x.getValue("b")); - assertEquals(1, x.getValue("c")); - assertEquals(2, x.getValue("d")); - } - - @Test public void testJavaFunctionReturnsMutableObject() throws Exception { new SkylarkTest() .update("mock", new Mock()) @@ -825,93 +807,6 @@ public class SkylarkEvaluationTest extends EvaluationTest { } @Test - public void testStructCreation() throws Exception { - // TODO(fwe): cannot be handled by current testing suite - eval("x = struct(a = 1, b = 2)"); - assertThat(lookup("x")).isInstanceOf(ClassObject.class); - } - - @Test - public void testStructFields() throws Exception { - // TODO(fwe): cannot be handled by current testing suite - eval("x = struct(a = 1, b = 2)"); - ClassObject x = (ClassObject) lookup("x"); - assertEquals(1, x.getValue("a")); - assertEquals(2, x.getValue("b")); - } - - @Test - public void testStructAccessingFieldsFromSkylark() throws Exception { - new SkylarkTest() - .setUp("x = struct(a = 1, b = 2)", "x1 = x.a", "x2 = x.b") - .testLookup("x1", 1) - .testLookup("x2", 2); - } - - @Test - public void testStructAccessingUnknownField() throws Exception { - new SkylarkTest() - .testIfErrorContains( - "'struct' object has no attribute 'c'\n" + "Available attributes: a, b", - "x = struct(a = 1, b = 2)", - "y = x.c"); - } - - @Test - public void testStructAccessingUnknownFieldWithArgs() throws Exception { - new SkylarkTest().testIfExactError( - "struct has no method 'c'", "x = struct(a = 1, b = 2)", "y = x.c()"); - } - - @Test - public void testStructAccessingNonFunctionFieldWithArgs() throws Exception { - new SkylarkTest().testIfExactError( - "struct field 'a' is not a function", "x = struct(a = 1, b = 2)", "x1 = x.a(1)"); - } - - @Test - public void testStructAccessingFunctionFieldWithArgs() throws Exception { - new SkylarkTest() - .setUp("def f(x): return x+5", "x = struct(a = f, b = 2)", "x1 = x.a(1)") - .testLookup("x1", 6); - } - - @Test - public void testStructPosArgs() throws Exception { - new SkylarkTest().testIfExactError( - "struct(**kwargs) does not accept positional arguments, but got 1", "x = struct(1, b = 2)"); - } - - @Test - public void testStructConcatenationFieldNames() throws Exception { - // TODO(fwe): cannot be handled by current testing suite - eval("x = struct(a = 1, b = 2)", - "y = struct(c = 1, d = 2)", - "z = x + y\n"); - SkylarkClassObject z = (SkylarkClassObject) lookup("z"); - assertEquals(ImmutableSet.of("a", "b", "c", "d"), z.getKeys()); - } - - @Test - public void testStructConcatenationFieldValues() throws Exception { - // TODO(fwe): cannot be handled by current testing suite - eval("x = struct(a = 1, b = 2)", - "y = struct(c = 1, d = 2)", - "z = x + y\n"); - SkylarkClassObject z = (SkylarkClassObject) lookup("z"); - assertEquals(1, z.getValue("a")); - assertEquals(2, z.getValue("b")); - assertEquals(1, z.getValue("c")); - assertEquals(2, z.getValue("d")); - } - - @Test - public void testStructConcatenationCommonFields() throws Exception { - new SkylarkTest().testIfExactError("Cannot concat structs with common field(s): a", - "x = struct(a = 1, b = 2)", "y = struct(c = 1, a = 2)", "z = x + y\n"); - } - - @Test public void testDotExpressionOnNonStructObject() throws Exception { new SkylarkTest().testIfExactError("Object of type 'string' has no field \"field\"", "x = 'a'.field"); @@ -1026,15 +921,6 @@ public class SkylarkEvaluationTest extends EvaluationTest { } @Test - public void testHasattr() throws Exception { - new SkylarkTest().setUp("s = struct(a=1)", - "x = hasattr(s, 'a')", - "y = hasattr(s, 'b')\n") - .testLookup("x", Boolean.TRUE) - .testLookup("y", Boolean.FALSE); - } - - @Test public void testHasattrMethods() throws Exception { new SkylarkTest() .update("mock", new Mock()) @@ -1049,23 +935,6 @@ public class SkylarkEvaluationTest extends EvaluationTest { } @Test - public void testGetattr() throws Exception { - new SkylarkTest() - .setUp("s = struct(a='val')", "x = getattr(s, 'a')", "y = getattr(s, 'b', 'def')", - "z = getattr(s, 'b', default = 'def')", "w = getattr(s, 'a', default='ignored')") - .testLookup("x", "val") - .testLookup("y", "def") - .testLookup("z", "def") - .testLookup("w", "val"); - } - - @Test - public void testGetattrNoAttr() throws Exception { - new SkylarkTest().testIfExactError("Object of type 'struct' has no attribute \"b\"", - "s = struct(a='val')", "getattr(s, 'b')"); - } - - @Test public void testListAnTupleConcatenationDoesNotWorkInSkylark() throws Exception { new SkylarkTest().testIfExactError("unsupported operand type(s) for +: 'list' and 'tuple'", "[1, 2] + (3, 4)"); diff --git a/src/test/java/com/google/devtools/build/lib/syntax/SkylarkNestedSetTest.java b/src/test/java/com/google/devtools/build/lib/syntax/SkylarkNestedSetTest.java index 5c6535e8c9..a0d84206e8 100644 --- a/src/test/java/com/google/devtools/build/lib/syntax/SkylarkNestedSetTest.java +++ b/src/test/java/com/google/devtools/build/lib/syntax/SkylarkNestedSetTest.java @@ -18,19 +18,15 @@ import static org.junit.Assert.assertEquals; import com.google.common.collect.ImmutableList; import com.google.devtools.build.lib.collect.nestedset.Order; -import com.google.devtools.build.lib.syntax.ClassObject.SkylarkClassObject; import com.google.devtools.build.lib.syntax.SkylarkList.Tuple; import com.google.devtools.build.lib.syntax.util.EvaluationTestCase; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - import java.util.Arrays; -import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Tests for SkylarkNestedSet. @@ -156,22 +152,6 @@ public class SkylarkNestedSetTest extends EvaluationTestCase { } @Test - public void testNsetGoodCompositeItem() throws Exception { - eval("def func():", - " return set([struct(a='a')])", - "s = func()"); - Collection<Object> result = get("s").toCollection(); - assertThat(result).hasSize(1); - assertThat(result.iterator().next()).isInstanceOf(SkylarkClassObject.class); - } - - @Test - public void testNsetBadMutableItem() throws Exception { - checkEvalError("sets cannot contain mutable items", "set([([],)])"); - checkEvalError("sets cannot contain mutable items", "set([struct(a=[])])"); - } - - @Test public void testNsetToString() throws Exception { eval("s = set() + [2, 4, 6] + [3, 4, 5]", "x = str(s)"); diff --git a/src/test/java/com/google/devtools/build/lib/syntax/ValidationTest.java b/src/test/java/com/google/devtools/build/lib/syntax/ValidationTest.java index c44e5c918b..b7bd6f6306 100644 --- a/src/test/java/com/google/devtools/build/lib/syntax/ValidationTest.java +++ b/src/test/java/com/google/devtools/build/lib/syntax/ValidationTest.java @@ -18,11 +18,11 @@ import static com.google.common.truth.Truth.assertThat; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.analysis.RuleConfiguredTarget; import com.google.devtools.build.lib.events.Event; +import com.google.devtools.build.lib.packages.SkylarkClassObject; import com.google.devtools.build.lib.skylarkinterface.SkylarkModule; 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.util.EvaluationTestCase; - import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -112,22 +112,6 @@ public class ValidationTest extends EvaluationTestCase { } @Test - public void testStructMembersAreImmutable() { - checkError( - "can only assign to variables and tuples, not to 's.x'", - "s = struct(x = 'a')", - "s.x = 'b'\n"); - } - - @Test - public void testStructDictMembersAreImmutable() { - checkError( - "can only assign to variables and tuples, not to 's.x['b']'", - "s = struct(x = {'a' : 1})", - "s.x['b'] = 2\n"); - } - - @Test public void testTupleLiteralWorksForDifferentTypes() throws Exception { parse("('a', 1)"); } @@ -299,11 +283,11 @@ public class ValidationTest extends EvaluationTestCase { // TODO(bazel-team): fix that? assertThat(ClassObject.class.isAnnotationPresent(SkylarkModule.class)).isFalse(); - assertThat(ClassObject.SkylarkClassObject.class.isAnnotationPresent(SkylarkModule.class)) + assertThat(SkylarkClassObject.class.isAnnotationPresent(SkylarkModule.class)) .isTrue(); assertThat( - EvalUtils.getParentWithSkylarkModule(ClassObject.SkylarkClassObject.class) - == ClassObject.SkylarkClassObject.class) + EvalUtils.getParentWithSkylarkModule(SkylarkClassObject.class) + == SkylarkClassObject.class) .isTrue(); assertThat(EvalUtils.getParentWithSkylarkModule(ClassObject.class)).isNull(); } @@ -320,8 +304,6 @@ public class ValidationTest extends EvaluationTestCase { assertThat(SkylarkType.of(tupleClass)).isEqualTo(SkylarkType.TUPLE); assertThat(SkylarkType.TUPLE).isNotEqualTo(SkylarkType.LIST); - // Also for ClassObject - assertThat(SkylarkType.of(ClassObject.SkylarkClassObject.class)).isEqualTo(SkylarkType.STRUCT); try { SkylarkType.of(ClassObject.class); throw new Exception("foo"); @@ -335,7 +317,7 @@ public class ValidationTest extends EvaluationTestCase { // TODO(bazel-team): move to some other place to remove dependency of syntax tests on Artifact? assertThat(SkylarkType.of(Artifact.SpecialArtifact.class)) .isEqualTo(SkylarkType.of(Artifact.class)); - assertThat(SkylarkType.of(RuleConfiguredTarget.class)).isNotEqualTo(SkylarkType.STRUCT); + assertThat(SkylarkType.of(RuleConfiguredTarget.class)).isNotEqualTo(SkylarkType.of(SkylarkClassObject.class)); } @Test @@ -348,9 +330,8 @@ public class ValidationTest extends EvaluationTestCase { assertThat(SkylarkType.LIST.includes(combo1)).isTrue(); SkylarkType union1 = - SkylarkType.Union.of(SkylarkType.DICT, SkylarkType.LIST, SkylarkType.STRUCT); + SkylarkType.Union.of(SkylarkType.DICT, SkylarkType.LIST); assertThat(union1.includes(SkylarkType.DICT)).isTrue(); - assertThat(union1.includes(SkylarkType.STRUCT)).isTrue(); assertThat(union1.includes(combo1)).isTrue(); assertThat(union1.includes(SkylarkType.STRING)).isFalse(); |