com.google.devtools.build.lib.syntax.BuiltinCallable.java Source code

Java tutorial

Introduction

Here is the source code for com.google.devtools.build.lib.syntax.BuiltinCallable.java

Source

// Copyright 2018 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.syntax;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.devtools.build.lib.events.Location;
import com.google.devtools.build.lib.profiler.Profiler;
import com.google.devtools.build.lib.profiler.ProfilerTask;
import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
import com.google.devtools.build.lib.syntax.Environment.LexicalFrame;
import com.google.devtools.build.lib.syntax.FuncallExpression.MethodDescriptor;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;

/**
 * A function-object abstraction on object methods exposed to skylark using {@link SkylarkCallable}.
 */
public class BuiltinCallable extends BaseFunction {

    /** Represents a required interpreter parameter as dictated by {@link SkylarkCallable} */
    public enum ExtraArgKind {
        LOCATION, // SkylarkCallable.useLocation
        SYNTAX_TREE, // SkylarkCallable.useAst
        ENVIRONMENT, // SkylarkCallable.useEnvironment
        SEMANTICS; // SkylarkCallable.useSemantics
    }

    // Builtins cannot create or modify variable bindings. So it's sufficient to use a shared
    // instance.
    private static final LexicalFrame SHARED_LEXICAL_FRAME_FOR_BUILTIN_METHOD_CALLS = LexicalFrame
            .create(Mutability.IMMUTABLE);

    private final Object obj;
    private final MethodDescriptor descriptor;
    private final List<ExtraArgKind> extraArgs;
    private int innerArgumentCount;

    public BuiltinCallable(String name, Object obj, MethodDescriptor descriptor) {
        super(name);
        this.obj = obj;
        this.descriptor = descriptor;
        this.extraArgs = getExtraArgs(descriptor.getAnnotation());
        configure(obj, descriptor);
    }

    @Override
    protected int getArgArraySize() {
        return innerArgumentCount;
    }

    private static List<ExtraArgKind> getExtraArgs(SkylarkCallable annotation) {
        ImmutableList.Builder<ExtraArgKind> extraArgs = ImmutableList.builder();
        if (annotation.useLocation()) {
            extraArgs.add(ExtraArgKind.LOCATION);
        }
        if (annotation.useAst()) {
            extraArgs.add(ExtraArgKind.SYNTAX_TREE);
        }
        if (annotation.useEnvironment()) {
            extraArgs.add(ExtraArgKind.ENVIRONMENT);
        }
        if (annotation.useSkylarkSemantics()) {
            extraArgs.add(ExtraArgKind.SEMANTICS);
        }
        return extraArgs.build();
    }

    /** Configure a BaseFunction from a @SkylarkCallable-annotated method */
    private void configure(Object obj, MethodDescriptor descriptor) {
        Preconditions.checkState(!isConfigured()); // must not be configured yet

        this.paramDoc = new ArrayList<>();
        this.signature = SkylarkSignatureProcessor.getSignatureForCallable(getName(), descriptor, paramDoc,
                getEnforcedArgumentTypes());
        this.objectType = obj.getClass();
        this.innerArgumentCount = signature.getSignature().getShape().getArguments() + extraArgs.size();
        configure();
    }

    @Override
    @Nullable
    public Object call(Object[] args, FuncallExpression ast, Environment env)
            throws EvalException, InterruptedException {
        Preconditions.checkNotNull(env);

        // ast is null when called from Java (as there's no Skylark call site).
        Location loc = ast == null ? Location.BUILTIN : ast.getLocation();

        // Add extra arguments, if needed
        int index = args.length - extraArgs.size();
        for (ExtraArgKind extraArg : extraArgs) {
            switch (extraArg) {
            case LOCATION:
                args[index] = loc;
                break;

            case SYNTAX_TREE:
                args[index] = ast;
                break;

            case ENVIRONMENT:
                args[index] = env;
                break;

            case SEMANTICS:
                args[index] = env.getSemantics();
                break;
            }
            index++;
        }

        Profiler.instance().startTask(ProfilerTask.SKYLARK_BUILTIN_FN, getName());

        try {
            env.enterScope(this, SHARED_LEXICAL_FRAME_FOR_BUILTIN_METHOD_CALLS, ast, env.getGlobals());
            return FuncallExpression.callMethod(descriptor, getName(), obj, args, ast.getLocation(), env);
        } finally {
            Profiler.instance().completeTask(ProfilerTask.SKYLARK_BUILTIN_FN);
            env.exitScope();
        }
    }
}