blob: 08c9a2a1cf7b486bc963cc59c2d59f3c2ec96169 [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2015 The Bazel Authors. All rights reserved.
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +00002//
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
tomlua155b532017-11-08 20:12:47 +010017import com.google.common.base.Preconditions;
brandjonba23ae22018-01-16 08:21:37 -080018import com.google.common.collect.ImmutableList;
brandjonb80eb182018-05-01 10:54:58 -070019import com.google.common.collect.ImmutableMap;
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +000020import com.google.common.collect.ImmutableSet;
21import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
cparsonsfcea9de2018-04-16 12:58:03 -070022import com.google.devtools.build.lib.skylarkinterface.SkylarkGlobalLibrary;
cparsons981f65b2018-04-23 12:04:09 -070023import com.google.devtools.build.lib.skylarkinterface.SkylarkInterfaceUtils;
John Field585d1a02015-12-16 16:03:52 +000024import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
vladmos46907932017-06-30 14:01:45 +020025import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter;
John Field585d1a02015-12-16 16:03:52 +000026import com.google.devtools.build.lib.skylarkinterface.SkylarkSignature;
27import com.google.devtools.build.lib.skylarkinterface.SkylarkValue;
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +000028import java.lang.reflect.Field;
29import java.util.HashMap;
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +000030import java.util.Map;
brandjondc2c5502017-12-07 14:30:04 -080031import java.util.TreeMap;
32import javax.annotation.Nullable;
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +000033
34/**
brandjondc2c5502017-12-07 14:30:04 -080035 * Global constants and support for static registration of builtin symbols.
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +000036 */
brandjondc2c5502017-12-07 14:30:04 -080037// TODO(bazel-team): Rename to SkylarkRuntime to avoid conflict with java.lang.Runtime.
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +000038public final class Runtime {
39
40 private Runtime() {}
41
42 @SkylarkSignature(name = "True", returnType = Boolean.class,
43 doc = "Literal for the boolean true.")
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000044 private static final Boolean TRUE = true;
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +000045
46 @SkylarkSignature(name = "False", returnType = Boolean.class,
47 doc = "Literal for the boolean false.")
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000048 private static final Boolean FALSE = false;
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +000049
John Caterd7928422017-01-03 20:06:59 +000050 /** There should be only one instance of this type to allow "== None" tests. */
51 @SkylarkModule(
52 name = "NoneType",
53 documented = false,
54 doc = "Unit type, containing the unique value None."
55 )
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +000056 @Immutable
Francois-Rene Rideau6c10eac2015-09-17 19:17:20 +000057 public static final class NoneType implements SkylarkValue {
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +000058 private NoneType() {}
Francois-Rene Rideau6c10eac2015-09-17 19:17:20 +000059
60 @Override
61 public String toString() {
62 return "None";
63 }
64
65 @Override
66 public boolean isImmutable() {
67 return true;
68 }
69
70 @Override
vladmos46907932017-06-30 14:01:45 +020071 public void repr(SkylarkPrinter printer) {
72 printer.append("None");
Francois-Rene Rideau6c10eac2015-09-17 19:17:20 +000073 }
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +000074 }
75
Francois-Rene Rideau432d7152016-02-18 16:33:03 +000076 /** Marker for unbound variables in cases where neither Java null nor Skylark None is suitable. */
77 @Immutable
78 public static final class UnboundMarker implements SkylarkValue {
79 private UnboundMarker() {}
80
81 @Override
82 public String toString() {
83 return "<unbound>";
84 }
85
86 @Override
87 public boolean isImmutable() {
88 return true;
89 }
90
91 @Override
vladmos46907932017-06-30 14:01:45 +020092 public void repr(SkylarkPrinter printer) {
93 printer.append("<unbound>");
Francois-Rene Rideau432d7152016-02-18 16:33:03 +000094 }
95 }
96
97 @SkylarkSignature(name = "<unbound>", returnType = UnboundMarker.class, documented = false,
98 doc = "Marker for unbound values in cases where neither Skylark None nor Java null can do.")
99 public static final UnboundMarker UNBOUND = new UnboundMarker();
100
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +0000101 @SkylarkSignature(name = "None", returnType = NoneType.class,
102 doc = "Literal for the None value.")
103 public static final NoneType NONE = new NoneType();
104
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +0000105 @SkylarkSignature(name = "PACKAGE_NAME", returnType = String.class,
laurentlb286f1e92017-07-19 14:53:15 +0200106 doc = "<b>Deprecated. Use <a href=\"native.html#package_name\">package_name()</a> "
107 + "instead.</b> The name of the package being evaluated. "
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +0000108 + "For example, in the BUILD file <code>some/package/BUILD</code>, its value "
109 + "will be <code>some/package</code>. "
Laurent Le Brun053966c2017-02-14 22:02:04 +0000110 + "If the BUILD file calls a function defined in a .bzl file, PACKAGE_NAME will "
111 + "match the caller BUILD file package. "
112 + "In .bzl files, do not access PACKAGE_NAME at the file-level (outside of functions), "
113 + "either directly or by calling a function at the file-level that accesses "
114 + "PACKAGE_NAME (PACKAGE_NAME is only defined during BUILD file evaluation)."
115 + "Here is an example of a .bzl file:<br>"
116 + "<pre class=language-python>"
117 + "# a = PACKAGE_NAME # not allowed outside functions\n"
118 + "def extension():\n"
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +0000119 + " return PACKAGE_NAME</pre>"
Laurent Le Brun053966c2017-02-14 22:02:04 +0000120 + "In this case, <code>extension()</code> can be called from a BUILD file (even "
121 + "indirectly), but not in a file-level expression in the .bzl file. "
Laurent Le Brun66a2d3a2017-03-08 19:19:28 +0000122 + "When implementing a rule, use <a href=\"ctx.html#label\">ctx.label</a> to know where "
Laurent Le Brun053966c2017-02-14 22:02:04 +0000123 + "the rule comes from. ")
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +0000124 public static final String PKG_NAME = "PACKAGE_NAME";
125
Rodrigo Queiro07b9c6f2017-12-05 01:10:53 -0800126 @SkylarkSignature(
Sam Rawlins71d0f6c2018-06-07 07:25:49 -0700127 name = "REPOSITORY_NAME",
128 returnType = String.class,
129 doc =
130 "<b>Deprecated. Use <a href=\"native.html#repository_name\">repository_name()</a> "
131 + "instead.</b> The name of the repository the rule or build extension is called "
132 + "from. "
133 + "For example, in packages that are called into existence by the WORKSPACE stanza "
134 + "<code>local_repository(name='local', path=...)</code> it will be set to "
135 + "<code>@local</code>. In packages in the main repository, it will be set to "
136 + "<code>@</code>. It can only be accessed in functions (transitively) called from "
137 + "BUILD files, i.e. it follows the same restrictions as "
138 + "<a href=\"#PACKAGE_NAME\">PACKAGE_NAME</a>.")
Lukacs Berki5d9b8a02015-10-21 12:36:56 +0000139 public static final String REPOSITORY_NAME = "REPOSITORY_NAME";
140
brandjonb80eb182018-05-01 10:54:58 -0700141 /** Adds bindings for False/True/None constants to the given map builder. */
142 public static void addConstantsToBuilder(ImmutableMap.Builder<String, Object> builder) {
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +0000143 // In Python 2.x, True and False are global values and can be redefined by the user.
brandjonb80eb182018-05-01 10:54:58 -0700144 // In Python 3.x, they are keywords. We implement them as values. Currently they can't be
145 // redefined because builtins can't be overridden. In the future we should permit shadowing of
146 // most builtins but still prevent shadowing of these constants.
147 builder
148 .put("False", FALSE)
149 .put("True", TRUE)
150 .put("None", NONE);
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +0000151 }
152
153
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +0000154 /**
brandjondc2c5502017-12-07 14:30:04 -0800155 * Returns the canonical class representing the namespace associated with the given class, i.e.,
156 * the class under which builtins should be registered.
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +0000157 */
brandjondc2c5502017-12-07 14:30:04 -0800158 public static Class<?> getSkylarkNamespace(Class<?> clazz) {
159 return String.class.isAssignableFrom(clazz)
cparsons0fcad772018-04-11 14:24:00 -0700160 ? StringModule.class
brandjondc2c5502017-12-07 14:30:04 -0800161 : EvalUtils.getSkylarkType(clazz);
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +0000162 }
163
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +0000164 /**
brandjondc2c5502017-12-07 14:30:04 -0800165 * A registry of builtins, including both global builtins and builtins that are under some
166 * namespace.
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +0000167 *
brandjon73f1a0a2018-03-06 14:19:47 -0800168 * <p>Concurrency model: This object is thread-safe. Read accesses are always allowed, while write
169 * accesses are only allowed before this object has been frozen ({@link #freeze}). Prior to
170 * freezing, all operations are synchronized, while after freezing they are lockless.
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +0000171 */
brandjondc2c5502017-12-07 14:30:04 -0800172 public static class BuiltinRegistry {
173
174 /**
brandjon73f1a0a2018-03-06 14:19:47 -0800175 * Whether the registry's construction has completed.
176 *
177 * <p>Mutating methods may only be called while this is still false. Accessor methods may be
178 * called at any time.
179 *
180 * <p>We use {@code volatile} rather than {@link AtomicBoolean} because the bit flip only
181 * happens once, and doesn't require correlated reads and writes.
182 */
183 private volatile boolean frozen = false;
184
185 /**
brandjondc2c5502017-12-07 14:30:04 -0800186 * All registered builtins, keyed and sorted by an identifying (but otherwise unimportant)
187 * string.
188 *
189 * <p>The string is typically formed from the builtin's simple name and the Java class in which
190 * it is defined. The Java class need not correspond to a namespace. (This map includes global
191 * builtins that have no namespace.)
192 */
193 private final Map<String, Object> allBuiltins = new TreeMap<>();
194
195 /** All non-global builtin functions, keyed by their namespace class and their name. */
196 private final Map<Class<?>, Map<String, BaseFunction>> functions = new HashMap<>();
197
brandjon73f1a0a2018-03-06 14:19:47 -0800198 /**
199 * Marks the registry as initialized, if it wasn't already.
200 *
201 * <p>It is guaranteed that after this method returns, all accessor methods are safe without
202 * synchronization; i.e. no mutation operation can touch the data structures.
203 */
204 public void freeze() {
205 // Similar to double-checked locking, but no need to check again on the inside since we don't
206 // care if two threads set the bit at once. The synchronized block is only to provide
207 // exclusion with mutations.
208 if (!this.frozen) {
209 synchronized (this) {
210 this.frozen = true;
211 }
212 }
213 }
214
brandjondc2c5502017-12-07 14:30:04 -0800215 /** Registers a builtin with the given simple name, that was defined in the given Java class. */
brandjon73f1a0a2018-03-06 14:19:47 -0800216 public synchronized void registerBuiltin(Class<?> definingClass, String name, Object builtin) {
brandjondc2c5502017-12-07 14:30:04 -0800217 String key = String.format("%s.%s", definingClass.getName(), name);
218 Preconditions.checkArgument(
219 !allBuiltins.containsKey(key),
220 "Builtin '%s' registered multiple times",
221 key);
brandjon73f1a0a2018-03-06 14:19:47 -0800222
223 Preconditions.checkState(
224 !frozen,
225 "Attempted to register builtin '%s' after registry has already been frozen",
226 key);
227
brandjondc2c5502017-12-07 14:30:04 -0800228 allBuiltins.put(key, builtin);
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +0000229 }
brandjondc2c5502017-12-07 14:30:04 -0800230
231 /**
232 * Registers a function underneath a namespace.
233 *
234 * <p>This is independent of {@link #registerBuiltin}.
235 */
brandjon73f1a0a2018-03-06 14:19:47 -0800236 public synchronized void registerFunction(Class<?> namespace, BaseFunction function) {
brandjondc2c5502017-12-07 14:30:04 -0800237 Preconditions.checkNotNull(namespace);
238 Preconditions.checkNotNull(function.getObjectType());
239 Class<?> skylarkNamespace = getSkylarkNamespace(namespace);
240 Preconditions.checkArgument(skylarkNamespace.equals(namespace));
241 Class<?> objType = getSkylarkNamespace(function.getObjectType());
242 Preconditions.checkArgument(objType.equals(skylarkNamespace));
243
brandjon73f1a0a2018-03-06 14:19:47 -0800244 Preconditions.checkState(
245 !frozen,
246 "Attempted to register function '%s' in namespace '%s' after registry has already been "
247 + "frozen",
248 function,
249 namespace);
250
brandjondc2c5502017-12-07 14:30:04 -0800251 functions.computeIfAbsent(namespace, k -> new HashMap<>());
252 functions.get(namespace).put(function.getName(), function);
253 }
254
brandjonba23ae22018-01-16 08:21:37 -0800255 /** Returns a list of all registered builtins, in a deterministic order. */
256 public ImmutableList<Object> getBuiltins() {
brandjon73f1a0a2018-03-06 14:19:47 -0800257 if (frozen) {
258 return ImmutableList.copyOf(allBuiltins.values());
259 } else {
260 synchronized (this) {
261 return ImmutableList.copyOf(allBuiltins.values());
262 }
263 }
brandjondc2c5502017-12-07 14:30:04 -0800264 }
265
266 @Nullable
267 private Map<String, BaseFunction> getFunctionsInNamespace(Class<?> namespace) {
268 return functions.get(getSkylarkNamespace(namespace));
269 }
270
271 /**
272 * Given a namespace, returns the function with the given name.
273 *
274 * <p>If the namespace does not exist or has no function with that name, returns null.
275 */
276 public BaseFunction getFunction(Class<?> namespace, String name) {
brandjon73f1a0a2018-03-06 14:19:47 -0800277 if (frozen) {
278 Map<String, BaseFunction> namespaceFunctions = getFunctionsInNamespace(namespace);
279 return namespaceFunctions != null ? namespaceFunctions.get(name) : null;
280 } else {
281 synchronized (this) {
282 Map<String, BaseFunction> namespaceFunctions = getFunctionsInNamespace(namespace);
283 return namespaceFunctions != null ? namespaceFunctions.get(name) : null;
284 }
285 }
brandjondc2c5502017-12-07 14:30:04 -0800286 }
287
288 /**
289 * Given a namespace, returns all function names.
290 *
291 * <p>If the namespace does not exist, returns an empty set.
292 */
293 public ImmutableSet<String> getFunctionNames(Class<?> namespace) {
brandjon73f1a0a2018-03-06 14:19:47 -0800294 if (frozen) {
295 Map<String, BaseFunction> namespaceFunctions = getFunctionsInNamespace(namespace);
296 if (namespaceFunctions == null) {
297 return ImmutableSet.of();
298 }
299 return ImmutableSet.copyOf(namespaceFunctions.keySet());
300 } else {
301 synchronized (this) {
302 Map<String, BaseFunction> namespaceFunctions = getFunctionsInNamespace(namespace);
303 if (namespaceFunctions == null) {
304 return ImmutableSet.of();
305 }
306 return ImmutableSet.copyOf(namespaceFunctions.keySet());
307 }
brandjondc2c5502017-12-07 14:30:04 -0800308 }
brandjondc2c5502017-12-07 14:30:04 -0800309 }
Francois-Rene Rideauab049e02016-02-17 16:13:46 +0000310 }
311
brandjondc2c5502017-12-07 14:30:04 -0800312 /**
313 * All Skylark builtins.
314 *
315 * <p>Note that just because a symbol is registered here does not necessarily mean that it is
316 * accessible in a particular {@link Environment}. This registry should include any builtin that
317 * is available in any environment.
318 *
319 * <p>Thread safety: This object is unsynchronized. The register functions are typically called
320 * from within static initializer blocks, which should be fine.
321 */
322 private static final BuiltinRegistry builtins = new BuiltinRegistry();
Francois-Rene Rideauab049e02016-02-17 16:13:46 +0000323
brandjondc2c5502017-12-07 14:30:04 -0800324 /** Retrieve the static instance containing information on all known Skylark builtins. */
325 public static BuiltinRegistry getBuiltinRegistry() {
326 return builtins;
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +0000327 }
328
329 /**
cparsonscf3e59d2018-05-23 10:01:20 -0700330 * Convenience overload of {@link #setupModuleGlobals(ImmutableMap.Builder, Class)} to add
331 * bindings directly to an {@link Environment}.
332 *
cparsons474d4a12018-06-21 12:04:59 -0700333 * @param env the Environment into which to register fields
334 * @param moduleClass the Class object containing globals
335 * @deprecated use {@link #setupSkylarkLibrary} instead (and {@link SkylarkCallable} instead of
336 * {@link SkylarkSignature})
cparsonscf3e59d2018-05-23 10:01:20 -0700337 */
cparsons474d4a12018-06-21 12:04:59 -0700338 @Deprecated
cparsonscf3e59d2018-05-23 10:01:20 -0700339 public static void setupModuleGlobals(Environment env, Class<?> moduleClass) {
340 ImmutableMap.Builder<String, Object> envBuilder = ImmutableMap.builder();
341
342 setupModuleGlobals(envBuilder, moduleClass);
343 for (Map.Entry<String, Object> envEntry : envBuilder.build().entrySet()) {
344 env.setup(envEntry.getKey(), envEntry.getValue());
345 }
346 }
347
348 /**
349 * Adds global (top-level) symbols, provided by the given class object, to the given bindings
350 * builder.
cparsonsfcea9de2018-04-16 12:58:03 -0700351 *
352 * <p>Global symbols may be provided by the given class in the following ways:
353 * <ul>
354 * <li>If the class is annotated with {@link SkylarkModule}, an instance of that object is
355 * a global object with the module's name.</li>
356 * <li>If the class has fields annotated with {@link SkylarkSignature}, each of these
357 * fields is a global object with the signature's name.</li>
358 * <li>If the class is annotated with {@link SkylarkGlobalLibrary}, then all of its methods
359 * which are annotated with
360 * {@link com.google.devtools.build.lib.skylarkinterface.SkylarkCallable} are global
361 * callables.</li>
362 * </ul>
363 *
cparsons981f65b2018-04-23 12:04:09 -0700364 * <p>On collisions, this method throws an {@link AssertionError}. Collisions may occur if
365 * multiple global libraries have functions of the same name, two modules of the same name
366 * are given, or if two subclasses of the same module are given.
367 *
cparsonscf3e59d2018-05-23 10:01:20 -0700368 * @param builder the builder for the "bindings" map, which maps from symbol names to objects,
369 * and which will be built into a global frame
370 * @param moduleClass the Class object containing globals
cparsons474d4a12018-06-21 12:04:59 -0700371 * @deprecated use {@link #setupSkylarkLibrary} instead (and {@link SkylarkCallable} instead of
372 * {@link SkylarkSignature})
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +0000373 */
cparsons474d4a12018-06-21 12:04:59 -0700374 @Deprecated
cparsonscf3e59d2018-05-23 10:01:20 -0700375 public static void setupModuleGlobals(ImmutableMap.Builder<String, Object> builder,
376 Class<?> moduleClass) {
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +0000377 try {
cparsons474d4a12018-06-21 12:04:59 -0700378 if (SkylarkInterfaceUtils.getSkylarkModule(moduleClass) != null
379 || SkylarkInterfaceUtils.hasSkylarkGlobalLibrary(moduleClass)) {
380 setupSkylarkLibrary(builder, moduleClass.getConstructor().newInstance());
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +0000381 }
382 for (Field field : moduleClass.getDeclaredFields()) {
383 if (field.isAnnotationPresent(SkylarkSignature.class)) {
384 // Fields in Skylark modules are sometimes private.
385 // Nevertheless they have to be annotated with SkylarkSignature.
386 field.setAccessible(true);
387 SkylarkSignature annotation = field.getAnnotation(SkylarkSignature.class);
388 Object value = field.get(null);
389 // Ignore function factories and non-global functions
390 if (!(value instanceof BuiltinFunction.Factory
391 || (value instanceof BaseFunction
392 && !annotation.objectType().equals(Object.class)))) {
cparsonscf3e59d2018-05-23 10:01:20 -0700393 builder.put(annotation.name(), value);
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +0000394 }
395 }
396 }
Laurent Le Brune51a4d22016-10-11 18:04:16 +0000397 } catch (ReflectiveOperationException e) {
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +0000398 throw new AssertionError(e);
399 }
400 }
cparsons474d4a12018-06-21 12:04:59 -0700401
402 /**
403 * Adds global (top-level) symbols, provided by the given object, to the given bindings
404 * builder.
405 *
406 * <p>Global symbols may be provided by the given object in the following ways:
407 * <ul>
408 * <li>If its class is annotated with {@link SkylarkModule}, an instance of that object is
409 * a global object with the module's name.</li>
410 * <li>If its class is annotated with {@link SkylarkGlobalLibrary}, then all of its methods
411 * which are annotated with
412 * {@link com.google.devtools.build.lib.skylarkinterface.SkylarkCallable} are global
413 * callables.</li>
414 * </ul>
415 *
416 * <p>On collisions, this method throws an {@link AssertionError}. Collisions may occur if
417 * multiple global libraries have functions of the same name, two modules of the same name
418 * are given, or if two subclasses of the same module are given.
419 *
420 * @param builder the builder for the "bindings" map, which maps from symbol names to objects,
421 * and which will be built into a global frame
422 * @param moduleInstance the object containing globals
423 * @throws AssertionError if there are name collisions
424 * @throws IllegalArgumentException if {@code moduleInstance} is not annotated with
425 * {@link SkylarkGlobalLibrary} nor {@link SkylarkModule}
426 */
427 public static void setupSkylarkLibrary(ImmutableMap.Builder<String, Object> builder,
428 Object moduleInstance) {
429 Class<?> moduleClass = moduleInstance.getClass();
430 SkylarkModule skylarkModule = SkylarkInterfaceUtils.getSkylarkModule(moduleClass);
431 boolean hasSkylarkGlobalLibrary = SkylarkInterfaceUtils.hasSkylarkGlobalLibrary(moduleClass);
432
433 Preconditions.checkArgument(hasSkylarkGlobalLibrary || skylarkModule != null,
434 "%s must be annotated with @SkylarkGlobalLibrary or @SkylarkModule",
435 moduleClass);
436
437 if (skylarkModule != null) {
438 builder.put(skylarkModule.name(), moduleInstance);
439 }
440 if (hasSkylarkGlobalLibrary) {
441 for (String methodName : FuncallExpression.getMethodNames(moduleClass)) {
442 builder.put(methodName, FuncallExpression.getBuiltinCallable(moduleInstance, methodName));
443 }
444 }
445 }
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +0000446}