blob: ca83d65bf78dc9a66209f66958285d7c6b249525 [file] [log] [blame]
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +00001// Copyright 2015 Google Inc. 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.base.Preconditions;
18import com.google.common.collect.ImmutableSet;
19import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
20
21import java.lang.reflect.Field;
22import java.util.HashMap;
23import java.util.List;
24import java.util.Map;
25import java.util.Set;
26
27/**
28 * Global constants and support for global namespaces of runtime functions.
29 */
30public final class Runtime {
31
32 private Runtime() {}
33
34 @SkylarkSignature(name = "True", returnType = Boolean.class,
35 doc = "Literal for the boolean true.")
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000036 private static final Boolean TRUE = true;
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +000037
38 @SkylarkSignature(name = "False", returnType = Boolean.class,
39 doc = "Literal for the boolean false.")
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000040 private static final Boolean FALSE = false;
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +000041
42 /**
43 * There should be only one instance of this type to allow "== None" tests.
44 */
Francois-Rene Rideau4e994102015-09-17 22:41:28 +000045 @SkylarkModule(name = "NoneType", documented = false,
46 doc = "Unit type, containing the unique value None")
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +000047 @Immutable
Francois-Rene Rideau6c10eac2015-09-17 19:17:20 +000048 public static final class NoneType implements SkylarkValue {
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +000049 private NoneType() {}
Francois-Rene Rideau6c10eac2015-09-17 19:17:20 +000050
51 @Override
52 public String toString() {
53 return "None";
54 }
55
56 @Override
57 public boolean isImmutable() {
58 return true;
59 }
60
61 @Override
62 public void write(Appendable buffer, char quotationMark) {
63 Printer.append(buffer, "None");
64 }
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +000065 }
66
67 @SkylarkSignature(name = "None", returnType = NoneType.class,
68 doc = "Literal for the None value.")
69 public static final NoneType NONE = new NoneType();
70
71
72 @SkylarkSignature(name = "PACKAGE_NAME", returnType = String.class,
73 doc = "The name of the package the rule or build extension is called from. "
74 + "For example, in the BUILD file <code>some/package/BUILD</code>, its value "
75 + "will be <code>some/package</code>. "
76 + "This variable is special, because its value comes from outside of the extension "
77 + "module (it comes from the BUILD file), so it can only be accessed in functions "
78 + "(transitively) called from BUILD files. For example:<br>"
79 + "<pre class=language-python>def extension():\n"
80 + " return PACKAGE_NAME</pre>"
81 + "In this case calling <code>extension()</code> works from the BUILD file (if the "
82 + "function is loaded), but not as a top level function call in the extension module.")
83 public static final String PKG_NAME = "PACKAGE_NAME";
84
85 /**
86 * Set up a given environment for supported class methods.
87 */
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000088 static Environment setupConstants(Environment env) {
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +000089 // In Python 2.x, True and False are global values and can be redefined by the user.
90 // In Python 3.x, they are keywords. We implement them as values, for the sake of
91 // simplicity. We define them as Boolean objects.
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000092 return env.setup("False", FALSE).setup("True", TRUE).setup("None", NONE);
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +000093 }
94
95
96 /** Global registry of functions associated to a class or namespace */
97 private static final Map<Class<?>, Map<String, BaseFunction>> functions = new HashMap<>();
98
99 /**
100 * Registers a function with namespace to this global environment.
101 */
102 public static void registerFunction(Class<?> nameSpace, BaseFunction function) {
103 Preconditions.checkNotNull(nameSpace);
104 // TODO(bazel-team): fix our code so that the two checks below work.
105 // Preconditions.checkArgument(function.getObjectType().equals(nameSpace));
106 // Preconditions.checkArgument(nameSpace.equals(getCanonicalRepresentation(nameSpace)));
107 nameSpace = getCanonicalRepresentation(nameSpace);
108 if (!functions.containsKey(nameSpace)) {
109 functions.put(nameSpace, new HashMap<String, BaseFunction>());
110 }
111 functions.get(nameSpace).put(function.getName(), function);
112 }
113
114 static Map<String, BaseFunction> getNamespaceFunctions(Class<?> nameSpace) {
115 return functions.get(getCanonicalRepresentation(nameSpace));
116 }
117
118 /**
119 * Returns the canonical representation of the given class, i.e. the super class for which any
120 * functions were registered.
121 *
122 * <p>Currently, this is only necessary for mapping the different subclasses of {@link
123 * java.util.Map} to the interface.
124 */
125 public static Class<?> getCanonicalRepresentation(Class<?> clazz) {
126 if (Map.class.isAssignableFrom(clazz)) {
127 return MethodLibrary.DictModule.class;
128 }
129 if (String.class.isAssignableFrom(clazz)) {
130 return MethodLibrary.StringModule.class;
131 }
132 if (List.class.isAssignableFrom(clazz)) {
133 return List.class;
134 }
135 return clazz;
136 }
137
138 /**
139 * Registers global fields with SkylarkSignature into the specified Environment.
140 * @param env the Environment into which to register fields.
141 * @param moduleClass the Class object containing globals.
142 */
143 public static void registerModuleGlobals(Environment env, Class<?> moduleClass) {
144 try {
145 if (moduleClass.isAnnotationPresent(SkylarkModule.class)) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000146 env.setup(
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +0000147 moduleClass.getAnnotation(SkylarkModule.class).name(), moduleClass.newInstance());
148 }
149 for (Field field : moduleClass.getDeclaredFields()) {
150 if (field.isAnnotationPresent(SkylarkSignature.class)) {
151 // Fields in Skylark modules are sometimes private.
152 // Nevertheless they have to be annotated with SkylarkSignature.
153 field.setAccessible(true);
154 SkylarkSignature annotation = field.getAnnotation(SkylarkSignature.class);
155 Object value = field.get(null);
156 // Ignore function factories and non-global functions
157 if (!(value instanceof BuiltinFunction.Factory
158 || (value instanceof BaseFunction
159 && !annotation.objectType().equals(Object.class)))) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000160 env.setup(annotation.name(), value);
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +0000161 }
162 }
163 }
164 } catch (IllegalAccessException | InstantiationException e) {
165 throw new AssertionError(e);
166 }
167 }
168
169 /**
170 * Returns the function of the namespace of the given name or null of it does not exists.
171 */
172 public static BaseFunction getFunction(Class<?> nameSpace, String name) {
173 Map<String, BaseFunction> nameSpaceFunctions = getNamespaceFunctions(nameSpace);
174 return nameSpaceFunctions != null ? nameSpaceFunctions.get(name) : null;
175 }
176
177 /**
178 * Returns the function names registered with the namespace.
179 */
180 public static Set<String> getFunctionNames(Class<?> nameSpace) {
181 Map<String, BaseFunction> nameSpaceFunctions = getNamespaceFunctions(nameSpace);
182 return nameSpaceFunctions != null ? nameSpaceFunctions.keySet() : ImmutableSet.<String>of();
183 }
184
185 static void setupMethodEnvironment(
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000186 Environment env, Iterable<BaseFunction> functions) {
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +0000187 for (BaseFunction function : functions) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000188 env.setup(function.getName(), function);
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +0000189 }
190 }
191}