aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com')
-rw-r--r--src/main/java/com/google/devtools/build/lib/cmdline/Label.java16
-rw-r--r--src/main/java/com/google/devtools/build/lib/query2/output/OutputFormatter.java42
-rw-r--r--src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkPrintableValue.java26
-rw-r--r--src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkValue.java34
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/BinaryOperatorExpression.java14
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/FormatParser.java6
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java3
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java25
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/Printer.java571
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/SkylarkSemanticsOptions.java15
10 files changed, 434 insertions, 318 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/cmdline/Label.java b/src/main/java/com/google/devtools/build/lib/cmdline/Label.java
index 9c229018b0..247ea4d98a 100644
--- a/src/main/java/com/google/devtools/build/lib/cmdline/Label.java
+++ b/src/main/java/com/google/devtools/build/lib/cmdline/Label.java
@@ -23,8 +23,8 @@ import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
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.skylarkinterface.SkylarkPrintableValue;
import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter;
+import com.google.devtools.build.lib.skylarkinterface.SkylarkValue;
import com.google.devtools.build.lib.util.Preconditions;
import com.google.devtools.build.lib.util.StringCanonicalizer;
import com.google.devtools.build.lib.util.StringUtilities;
@@ -51,7 +51,7 @@ import javax.annotation.Nullable;
)
@Immutable
@ThreadSafe
-public final class Label implements Comparable<Label>, Serializable, SkylarkPrintableValue, SkyKey {
+public final class Label implements Comparable<Label>, Serializable, SkylarkValue, SkyKey {
public static final PathFragment EXTERNAL_PACKAGE_NAME = PathFragment.create("external");
public static final PathFragment EXTERNAL_PACKAGE_FILE_NAME = PathFragment.create("WORKSPACE");
public static final String DEFAULT_REPOSITORY_DIRECTORY = "__main__";
@@ -562,8 +562,20 @@ public final class Label implements Comparable<Label>, Serializable, SkylarkPrin
}
@Override
+ public void reprLegacy(SkylarkPrinter printer) {
+ printer.repr(getCanonicalForm());
+ }
+
+ @Override
public void repr(SkylarkPrinter printer) {
+ printer.append("Label(");
printer.repr(getCanonicalForm());
+ printer.append(")");
+ }
+
+ @Override
+ public void strLegacy(SkylarkPrinter printer) {
+ printer.append(getCanonicalForm());
}
@Override
diff --git a/src/main/java/com/google/devtools/build/lib/query2/output/OutputFormatter.java b/src/main/java/com/google/devtools/build/lib/query2/output/OutputFormatter.java
index 29cfb47c57..acc5a54644 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/output/OutputFormatter.java
+++ b/src/main/java/com/google/devtools/build/lib/query2/output/OutputFormatter.java
@@ -466,7 +466,8 @@ public abstract class OutputFormatter implements Serializable {
if (attributeMap.isConfigurable(attr.getName())) {
// We don't know the actual value for configurable attributes, so we reconstruct
// the select without trying to resolve it.
- printStream.printf(outputAttributePattern,
+ printStream.printf(
+ outputAttributePattern,
attr.getPublicName(),
outputConfigurableAttrValue(rule, attributeMap, attr));
continue;
@@ -479,20 +480,17 @@ public abstract class OutputFormatter implements Serializable {
// Computed defaults that depend on configurable attributes can have multiple values.
continue;
}
- printStream.printf(outputAttributePattern,
+ printStream.printf(
+ outputAttributePattern,
attr.getPublicName(),
outputAttrValue(Iterables.getOnlyElement(values)));
}
printStream.printf(")\n%s", lineTerm);
}
- /**
- * Returns the given attribute value with BUILD output syntax. Does not support selects.
- */
+ /** Returns the given attribute value with BUILD output syntax. Does not support selects. */
private String outputAttrValue(Object value) {
- if (value instanceof Label) {
- value = ((Label) value).getDefaultCanonicalForm();
- } else if (value instanceof License) {
+ if (value instanceof License) {
List<String> licenseTypes = new ArrayList<>();
for (License.LicenseType licenseType : ((License) value).getLicenseTypes()) {
licenseTypes.add(licenseType.toString().toLowerCase());
@@ -504,7 +502,7 @@ public abstract class OutputFormatter implements Serializable {
} else if (value instanceof TriState) {
value = ((TriState) value).toInt();
}
- return Printer.repr(value);
+ return new LabelPrinter().repr(value).toString();
}
/**
@@ -513,14 +511,16 @@ public abstract class OutputFormatter implements Serializable {
* <p>Since query doesn't know which select path should be chosen, this doesn't try to
* resolve the final value. Instead it just reconstructs the select.
*/
- private String outputConfigurableAttrValue(Rule rule, RawAttributeMapper attributeMap,
- Attribute attr) {
+ private String outputConfigurableAttrValue(
+ Rule rule, RawAttributeMapper attributeMap, Attribute attr) {
List<String> selectors = new ArrayList<>();
- for (BuildType.Selector<?> selector : ((BuildType.SelectorList<?>)
- attributeMap.getRawAttributeValue(rule, attr)).getSelectors()) {
+ for (BuildType.Selector<?> selector :
+ ((BuildType.SelectorList<?>) attributeMap.getRawAttributeValue(rule, attr))
+ .getSelectors()) {
if (selector.isUnconditional()) {
- selectors.add(outputAttrValue(
- Iterables.getOnlyElement(selector.getEntries().entrySet()).getValue()));
+ selectors.add(
+ outputAttrValue(
+ Iterables.getOnlyElement(selector.getEntries().entrySet()).getValue()));
} else {
selectors.add(String.format("select(%s)", outputAttrValue(selector.getEntries())));
}
@@ -834,4 +834,16 @@ public abstract class OutputFormatter implements Serializable {
target.getPackage().getNameFragment())
: location.print();
}
+
+ private static class LabelPrinter extends Printer.BasePrinter {
+ @Override
+ public LabelPrinter repr(Object o) {
+ if (o instanceof Label) {
+ writeString(((Label) o).getCanonicalForm());
+ } else {
+ super.repr(o);
+ }
+ return this;
+ }
+ }
}
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkPrintableValue.java b/src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkPrintableValue.java
deleted file mode 100644
index 69426ea6c8..0000000000
--- a/src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkPrintableValue.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2015 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.skylarkinterface;
-
-/**
- * A Skylark value that is represented by {@code str()} differently than by {@code repr()}.
- */
-public interface SkylarkPrintableValue extends SkylarkValue {
- /**
- * Print an informal, human-readable representation of the value.
- *
- * @param printer a printer to be used for formatting nested values.
- */
- void str(SkylarkPrinter printer);
-}
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkValue.java b/src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkValue.java
index a7c8c2779b..bf6a5c5586 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkValue.java
+++ b/src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkValue.java
@@ -37,4 +37,38 @@ public interface SkylarkValue {
* @param printer a printer to be used for formatting nested values.
*/
void repr(SkylarkPrinter printer);
+
+ /**
+ * Print a legacy representation of object x.
+ *
+ * <p>By default dispatches to the {@code repr} method. Should be called instead of {@code repr}
+ * if --incompatible_descriptive_string_representations=false is used.
+ *
+ * @param printer an instance of a printer to be used for formatting nested values
+ */
+ default void reprLegacy(SkylarkPrinter printer) {
+ repr(printer);
+ }
+
+ /**
+ * Print an informal, human-readable representation of the value.
+ *
+ * <p>By default dispatches to the {@code repr} method.
+ *
+ * @param printer a printer to be used for formatting nested values.
+ */
+ default void str(SkylarkPrinter printer) {
+ repr(printer);
+ }
+
+ /**
+ * Print a legacy informal, human-readable representation of the value.
+ *
+ * <p>By default dispatches to the {@code reprLegacy} method.
+ *
+ * @param printer a printer to be used for formatting nested values.
+ */
+ default void strLegacy(SkylarkPrinter printer) {
+ reprLegacy(printer);
+ }
}
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 06e46dbbdb..6022768bee 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
@@ -84,7 +84,7 @@ public final class BinaryOperatorExpression extends Expression {
}
/** Implements the "in" operator. */
- private static boolean in(Object lval, Object rval, Location location, Environment env)
+ private static boolean in(Object lval, Object rval, Environment env, Location location)
throws EvalException {
if (env.getSemantics().incompatibleDepsetIsNotIterable && rval instanceof SkylarkNestedSet) {
throw new EvalException(
@@ -163,7 +163,7 @@ public final class BinaryOperatorExpression extends Expression {
return divide(lval, rval, location);
case PERCENT:
- return percent(lval, rval, location);
+ return percent(lval, rval, env, location);
case EQUALS_EQUALS:
return lval.equals(rval);
@@ -184,10 +184,10 @@ public final class BinaryOperatorExpression extends Expression {
return compare(lval, rval, location) >= 0;
case IN:
- return in(lval, rval, location, env);
+ return in(lval, rval, env, location);
case NOT_IN:
- return !in(lval, rval, location, env);
+ return !in(lval, rval, env, location);
default:
throw new AssertionError("Unsupported binary operator: " + operator);
@@ -352,7 +352,7 @@ public final class BinaryOperatorExpression extends Expression {
}
/** Implements Operator.PERCENT. */
- private static Object percent(Object lval, Object rval, Location location)
+ private static Object percent(Object lval, Object rval, Environment env, Location location)
throws EvalException {
// int % int
if (lval instanceof Integer && rval instanceof Integer) {
@@ -376,9 +376,9 @@ public final class BinaryOperatorExpression extends Expression {
String pattern = (String) lval;
try {
if (rval instanceof Tuple) {
- return Printer.formatWithList(pattern, (Tuple) rval);
+ return Printer.getPrinter(env).formatWithList(pattern, (Tuple) rval).toString();
}
- return Printer.format(pattern, rval);
+ return Printer.getPrinter(env).format(pattern, rval).toString();
} catch (IllegalFormatException e) {
throw new EvalException(location, e.getMessage());
}
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/FormatParser.java b/src/main/java/com/google/devtools/build/lib/syntax/FormatParser.java
index d4a2323994..bc3338add6 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/FormatParser.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/FormatParser.java
@@ -30,9 +30,11 @@ public final class FormatParser {
private static final ImmutableSet<Character> ILLEGAL_IN_FIELD =
ImmutableSet.of('.', '[', ']', ',');
+ private final Environment environment;
private final Location location;
- public FormatParser(Location location) {
+ public FormatParser(Environment environment, Location location) {
+ this.environment = environment;
this.location = location;
}
@@ -91,7 +93,7 @@ public final class FormatParser {
History history,
StringBuilder output)
throws EvalException {
- BasePrinter printer = Printer.getPrinter(output);
+ BasePrinter printer = Printer.getPrinter(environment, output);
if (has(chars, pos + 1, '{')) {
// Escaped brace -> output and move to char after right brace
printer.append("{");
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java b/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java
index 6caf43fc2f..3ab4e4d71a 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java
@@ -29,7 +29,6 @@ import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
import com.google.devtools.build.lib.skylarkinterface.SkylarkInterfaceUtils;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
import com.google.devtools.build.lib.syntax.EvalException.EvalExceptionWithJavaCause;
-import com.google.devtools.build.lib.syntax.Printer.BasePrinter;
import com.google.devtools.build.lib.syntax.Runtime.NoneType;
import com.google.devtools.build.lib.util.Pair;
import com.google.devtools.build.lib.util.Preconditions;
@@ -271,7 +270,7 @@ public final class FuncallExpression extends Expression {
@Override
public String toString() {
- BasePrinter printer = Printer.getPrinter();
+ Printer.LengthLimitedPrinter printer = new Printer.LengthLimitedPrinter();
if (obj != null) {
printer.append(obj.toString()).append(".");
}
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 5c5905b9f4..e549699287 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
@@ -963,7 +963,7 @@ public class MethodLibrary {
Location loc,
Environment env)
throws EvalException {
- return new FormatParser(loc)
+ return new FormatParser(env, loc)
.format(
self,
args.getImmutableList(),
@@ -1619,12 +1619,13 @@ public class MethodLibrary {
doc =
"Converts any object to string. This is useful for debugging."
+ "<pre class=\"language-python\">str(\"ab\") == \"ab\"</pre>",
- parameters = {@Param(name = "x", doc = "The object to convert.")}
+ parameters = {@Param(name = "x", doc = "The object to convert.")},
+ useEnvironment = true
)
private static final BuiltinFunction str =
new BuiltinFunction("str") {
- public String invoke(Object x) {
- return Printer.getPrinter().str(x).toString();
+ public String invoke(Object x, Environment env) {
+ return Printer.getPrinter(env).str(x).toString();
}
};
@@ -1634,12 +1635,13 @@ public class MethodLibrary {
doc =
"Converts any object to a string representation. This is useful for debugging.<br>"
+ "<pre class=\"language-python\">str(\"ab\") == \\\"ab\\\"</pre>",
- parameters = {@Param(name = "x", doc = "The object to convert.")}
+ parameters = {@Param(name = "x", doc = "The object to convert.")},
+ useEnvironment = true
)
private static final BuiltinFunction repr =
new BuiltinFunction("repr") {
- public String invoke(Object x) {
- return Printer.getPrinter().repr(x).toString();
+ public String invoke(Object x, Environment env) {
+ return Printer.getPrinter(env).repr(x).toString();
}
};
@@ -2104,11 +2106,14 @@ public class MethodLibrary {
public Runtime.NoneType invoke(
String sep, SkylarkList<?> starargs, Location loc, Environment env)
throws EvalException {
- String msg = starargs.stream().map(Printer::str).collect(joining(sep));
+ String msg =
+ starargs
+ .stream()
+ .map((Object o) -> Printer.getPrinter(env).str(o).toString())
+ .collect(joining(sep));
// As part of the integration test "skylark_flag_test.sh", if the
// "--internal_skylark_flag_test_canary" flag is enabled, append an extra marker string to
- // the
- // output.
+ // the output.
if (env.getSemantics().skylarkFlagTestCanary) {
msg += "<== skylark flag test ==>";
}
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Printer.java b/src/main/java/com/google/devtools/build/lib/syntax/Printer.java
index 0a581777d8..12cc7929f4 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/Printer.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/Printer.java
@@ -17,7 +17,6 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.events.Location;
-import com.google.devtools.build.lib.skylarkinterface.SkylarkPrintableValue;
import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter;
import com.google.devtools.build.lib.skylarkinterface.SkylarkValue;
import com.google.devtools.build.lib.syntax.SkylarkList.Tuple;
@@ -51,22 +50,53 @@ public class Printer {
public static final int SUGGESTED_CRITICAL_LIST_ELEMENTS_STRING_LENGTH = 32;
/**
- * Creates an instance of BasePrinter that wraps an existing buffer.
- * @param buffer an Appendable
- * @return new BasePrinter
+ * Creates an instance of {@link BasePrinter} that wraps an existing buffer.
+ *
+ * @param buffer an {@link Appendable}
+ * @return new {@link BasePrinter}
*/
- public static BasePrinter getPrinter(Appendable buffer) {
+ static BasePrinter getPrinter(Appendable buffer) {
return new BasePrinter(buffer);
}
/**
- * Creates an instance of BasePrinter with an empty buffer.
- * @return new BasePrinter
+ * Creates an instance of {@link BasePrinter} with an empty buffer.
+ *
+ * @return new {@link BasePrinter}
*/
public static BasePrinter getPrinter() {
return getPrinter(new StringBuilder());
}
+ /**
+ * Creates an instance of BasePrinter with a given buffer.
+ *
+ * @param env {@link Environment}
+ * @param buffer an {@link Appendable}
+ * @return new BasePrinter
+ */
+ static BasePrinter getPrinter(Environment env, Appendable buffer) {
+ if (env.getSemantics().incompatibleDescriptiveStringRepresentations) {
+ return new BasePrinter(buffer);
+ } else {
+ return new LegacyPrinter(buffer);
+ }
+ }
+
+ /**
+ * Creates an instance of BasePrinter with an empty buffer.
+ *
+ * @param env {@link Environment}
+ * @return new BasePrinter
+ */
+ static BasePrinter getPrinter(Environment env) {
+ if (env.getSemantics().incompatibleDescriptiveStringRepresentations) {
+ return new BasePrinter();
+ } else {
+ return new LegacyPrinter();
+ }
+ }
+
private Printer() {}
// These static methods proxy to the similar methods of BasePrinter
@@ -111,7 +141,7 @@ public class Printer {
@Nullable String singletonTerminator,
int maxItemsToPrint,
int criticalItemsStringLength) {
- return getPrinter()
+ return new LengthLimitedPrinter()
.printAbbreviatedList(
list,
before,
@@ -164,7 +194,7 @@ public class Printer {
boolean isTuple,
int maxItemsToPrint,
int criticalItemsStringLength) {
- return getPrinter()
+ return new LengthLimitedPrinter()
.printAbbreviatedList(list, isTuple, maxItemsToPrint, criticalItemsStringLength)
.toString();
}
@@ -256,158 +286,26 @@ public class Printer {
}
}
- /**
- * Helper class for {@code Appendable}s that want to limit the length of their input.
- *
- * <p>Instances of this class act as a proxy for one {@code Appendable} object and decide whether
- * the given input (or parts of it) can be written to the underlying {@code Appendable}, depending
- * on whether the specified maximum length has been met or not.
- */
- private static final class LengthLimitedAppendable implements Appendable {
-
- private static final ImmutableSet<Character> SPECIAL_CHARS =
- ImmutableSet.of(',', ' ', '"', '\'', ':', '(', ')', '[', ']', '{', '}');
-
- private static final Pattern ARGS_PATTERN = Pattern.compile("<\\d+ more arguments>");
-
- private final Appendable original;
- private int limit;
- private boolean ignoreLimit;
- private boolean previouslyShortened;
-
- private LengthLimitedAppendable(Appendable original, int limit) {
- this.original = original;
- this.limit = limit;
- }
-
- private static LengthLimitedAppendable create(Appendable original, int limit) {
- // We don't want to overwrite the limit if original is already an instance of this class.
- return (original instanceof LengthLimitedAppendable)
- ? (LengthLimitedAppendable) original : new LengthLimitedAppendable(original, limit);
- }
-
- @Override
- public Appendable append(CharSequence csq) throws IOException {
- if (ignoreLimit || hasOnlySpecialChars(csq)) {
- // Don't update limit.
- original.append(csq);
- previouslyShortened = false;
- } else {
- int length = csq.length();
- if (length <= limit) {
- limit -= length;
- original.append(csq);
- } else {
- original.append(csq, 0, limit);
- // We don't want to append multiple ellipses.
- if (!previouslyShortened) {
- original.append("...");
- }
- appendTrailingSpecialChars(csq, limit);
- previouslyShortened = true;
- limit = 0;
- }
- }
- return this;
- }
-
- /**
- * Appends any trailing "special characters" (e.g. brackets, quotation marks) in the given
- * sequence to the output buffer, regardless of the limit.
- *
- * <p>For example, let's look at foo(['too long']). Without this method, the shortened result
- * would be foo(['too...) instead of the prettier foo(['too...']).
- *
- * <p>If the input string was already shortened and contains "<x more arguments>", this part
- * will also be appended.
- */
- // TODO(bazel-team): Given an input list
- //
- // [1, 2, 3, [10, 20, 30, 40, 50, 60], 4, 5, 6]
- //
- // the inner list gets doubly mangled as
- //
- // [1, 2, 3, [10, 20, 30, 40, <2 more argu...<2 more arguments>], <3 more arguments>]
- private void appendTrailingSpecialChars(CharSequence csq, int limit) throws IOException {
- int length = csq.length();
- Matcher matcher = ARGS_PATTERN.matcher(csq);
- // We assume that everything following the "x more arguments" part has to be copied, too.
- int start = matcher.find() ? matcher.start() : length;
- // Find the left-most non-arg char that has to be copied.
- for (int i = start - 1; i > limit; --i) {
- if (isSpecialChar(csq.charAt(i))) {
- start = i;
- } else {
- break;
- }
- }
- if (start < length) {
- original.append(csq, start, csq.length());
- }
- }
-
- /**
- * Returns whether the given sequence denotes characters that are not part of the value of an
- * argument.
- *
- * <p>Examples are brackets, braces and quotation marks.
- */
- private boolean hasOnlySpecialChars(CharSequence csq) {
- for (int i = 0; i < csq.length(); ++i) {
- if (!isSpecialChar(csq.charAt(i))) {
- return false;
- }
- }
- return true;
- }
-
- private boolean isSpecialChar(char c) {
- return SPECIAL_CHARS.contains(c);
- }
-
- @Override
- public Appendable append(CharSequence csq, int start, int end) throws IOException {
- return this.append(csq.subSequence(start, end));
- }
-
- @Override
- public Appendable append(char c) throws IOException {
- return this.append(String.valueOf(c));
- }
-
- public boolean hasHitLimit() {
- return limit <= 0;
- }
-
- public void enforceLimit() {
- ignoreLimit = false;
- }
-
- public void ignoreLimit() {
- ignoreLimit = true;
- }
-
- @Override
- public String toString() {
- return original.toString();
- }
- }
-
/** Actual class that implements Printer API */
- public static final class BasePrinter implements SkylarkPrinter {
+ public static class BasePrinter implements SkylarkPrinter {
// Methods of this class should not recurse through static methods of Printer
- private final Appendable buffer;
+ protected final Appendable buffer;
/**
* Creates a printer instance.
*
- * @param buffer the Appendable to which to print the representation
+ * @param buffer the {@link Appendable} to which to print the representation
*/
- private BasePrinter(Appendable buffer) {
+ protected BasePrinter(Appendable buffer) {
this.buffer = buffer;
}
+ /** Creates a printer instance with a new StringBuilder. */
+ protected BasePrinter() {
+ this.buffer = new StringBuilder();
+ }
+
@Override
public String toString() {
return buffer.toString();
@@ -421,8 +319,8 @@ public class Printer {
* @return the buffer, in fluent style
*/
public BasePrinter str(Object o) {
- if (o instanceof SkylarkPrintableValue) {
- ((SkylarkPrintableValue) o).str(this);
+ if (o instanceof SkylarkValue) {
+ ((SkylarkValue) o).str(this);
return this;
}
@@ -496,9 +394,9 @@ public class Printer {
* Write a properly escaped Skylark representation of a string to a buffer.
*
* @param s the string a representation of which to repr.
- * @return the Appendable, in fluent style.
+ * @return this printer.
*/
- private BasePrinter writeString(String s) {
+ protected BasePrinter writeString(String s) {
this.append(SKYLARK_QUOTATION_MARK);
int len = s.length();
for (int i = 0; i < len; i++) {
@@ -544,7 +442,7 @@ public class Printer {
* @param singletonTerminator null or a string to print after the list if it is a singleton The
* singleton case is notably relied upon in python syntax to distinguish a tuple of size one
* such as ("foo",) from a merely parenthesized object such as ("foo").
- * @return the BasePrinter.
+ * @return this printer.
*/
@Override
public BasePrinter printList(
@@ -553,48 +451,9 @@ public class Printer {
String separator,
String after,
@Nullable String singletonTerminator) {
- return printAbbreviatedList(list, before, separator, after, singletonTerminator, -1, -1);
- }
- /**
- * Print a list of object representations.
- *
- * <p>The length of the output will be limited when both {@code maxItemsToPrint} and {@code
- * criticalItemsStringLength} have values greater than zero.
- *
- * @param list the list of objects to repr (each as with repr)
- * @param before a string to print before the list
- * @param separator a separator to print between each object
- * @param after a string to print after the list
- * @param singletonTerminator null or a string to print after the list if it is a singleton The
- * singleton case is notably relied upon in python syntax to distinguish a tuple of size one
- * such as ("foo",) from a merely parenthesized object such as ("foo").
- * @param maxItemsToPrint the maximum number of elements to be printed.
- * @param criticalItemsStringLength a soft limit for the total string length of all arguments.
- * 'Soft' means that this limit may be exceeded because of formatting.
- * @return the BasePrinter.
- */
- public BasePrinter printAbbreviatedList(
- Iterable<?> list,
- String before,
- String separator,
- String after,
- @Nullable String singletonTerminator,
- int maxItemsToPrint,
- int criticalItemsStringLength) {
this.append(before);
- int len = 0;
- // Limits the total length of the string representation of the elements, if specified.
- if (maxItemsToPrint > 0 && criticalItemsStringLength > 0) {
- len =
- appendListElements(
- LengthLimitedAppendable.create(buffer, criticalItemsStringLength),
- list,
- separator,
- maxItemsToPrint);
- } else {
- len = appendListElements(list, separator);
- }
+ int len = appendListElements(list, separator);
if (singletonTerminator != null && len == 1) {
this.append(singletonTerminator);
}
@@ -620,72 +479,23 @@ public class Printer {
}
/**
- * Tries to append the given elements to the specified {@link Appendable} until specific limits
- * are reached.
- *
- * @return the number of appended elements.
- */
- private int appendListElements(
- LengthLimitedAppendable appendable,
- Iterable<?> list,
- String separator,
- int maxItemsToPrint) {
- boolean printSeparator = false; // don't print the separator before the first element
- boolean skipArgs = false;
- int items = Iterables.size(list);
- int len = 0;
- // We don't want to print "1 more arguments", hence we don't skip arguments if there is only
- // one above the limit.
- int itemsToPrint = (items - maxItemsToPrint == 1) ? items : maxItemsToPrint;
- appendable.enforceLimit();
- for (Object o : list) {
- // We don't want to print "1 more arguments", even if we hit the string limit.
- if (len == itemsToPrint || (appendable.hasHitLimit() && len < items - 1)) {
- skipArgs = true;
- break;
- }
- if (printSeparator) {
- this.append(separator);
- }
- Printer.getPrinter(appendable).repr(o);
- printSeparator = true;
- len++;
- }
- appendable.ignoreLimit();
- if (skipArgs) {
- this.append(separator);
- this.append(String.format("<%d more arguments>", items - len));
- }
- return len;
- }
-
- /**
* Print a Skylark list or tuple of object representations
*
* @param list the contents of the list or tuple
* @param isTuple if true the list will be formatted with parentheses and with a trailing comma
- * in case of one-element tuples.
- * @param maxItemsToPrint the maximum number of elements to be printed.
- * @param criticalItemsStringLength a soft limit for the total string length of all arguments.
- * 'Soft' means that this limit may be exceeded because of formatting.
- * @return the Appendable, in fluent style.
+ * in case of one-element tuples. 'Soft' means that this limit may be exceeded because of
+ * formatting.
+ * @return this printer.
*/
- public BasePrinter printAbbreviatedList(
- Iterable<?> list, boolean isTuple, int maxItemsToPrint, int criticalItemsStringLength) {
+ @Override
+ public BasePrinter printList(Iterable<?> list, boolean isTuple) {
if (isTuple) {
- return this.printAbbreviatedList(list, "(", ", ", ")", ",",
- maxItemsToPrint, criticalItemsStringLength);
+ return this.printList(list, "(", ", ", ")", ",");
} else {
- return this.printAbbreviatedList(list, "[", ", ", "]", null,
- maxItemsToPrint, criticalItemsStringLength);
+ return this.printList(list, "[", ", ", "]", null);
}
}
- @Override
- public BasePrinter printList(Iterable<?> list, boolean isTuple) {
- return this.printAbbreviatedList(list, isTuple, -1, -1);
- }
-
/**
* Perform Python-style string formatting, as per pattern % tuple Limitations: only %d %s %r %%
* are supported.
@@ -743,9 +553,9 @@ public class Printer {
if (a >= argLength) {
throw new MissingFormatWidthException(
"not enough arguments for format pattern "
- + this.repr(pattern)
+ + Printer.repr(pattern)
+ ": "
- + this.repr(Tuple.copyOf(arguments)));
+ + Printer.repr(Tuple.copyOf(arguments)));
}
Object argument = arguments.get(a++);
switch (directive) {
@@ -755,7 +565,7 @@ public class Printer {
continue;
} else {
throw new MissingFormatWidthException(
- "invalid argument " + this.repr(argument) + " for format pattern %d");
+ "invalid argument " + Printer.repr(argument) + " for format pattern %d");
}
case 'r':
this.repr(argument);
@@ -791,5 +601,258 @@ public class Printer {
Printer.append(buffer, s);
return this;
}
+
+ BasePrinter append(CharSequence sequence, int start, int end) {
+ return this.append(sequence.subSequence(start, end));
+ }
+ }
+
+ /** A version of BasePrinter that renders object in old style for compatibility reasons. */
+ static final class LegacyPrinter extends BasePrinter {
+ protected LegacyPrinter() {
+ super();
+ }
+
+ protected LegacyPrinter(Appendable buffer) {
+ super(buffer);
+ }
+
+ @Override
+ public LegacyPrinter repr(Object o) {
+ if (o instanceof SkylarkValue) {
+ ((SkylarkValue) o).reprLegacy(this);
+ } else {
+ super.repr(o);
+ }
+ return this;
+ }
+
+ @Override
+ public LegacyPrinter str(Object o) {
+ if (o instanceof SkylarkValue) {
+ ((SkylarkValue) o).strLegacy(this);
+ } else {
+ super.str(o);
+ }
+ return this;
+ }
+ }
+
+ /** A version of {@code BasePrinter} that is able to print abbreviated lists. */
+ public static final class LengthLimitedPrinter extends BasePrinter {
+
+ private static final ImmutableSet<Character> SPECIAL_CHARS =
+ ImmutableSet.of(',', ' ', '"', '\'', ':', '(', ')', '[', ']', '{', '}');
+
+ private static final Pattern ARGS_PATTERN = Pattern.compile("<\\d+ more arguments>");
+
+ // Limits can be set several times recursively and then unset the same amount of times.
+ // But in fact they should be set only the first time and unset only the last time.
+ // To achieve that we need to keep track of the recursion depth.
+ private int recursionDepth;
+ // Current limit of symbols to print in the limited mode (`ignoreLimit = false`).
+ private int limit;
+ private boolean ignoreLimit = true;
+ private boolean previouslyShortened;
+
+ /**
+ * Print a list of object representations.
+ *
+ * <p>The length of the output will be limited when both {@code maxItemsToPrint} and {@code
+ * criticalItemsStringLength} have values greater than zero.
+ *
+ * @param list the list of objects to repr (each as with repr)
+ * @param before a string to print before the list
+ * @param separator a separator to print between each object
+ * @param after a string to print after the list
+ * @param singletonTerminator null or a string to print after the list if it is a singleton The
+ * singleton case is notably relied upon in python syntax to distinguish a tuple of size one
+ * such as ("foo",) from a merely parenthesized object such as ("foo").
+ * @param maxItemsToPrint the maximum number of elements to be printed.
+ * @param criticalItemsStringLength a soft limit for the total string length of all arguments.
+ * 'Soft' means that this limit may be exceeded because of formatting.
+ * @return the BasePrinter.
+ */
+ LengthLimitedPrinter printAbbreviatedList(
+ Iterable<?> list,
+ String before,
+ String separator,
+ String after,
+ @Nullable String singletonTerminator,
+ int maxItemsToPrint,
+ int criticalItemsStringLength) {
+ this.append(before);
+ int len = appendListElements(list, separator, maxItemsToPrint, criticalItemsStringLength);
+ if (singletonTerminator != null && len == 1) {
+ this.append(singletonTerminator);
+ }
+ return this.append(after);
+ }
+
+ /**
+ * Print a Skylark list or tuple of object representations
+ *
+ * @param list the contents of the list or tuple
+ * @param isTuple if true the list will be formatted with parentheses and with a trailing comma
+ * in case of one-element tuples.
+ * @param maxItemsToPrint the maximum number of elements to be printed.
+ * @param criticalItemsStringLength a soft limit for the total string length of all arguments.
+ * 'Soft' means that this limit may be exceeded because of formatting.
+ * @return this printer.
+ */
+ public LengthLimitedPrinter printAbbreviatedList(
+ Iterable<?> list, boolean isTuple, int maxItemsToPrint, int criticalItemsStringLength) {
+ if (isTuple) {
+ return this.printAbbreviatedList(
+ list, "(", ", ", ")", ",", maxItemsToPrint, criticalItemsStringLength);
+ } else {
+ return this.printAbbreviatedList(
+ list, "[", ", ", "]", null, maxItemsToPrint, criticalItemsStringLength);
+ }
+ }
+
+ /**
+ * Tries to append the given elements to the specified {@link Appendable} until specific limits
+ * are reached.
+ *
+ * @return the number of appended elements.
+ */
+ private int appendListElements(
+ Iterable<?> list, String separator, int maxItemsToPrint, int criticalItemsStringLength) {
+ boolean printSeparator = false; // don't print the separator before the first element
+ boolean skipArgs = false;
+ int items = Iterables.size(list);
+ int len = 0;
+ // We don't want to print "1 more arguments", hence we don't skip arguments if there is only
+ // one above the limit.
+ int itemsToPrint = (items - maxItemsToPrint == 1) ? items : maxItemsToPrint;
+ enforceLimit(criticalItemsStringLength);
+ for (Object o : list) {
+ // We don't want to print "1 more arguments", even if we hit the string limit.
+ if (len == itemsToPrint || (hasHitLimit() && len < items - 1)) {
+ skipArgs = true;
+ break;
+ }
+ if (printSeparator) {
+ this.append(separator);
+ }
+ this.repr(o);
+ printSeparator = true;
+ len++;
+ }
+ ignoreLimit();
+ if (skipArgs) {
+ this.append(separator);
+ this.append(String.format("<%d more arguments>", items - len));
+ }
+ return len;
+ }
+
+ @Override
+ public LengthLimitedPrinter append(CharSequence csq) {
+ if (ignoreLimit || hasOnlySpecialChars(csq)) {
+ // Don't update limit.
+ Printer.append(buffer, csq);
+ previouslyShortened = false;
+ } else {
+ int length = csq.length();
+ if (length <= limit) {
+ limit -= length;
+ Printer.append(buffer, csq);
+ } else {
+ Printer.append(buffer, csq, 0, limit);
+ // We don't want to append multiple ellipses.
+ if (!previouslyShortened) {
+ Printer.append(buffer, "...");
+ }
+ appendTrailingSpecialChars(csq, limit);
+ previouslyShortened = true;
+ limit = 0;
+ }
+ }
+ return this;
+ }
+
+ @Override
+ public LengthLimitedPrinter append(char c) {
+ // Use the local `append(sequence)` method so that limits can apply
+ return this.append(String.valueOf(c));
+ }
+
+ /**
+ * Appends any trailing "special characters" (e.g. brackets, quotation marks) in the given
+ * sequence to the output buffer, regardless of the limit.
+ *
+ * <p>For example, let's look at foo(['too long']). Without this method, the shortened result
+ * would be foo(['too...) instead of the prettier foo(['too...']).
+ *
+ * <p>If the input string was already shortened and contains "<x more arguments>", this part
+ * will also be appended.
+ */
+ // TODO(bazel-team): Given an input list
+ //
+ // [1, 2, 3, [10, 20, 30, 40, 50, 60], 4, 5, 6]
+ //
+ // the inner list gets doubly mangled as
+ //
+ // [1, 2, 3, [10, 20, 30, 40, <2 more argu...<2 more arguments>], <3 more arguments>]
+ private LengthLimitedPrinter appendTrailingSpecialChars(CharSequence csq, int limit) {
+ int length = csq.length();
+ Matcher matcher = ARGS_PATTERN.matcher(csq);
+ // We assume that everything following the "x more arguments" part has to be copied, too.
+ int start = matcher.find() ? matcher.start() : length;
+ // Find the left-most non-arg char that has to be copied.
+ for (int i = start - 1; i > limit; --i) {
+ if (isSpecialChar(csq.charAt(i))) {
+ start = i;
+ } else {
+ break;
+ }
+ }
+ if (start < length) {
+ Printer.append(buffer, csq, start, csq.length());
+ }
+ return this;
+ }
+
+ /**
+ * Returns whether the given sequence denotes characters that are not part of the value of an
+ * argument.
+ *
+ * <p>Examples are brackets, braces and quotation marks.
+ */
+ private boolean hasOnlySpecialChars(CharSequence csq) {
+ for (int i = 0; i < csq.length(); ++i) {
+ if (!isSpecialChar(csq.charAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean isSpecialChar(char c) {
+ return SPECIAL_CHARS.contains(c);
+ }
+
+ boolean hasHitLimit() {
+ return limit <= 0;
+ }
+
+ private void enforceLimit(int limit) {
+ ignoreLimit = false;
+ if (recursionDepth == 0) {
+ this.limit = limit;
+ ++recursionDepth;
+ }
+ }
+
+ private void ignoreLimit() {
+ if (recursionDepth > 0) {
+ --recursionDepth;
+ }
+ if (recursionDepth == 0) {
+ ignoreLimit = true;
+ }
+ }
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSemanticsOptions.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSemanticsOptions.java
index 097f581ffb..d77fda83e1 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSemanticsOptions.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSemanticsOptions.java
@@ -195,4 +195,19 @@ public class SkylarkSemanticsOptions extends OptionsBase implements Serializable
help = "If set to true, arithmetic operations throw an error in case of overflow/underflow."
)
public boolean incompatibleCheckedArithmetic;
+
+ @Option(
+ name = "incompatible_descriptive_string_representations",
+ defaultValue = "false",
+ category = "incompatible changes",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ metadataTags = {OptionMetadataTag.INCOMPATIBLE_CHANGE},
+ optionUsageRestrictions = OptionUsageRestrictions.UNDOCUMENTED,
+ help =
+ "If set to true, objects are converted to strings by `str` and `repr` functions using the "
+ + "new style representations that are designed to be more descriptive and not to leak "
+ + "information that's not supposed to be exposed."
+ )
+ public boolean incompatibleDescriptiveStringRepresentations;
}