blob: 04628352cd000b1da63b76c22623b50bf841ca16 [file] [log] [blame]
Googlercfd681f2019-11-11 07:24:02 -08001// Copyright 2014 The Bazel Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package com.google.devtools.build.lib.syntax;
16
17import com.google.common.collect.ImmutableList;
Googlerccdeeda2019-11-15 10:00:34 -080018import com.google.common.collect.Iterables;
19import com.google.common.collect.ObjectArrays;
Googlercfd681f2019-11-11 07:24:02 -080020import com.google.devtools.build.lib.events.Location;
21import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
22import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory;
Googlerccdeeda2019-11-15 10:00:34 -080023import java.util.AbstractCollection;
24import java.util.AbstractList;
25import java.util.Arrays;
26import java.util.Iterator;
Googlercfd681f2019-11-11 07:24:02 -080027import java.util.List;
28
29/**
30 * A Skylark tuple, i.e. the value represented by {@code (1, 2, 3)}. Tuples are always immutable
31 * (regardless of the {@link StarlarkThread} they are created in).
32 */
33@SkylarkModule(
34 name = "tuple",
35 category = SkylarkModuleCategory.BUILTIN,
36 doc =
37 "The built-in tuple type. Example tuple expressions:<br>"
38 + "<pre class=language-python>x = (1, 2, 3)</pre>"
39 + "Accessing elements is possible using indexing (starts from <code>0</code>):<br>"
40 + "<pre class=language-python>e = x[1] # e == 2</pre>"
41 + "Lists support the <code>+</code> operator to concatenate two tuples. Example:<br>"
42 + "<pre class=language-python>x = (1, 2) + (3, 4) # x == (1, 2, 3, 4)\n"
43 + "x = (\"a\", \"b\")\n"
44 + "x += (\"c\",) # x == (\"a\", \"b\", \"c\")</pre>"
45 + "Similar to lists, tuples support slice operations:"
46 + "<pre class=language-python>('a', 'b', 'c', 'd')[1:3] # ('b', 'c')\n"
47 + "('a', 'b', 'c', 'd')[::2] # ('a', 'c')\n"
48 + "('a', 'b', 'c', 'd')[3:0:-1] # ('d', 'c', 'b')</pre>"
49 + "Tuples are immutable, therefore <code>x[1] = \"a\"</code> is not supported.")
Googlerccdeeda2019-11-15 10:00:34 -080050public final class Tuple<E> extends AbstractList<E> implements Sequence<E> {
Googlercfd681f2019-11-11 07:24:02 -080051
Googlerccdeeda2019-11-15 10:00:34 -080052 private final Object[] elems;
Googlercfd681f2019-11-11 07:24:02 -080053
Googlerccdeeda2019-11-15 10:00:34 -080054 private Tuple(Object[] elems) {
55 this.elems = elems;
Googler1b80d572019-11-14 11:01:25 -080056 }
57
Googlerccdeeda2019-11-15 10:00:34 -080058 // The shared (sole) empty tuple.
59 private static final Tuple<?> EMPTY = new Tuple<>(new Object[0]);
Googler1b80d572019-11-14 11:01:25 -080060
61 /** Returns the empty tuple, cast to have an arbitrary content type. */
62 @SuppressWarnings("unchecked")
63 public static <T> Tuple<T> empty() {
Googlerccdeeda2019-11-15 10:00:34 -080064 return (Tuple<T>) EMPTY; // unchecked
Googler1b80d572019-11-14 11:01:25 -080065 }
66
67 /**
Googlerccdeeda2019-11-15 10:00:34 -080068 * Returns a Tuple that wraps the specified array, which must not be subsequently modified. The
69 * caller is additionally trusted to choose an appropriate type T.
Googler1b80d572019-11-14 11:01:25 -080070 */
Googlerccdeeda2019-11-15 10:00:34 -080071 static <T> Tuple<T> wrap(Object[] array) {
72 return array.length == 0 ? empty() : new Tuple<T>(array);
73 }
74
75 /** Returns a tuple containing the given elements. */
76 @SuppressWarnings("unchecked")
77 public static <T> Tuple<T> copyOf(Iterable<? extends T> seq) {
78 if (seq instanceof Tuple) {
79 return (Tuple<T>) seq; // unchecked
80 }
81 return wrap(Iterables.toArray(seq, Object.class));
82 }
83
84 /** Returns a tuple containing the given elements. */
85 public static <T> Tuple<T> of(T... elems) {
86 if (elems.length == 0) {
Googler1b80d572019-11-14 11:01:25 -080087 return empty();
Googlercfd681f2019-11-11 07:24:02 -080088 }
Googlerccdeeda2019-11-15 10:00:34 -080089 return new Tuple<T>(Arrays.copyOf(elems, elems.length));
Googler1b80d572019-11-14 11:01:25 -080090 }
Googlercfd681f2019-11-11 07:24:02 -080091
Googlerccdeeda2019-11-15 10:00:34 -080092 /** Returns a two-element tuple. */
93 public static <T> Tuple<T> pair(T a, T b) {
94 // Equivalent to of(a, b) but avoids variadic array allocation.
95 return wrap(new Object[] {a, b});
Googler1b80d572019-11-14 11:01:25 -080096 }
Googlercfd681f2019-11-11 07:24:02 -080097
Googlerccdeeda2019-11-15 10:00:34 -080098 /** Returns a three-element tuple. */
99 public static <T> Tuple<T> triple(T a, T b, T c) {
100 // Equivalent to of(a, b, c) but avoids variadic array allocation.
101 return wrap(new Object[] {a, b, c});
Googler1b80d572019-11-14 11:01:25 -0800102 }
Googlercfd681f2019-11-11 07:24:02 -0800103
Googlerccdeeda2019-11-15 10:00:34 -0800104 /** Returns a tuple that is the concatenation of two tuples. */
105 public static <T> Tuple<T> concat(Tuple<? extends T> x, Tuple<? extends T> y) {
106 // TODO(adonovan): opt: exploit x + () == x; y + () == y.
107 return wrap(ObjectArrays.concat(x.elems, y.elems, Object.class));
Googler1b80d572019-11-14 11:01:25 -0800108 }
109
110 @Override
111 public boolean isImmutable() {
Googlerccdeeda2019-11-15 10:00:34 -0800112 for (Object x : elems) {
113 if (!EvalUtils.isImmutable(x)) {
Googler1b80d572019-11-14 11:01:25 -0800114 return false;
Googlercfd681f2019-11-11 07:24:02 -0800115 }
Googlercfd681f2019-11-11 07:24:02 -0800116 }
Googler1b80d572019-11-14 11:01:25 -0800117 return true;
118 }
Googlercfd681f2019-11-11 07:24:02 -0800119
Googler1b80d572019-11-14 11:01:25 -0800120 @Override
121 public boolean isHashable() {
Googlerccdeeda2019-11-15 10:00:34 -0800122 for (Object x : elems) {
123 if (!EvalUtils.isHashable(x)) {
Googler1b80d572019-11-14 11:01:25 -0800124 return false;
Googlercfd681f2019-11-11 07:24:02 -0800125 }
Googler1b80d572019-11-14 11:01:25 -0800126 }
127 return true;
128 }
129
130 @Override
Googlerccdeeda2019-11-15 10:00:34 -0800131 public int hashCode() {
132 return 9857 + 8167 * Arrays.hashCode(elems);
Googler1b80d572019-11-14 11:01:25 -0800133 }
134
135 @Override
Googlerccdeeda2019-11-15 10:00:34 -0800136 public boolean equals(Object that) {
137 // This slightly violates the java.util.List equivalence contract
138 // because it considers the class, not just the elements.
139 return this == that
140 || (that instanceof Tuple && Arrays.equals(this.elems, ((Tuple) that).elems));
Googler1b80d572019-11-14 11:01:25 -0800141 }
142
Googlerccdeeda2019-11-15 10:00:34 -0800143 @Override
144 @SuppressWarnings("unchecked")
145 public E get(int i) {
146 return (E) elems[i]; // unchecked
147 }
148
149 @Override
150 public int size() {
151 return elems.length;
152 }
153
154 @Override
155 public Tuple<E> subList(int from, int to) {
156 return wrap(Arrays.copyOfRange(elems, from, to));
157 }
158
159 @Override
160 public Object[] toArray() {
161 return Arrays.copyOf(elems, elems.length);
162 }
163
164 @Override
Googler34f70582019-11-25 12:27:34 -0800165 public void repr(Printer printer) {
166 // TODO(adonovan): when Printer moves into this package,
Googlerccdeeda2019-11-15 10:00:34 -0800167 // inline and simplify this, the sole call with isTuple=true.
168 printer.printList(this, /*isTuple=*/ true);
169 }
170
Googler34f70582019-11-25 12:27:34 -0800171 // TODO(adonovan): StarlarkValue has 3 String methods yet still we need this fourth. Why?
Googlerccdeeda2019-11-15 10:00:34 -0800172 @Override
173 public String toString() {
Googlerb1e232d2019-11-22 15:29:43 -0800174 return Starlark.repr(this);
Googlerccdeeda2019-11-15 10:00:34 -0800175 }
176
177 @Override
178 public ImmutableList<E> getImmutableList() {
179 // Share the array with this (immutable) Tuple.
180 return wrapImmutable(elems);
181 }
182
183 /**
184 * Returns a new ImmutableList<T> backed by {@code array}, which must not be subsequently
185 * modified.
186 */
187 // TODO(adonovan): move this somewhere more appropriate.
188 static <T> ImmutableList<T> wrapImmutable(Object[] array) {
189 // Construct an ImmutableList that shares the array.
190 // ImmutableList relies on the implementation of Collection.toArray
191 // not subsequently modifying the returned array.
192 return ImmutableList.copyOf(
193 new AbstractCollection<T>() {
194 @Override
195 public Object[] toArray() {
196 return array;
197 }
198
199 @Override
200 public int size() {
201 return array.length;
202 }
203
204 @Override
205 public Iterator<T> iterator() {
206 throw new UnsupportedOperationException();
207 }
208 });
Googler1b80d572019-11-14 11:01:25 -0800209 }
210
211 @Override
212 public Tuple<E> getSlice(
213 Object start, Object end, Object step, Location loc, Mutability mutability)
214 throws EvalException {
Googlerccdeeda2019-11-15 10:00:34 -0800215 // TODO(adonovan): opt: this is horribly inefficient.
216 List<Integer> indices = EvalUtils.getSliceIndices(start, end, step, this.size(), loc);
217 Object[] res = new Object[indices.size()];
218 for (int i = 0; i < indices.size(); ++i) {
219 res[i] = elems[indices.get(i)];
Googler1b80d572019-11-14 11:01:25 -0800220 }
Googlerccdeeda2019-11-15 10:00:34 -0800221 return wrap(res);
Googler1b80d572019-11-14 11:01:25 -0800222 }
223
Googlerccdeeda2019-11-15 10:00:34 -0800224 /** Returns a Tuple containing n consecutive repeats of this tuple. */
225 public Tuple<E> repeat(int n, Mutability mutability) {
226 if (n <= 0 || isEmpty()) {
Googler1b80d572019-11-14 11:01:25 -0800227 return empty();
Googlercfd681f2019-11-11 07:24:02 -0800228 }
Googlerccdeeda2019-11-15 10:00:34 -0800229 // TODO(adonovan): reject unreasonably large n.
230 Object[] res = new Object[n * elems.length];
231 for (int i = 0; i < n; i++) {
232 System.arraycopy(elems, 0, res, i * elems.length, elems.length);
Googlercfd681f2019-11-11 07:24:02 -0800233 }
Googlerccdeeda2019-11-15 10:00:34 -0800234 return wrap(res);
Googler1b80d572019-11-14 11:01:25 -0800235 }
Googlercfd681f2019-11-11 07:24:02 -0800236}