blob: 5783d11e0377e66516e7cd63514c0ffdffd4656e [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;
ulfjack4cf2ebd2018-06-11 06:00:36 -070021import com.google.devtools.build.lib.profiler.SilentCloseable;
vladmos3feea742017-07-06 12:16:18 -040022import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter;
nharmata68cc06b2018-03-01 10:21:57 -080023import com.google.devtools.build.lib.syntax.Environment.LexicalFrame;
Klaas Boesche0ec13b92015-11-06 12:16:03 +000024
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010025/**
janakr9ba46f82018-03-13 13:07:52 -070026 * The actual function registered in the environment. This function is defined in the parsed code
27 * using {@link FunctionDefStatement}.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010028 */
Francois-Rene Rideau4feb1602015-03-18 19:49:13 +000029public class UserDefinedFunction extends BaseFunction {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010030
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010031 private final ImmutableList<Statement> statements;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000032
33 // we close over the globals at the time of definition
nharmata68cc06b2018-03-01 10:21:57 -080034 private final Environment.GlobalFrame definitionGlobals;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010035
brandjon64c66322017-08-23 04:36:38 +020036 public UserDefinedFunction(
37 String name,
janakr9ba46f82018-03-13 13:07:52 -070038 Location location,
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000039 FunctionSignature.WithValues<Object, SkylarkType> signature,
Florian Weikert33f819b2015-11-09 18:15:55 +000040 ImmutableList<Statement> statements,
nharmata68cc06b2018-03-01 10:21:57 -080041 Environment.GlobalFrame definitionGlobals) {
janakr9ba46f82018-03-13 13:07:52 -070042 super(name, signature, location);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010043 this.statements = statements;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000044 this.definitionGlobals = definitionGlobals;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010045 }
46
brandjon64c66322017-08-23 04:36:38 +020047 public ImmutableList<Statement> getStatements() {
48 return statements;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010049 }
50
nharmata68cc06b2018-03-01 10:21:57 -080051 public Environment.GlobalFrame getDefinitionGlobals() {
brandjon64c66322017-08-23 04:36:38 +020052 return definitionGlobals;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010053 }
54
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010055 @Override
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000056 public Object call(Object[] arguments, FuncallExpression ast, Environment env)
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010057 throws EvalException, InterruptedException {
brandjon33b49432017-05-04 18:58:52 +020058 if (env.mutability().isFrozen()) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000059 throw new EvalException(getLocation(), "Trying to call in frozen environment");
60 }
michajlocbb33472017-10-23 20:30:18 +020061 if (env.isRecursiveCall(this)) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000062 throw new EvalException(getLocation(),
63 String.format("Recursion was detected when calling '%s' from '%s'",
michajlocbb33472017-10-23 20:30:18 +020064 getName(), env.getCurrentFunction().getName()));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010065 }
66
nharmata62678a42018-03-08 16:43:45 -080067 ImmutableList<String> names = signature.getSignature().getNames();
laurentlbcaafe302018-08-28 10:24:31 -070068 LexicalFrame lexicalFrame = LexicalFrame.create(env.mutability(), /*numArgs=*/ names.size());
ulfjack4cf2ebd2018-06-11 06:00:36 -070069 try (SilentCloseable c = Profiler.instance().profile(ProfilerTask.SKYLARK_USER_FN, getName())) {
nharmata62678a42018-03-08 16:43:45 -080070 env.enterScope(this, lexicalFrame, ast, definitionGlobals);
nharmata6e233f02018-03-07 16:02:22 -080071
72 // Registering the functions's arguments as variables in the local Environment
Taras Tsugrii77e47922018-08-02 06:12:15 -070073 // foreach loop is not used to avoid iterator overhead
74 for (int i = 0; i < names.size(); ++i) {
75 env.update(names.get(i), arguments[i]);
nharmata6e233f02018-03-07 16:02:22 -080076 }
77
Googler29eafdf2018-05-23 12:32:07 -070078 Eval eval = Eval.fromEnvironment(env);
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000079 try {
80 for (Statement stmt : statements) {
Laurent Le Brunb6551672015-10-28 12:42:09 +000081 if (stmt instanceof ReturnStatement) {
82 // Performance optimization.
83 // Executing the statement would throw an exception, which is slow.
fzaiser317a2692017-08-23 16:40:30 +020084 Expression returnExpr = ((ReturnStatement) stmt).getReturnExpression();
85 if (returnExpr == null) {
86 return Runtime.NONE;
87 }
88 return returnExpr.eval(env);
Laurent Le Brunb6551672015-10-28 12:42:09 +000089 } else {
laurentlb919d1b72017-09-01 20:22:11 +020090 eval.exec(stmt);
Laurent Le Brunb6551672015-10-28 12:42:09 +000091 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000092 }
93 } catch (ReturnStatement.ReturnException e) {
94 return e.getValue();
95 }
96 return Runtime.NONE;
Googler768cbc42015-08-28 12:52:14 +000097 } finally {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000098 env.exitScope();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010099 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100100 }
vladmos3feea742017-07-06 12:16:18 -0400101
102 @Override
103 public void repr(SkylarkPrinter printer) {
104 Label label = this.definitionGlobals.getTransitiveLabel();
105
106 printer.append("<function " + getName());
107 if (label != null) {
108 printer.append(" from " + label);
109 }
110 printer.append(">");
111 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100112}