diff options
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/syntax/Runtime.java')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/syntax/Runtime.java | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Runtime.java b/src/main/java/com/google/devtools/build/lib/syntax/Runtime.java new file mode 100644 index 0000000000..50ef2546d5 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/syntax/Runtime.java @@ -0,0 +1,176 @@ +// Copyright 2015 Google Inc. 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.common.base.Preconditions; +import com.google.common.collect.ImmutableSet; +import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Global constants and support for global namespaces of runtime functions. + */ +public final class Runtime { + + private Runtime() {} + + @SkylarkSignature(name = "True", returnType = Boolean.class, + doc = "Literal for the boolean true.") + static final Boolean TRUE = true; + + @SkylarkSignature(name = "False", returnType = Boolean.class, + doc = "Literal for the boolean false.") + static final Boolean FALSE = false; + + /** + * There should be only one instance of this type to allow "== None" tests. + */ + @Immutable + public static final class NoneType { + @Override + public String toString() { return "None"; } + private NoneType() {} + } + + @SkylarkSignature(name = "None", returnType = NoneType.class, + doc = "Literal for the None value.") + public static final NoneType NONE = new NoneType(); + + + @SkylarkSignature(name = "PACKAGE_NAME", returnType = String.class, + doc = "The name of the package the rule or build extension is called from. " + + "For example, in the BUILD file <code>some/package/BUILD</code>, its value " + + "will be <code>some/package</code>. " + + "This variable is special, because its value comes from outside of the extension " + + "module (it comes from the BUILD file), so it can only be accessed in functions " + + "(transitively) called from BUILD files. For example:<br>" + + "<pre class=language-python>def extension():\n" + + " return PACKAGE_NAME</pre>" + + "In this case calling <code>extension()</code> works from the BUILD file (if the " + + "function is loaded), but not as a top level function call in the extension module.") + public static final String PKG_NAME = "PACKAGE_NAME"; + + /** + * Set up a given environment for supported class methods. + */ + static Environment setupConstants(Environment env) throws EvalException { + // In Python 2.x, True and False are global values and can be redefined by the user. + // In Python 3.x, they are keywords. We implement them as values, for the sake of + // simplicity. We define them as Boolean objects. + return env.update("False", FALSE).update("True", TRUE).update("None", NONE); + } + + + /** Global registry of functions associated to a class or namespace */ + private static final Map<Class<?>, Map<String, BaseFunction>> functions = new HashMap<>(); + + /** + * Registers a function with namespace to this global environment. + */ + public static void registerFunction(Class<?> nameSpace, BaseFunction function) { + Preconditions.checkNotNull(nameSpace); + // TODO(bazel-team): fix our code so that the two checks below work. + // Preconditions.checkArgument(function.getObjectType().equals(nameSpace)); + // Preconditions.checkArgument(nameSpace.equals(getCanonicalRepresentation(nameSpace))); + nameSpace = getCanonicalRepresentation(nameSpace); + if (!functions.containsKey(nameSpace)) { + functions.put(nameSpace, new HashMap<String, BaseFunction>()); + } + functions.get(nameSpace).put(function.getName(), function); + } + + static Map<String, BaseFunction> getNamespaceFunctions(Class<?> nameSpace) { + return functions.get(getCanonicalRepresentation(nameSpace)); + } + + /** + * Returns the canonical representation of the given class, i.e. the super class for which any + * functions were registered. + * + * <p>Currently, this is only necessary for mapping the different subclasses of {@link + * java.util.Map} to the interface. + */ + public static Class<?> getCanonicalRepresentation(Class<?> clazz) { + if (Map.class.isAssignableFrom(clazz)) { + return MethodLibrary.DictModule.class; + } + if (String.class.isAssignableFrom(clazz)) { + return MethodLibrary.StringModule.class; + } + if (List.class.isAssignableFrom(clazz)) { + return List.class; + } + return clazz; + } + + /** + * Registers global fields with SkylarkSignature into the specified Environment. + * @param env the Environment into which to register fields. + * @param moduleClass the Class object containing globals. + */ + public static void registerModuleGlobals(Environment env, Class<?> moduleClass) { + try { + if (moduleClass.isAnnotationPresent(SkylarkModule.class)) { + env.update( + moduleClass.getAnnotation(SkylarkModule.class).name(), moduleClass.newInstance()); + } + for (Field field : moduleClass.getDeclaredFields()) { + if (field.isAnnotationPresent(SkylarkSignature.class)) { + // Fields in Skylark modules are sometimes private. + // Nevertheless they have to be annotated with SkylarkSignature. + field.setAccessible(true); + SkylarkSignature annotation = field.getAnnotation(SkylarkSignature.class); + Object value = field.get(null); + // Ignore function factories and non-global functions + if (!(value instanceof BuiltinFunction.Factory + || (value instanceof BaseFunction + && !annotation.objectType().equals(Object.class)))) { + env.update(annotation.name(), value); + } + } + } + } catch (IllegalAccessException | InstantiationException e) { + throw new AssertionError(e); + } + } + + /** + * Returns the function of the namespace of the given name or null of it does not exists. + */ + public static BaseFunction getFunction(Class<?> nameSpace, String name) { + Map<String, BaseFunction> nameSpaceFunctions = getNamespaceFunctions(nameSpace); + return nameSpaceFunctions != null ? nameSpaceFunctions.get(name) : null; + } + + /** + * Returns the function names registered with the namespace. + */ + public static Set<String> getFunctionNames(Class<?> nameSpace) { + Map<String, BaseFunction> nameSpaceFunctions = getNamespaceFunctions(nameSpace); + return nameSpaceFunctions != null ? nameSpaceFunctions.keySet() : ImmutableSet.<String>of(); + } + + static void setupMethodEnvironment( + Environment env, Iterable<BaseFunction> functions) throws EvalException { + for (BaseFunction function : functions) { + env.update(function.getName(), function); + } + } +} |