blob: 4db184eceb331c3d91ba860eb075a1053d6de0bf [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01002//
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.
14package com.google.devtools.build.lib.syntax;
15
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010016import com.google.common.collect.ImmutableList;
vladmos3feea742017-07-06 12:16:18 -040017import com.google.devtools.build.lib.cmdline.Label;
brandjon64c66322017-08-23 04:36:38 +020018import com.google.devtools.build.lib.events.Location;
Googler768cbc42015-08-28 12:52:14 +000019import com.google.devtools.build.lib.profiler.Profiler;
20import com.google.devtools.build.lib.profiler.ProfilerTask;
vladmos3feea742017-07-06 12:16:18 -040021import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter;
nharmata68cc06b2018-03-01 10:21:57 -080022import com.google.devtools.build.lib.syntax.Environment.LexicalFrame;
Klaas Boesche0ec13b92015-11-06 12:16:03 +000023
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010024/**
janakr9ba46f82018-03-13 13:07:52 -070025 * The actual function registered in the environment. This function is defined in the parsed code
26 * using {@link FunctionDefStatement}.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010027 */
Francois-Rene Rideau4feb1602015-03-18 19:49:13 +000028public class UserDefinedFunction extends BaseFunction {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010029
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010030 private final ImmutableList<Statement> statements;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000031
32 // we close over the globals at the time of definition
nharmata68cc06b2018-03-01 10:21:57 -080033 private final Environment.GlobalFrame definitionGlobals;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010034
brandjon64c66322017-08-23 04:36:38 +020035 public UserDefinedFunction(
36 String name,
janakr9ba46f82018-03-13 13:07:52 -070037 Location location,
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000038 FunctionSignature.WithValues<Object, SkylarkType> signature,
Florian Weikert33f819b2015-11-09 18:15:55 +000039 ImmutableList<Statement> statements,
nharmata68cc06b2018-03-01 10:21:57 -080040 Environment.GlobalFrame definitionGlobals) {
janakr9ba46f82018-03-13 13:07:52 -070041 super(name, signature, location);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010042 this.statements = statements;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000043 this.definitionGlobals = definitionGlobals;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010044 }
45
brandjon64c66322017-08-23 04:36:38 +020046 public ImmutableList<Statement> getStatements() {
47 return statements;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010048 }
49
nharmata68cc06b2018-03-01 10:21:57 -080050 public Environment.GlobalFrame getDefinitionGlobals() {
brandjon64c66322017-08-23 04:36:38 +020051 return definitionGlobals;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010052 }
53
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010054 @Override
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000055 public Object call(Object[] arguments, FuncallExpression ast, Environment env)
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010056 throws EvalException, InterruptedException {
brandjon33b49432017-05-04 18:58:52 +020057 if (env.mutability().isFrozen()) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000058 throw new EvalException(getLocation(), "Trying to call in frozen environment");
59 }
michajlocbb33472017-10-23 20:30:18 +020060 if (env.isRecursiveCall(this)) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000061 throw new EvalException(getLocation(),
62 String.format("Recursion was detected when calling '%s' from '%s'",
michajlocbb33472017-10-23 20:30:18 +020063 getName(), env.getCurrentFunction().getName()));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010064 }
65
nharmata62678a42018-03-08 16:43:45 -080066 ImmutableList<String> names = signature.getSignature().getNames();
67 LexicalFrame lexicalFrame =
68 LexicalFrame.createForUserDefinedFunctionCall(env.mutability(), /*numArgs=*/ names.size());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010069 try {
nharmata62678a42018-03-08 16:43:45 -080070 Profiler.instance().startTask(ProfilerTask.SKYLARK_USER_FN, getName());
71 env.enterScope(this, lexicalFrame, ast, definitionGlobals);
nharmata6e233f02018-03-07 16:02:22 -080072
73 // Registering the functions's arguments as variables in the local Environment
74 int i = 0;
75 for (String name : names) {
76 env.update(name, arguments[i++]);
77 }
78
Googler29eafdf2018-05-23 12:32:07 -070079 Eval eval = Eval.fromEnvironment(env);
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000080 try {
81 for (Statement stmt : statements) {
Laurent Le Brunb6551672015-10-28 12:42:09 +000082 if (stmt instanceof ReturnStatement) {
83 // Performance optimization.
84 // Executing the statement would throw an exception, which is slow.
fzaiser317a2692017-08-23 16:40:30 +020085 Expression returnExpr = ((ReturnStatement) stmt).getReturnExpression();
86 if (returnExpr == null) {
87 return Runtime.NONE;
88 }
89 return returnExpr.eval(env);
Laurent Le Brunb6551672015-10-28 12:42:09 +000090 } else {
laurentlb919d1b72017-09-01 20:22:11 +020091 eval.exec(stmt);
Laurent Le Brunb6551672015-10-28 12:42:09 +000092 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000093 }
94 } catch (ReturnStatement.ReturnException e) {
95 return e.getValue();
96 }
97 return Runtime.NONE;
Googler768cbc42015-08-28 12:52:14 +000098 } finally {
Klaas Boeschef441c192015-09-09 16:03:55 +000099 Profiler.instance().completeTask(ProfilerTask.SKYLARK_USER_FN);
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000100 env.exitScope();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100101 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100102 }
vladmos3feea742017-07-06 12:16:18 -0400103
104 @Override
105 public void repr(SkylarkPrinter printer) {
106 Label label = this.definitionGlobals.getTransitiveLabel();
107
108 printer.append("<function " + getName());
109 if (label != null) {
110 printer.append(" from " + label);
111 }
112 printer.append(">");
113 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100114}