blob: c12aaddff51c8add88bb179f58ca5c37f5695e26 [file] [log] [blame]
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001// Copyright 2014 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.
14package com.google.devtools.build.lib.syntax;
15
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010016import com.google.common.collect.ImmutableList;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000017import com.google.common.collect.Iterables;
Googlerfe98ecb2015-09-02 15:14:38 +000018import com.google.devtools.build.lib.events.Location.LineAndColumn;
Googler768cbc42015-08-28 12:52:14 +000019import com.google.devtools.build.lib.profiler.Profiler;
20import com.google.devtools.build.lib.profiler.ProfilerTask;
Googlerfe98ecb2015-09-02 15:14:38 +000021import com.google.devtools.build.lib.vfs.PathFragment;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010022
23/**
24 * The actual function registered in the environment. This function is defined in the
25 * parsed code using {@link FunctionDefStatement}.
26 */
Francois-Rene Rideau4feb1602015-03-18 19:49:13 +000027public class UserDefinedFunction extends BaseFunction {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010028
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010029 private final ImmutableList<Statement> statements;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000030
31 // we close over the globals at the time of definition
32 private final Environment.Frame definitionGlobals;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010033
Florian Weikert6f864c32015-07-23 11:26:39 +000034 protected UserDefinedFunction(Identifier function,
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000035 FunctionSignature.WithValues<Object, SkylarkType> signature,
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000036 ImmutableList<Statement> statements, Environment.Frame definitionGlobals) {
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000037 super(function.getName(), signature, function.getLocation());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010038 this.statements = statements;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000039 this.definitionGlobals = definitionGlobals;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010040 }
41
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000042 public FunctionSignature.WithValues<Object, SkylarkType> getFunctionSignature() {
43 return signature;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010044 }
45
46 ImmutableList<Statement> getStatements() {
47 return statements;
48 }
49
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010050 @Override
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +000051 public Object call(Object[] arguments, FuncallExpression ast, Environment env)
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010052 throws EvalException, InterruptedException {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000053 if (!env.mutability().isMutable()) {
54 throw new EvalException(getLocation(), "Trying to call in frozen environment");
55 }
56 if (env.getStackTrace().contains(this)) {
57 throw new EvalException(getLocation(),
58 String.format("Recursion was detected when calling '%s' from '%s'",
59 getName(), Iterables.getLast(env.getStackTrace()).getName()));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010060 }
61
Klaas Boeschef441c192015-09-09 16:03:55 +000062 Profiler.instance().startTask(ProfilerTask.SKYLARK_USER_FN,
63 getLocationPathAndLine() + "#" + getName());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010064 try {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000065 env.enterScope(this, ast, definitionGlobals);
66 ImmutableList<String> names = signature.getSignature().getNames();
67
68 // Registering the functions's arguments as variables in the local Environment
69 int i = 0;
70 for (String name : names) {
71 env.update(name, arguments[i++]);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010072 }
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000073
74 try {
75 for (Statement stmt : statements) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000076 stmt.exec(env);
77 }
78 } catch (ReturnStatement.ReturnException e) {
79 return e.getValue();
80 }
81 return Runtime.NONE;
Googler768cbc42015-08-28 12:52:14 +000082 } finally {
Klaas Boeschef441c192015-09-09 16:03:55 +000083 Profiler.instance().completeTask(ProfilerTask.SKYLARK_USER_FN);
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000084 env.exitScope();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010085 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010086 }
Florian Weikert3f610e82015-08-18 14:37:46 +000087
88 /**
Googlerfe98ecb2015-09-02 15:14:38 +000089 * Returns the location (filename:line) of the BaseFunction's definition.
90 *
91 * <p>If such a location is not defined, this method returns an empty string.
92 */
93 private String getLocationPathAndLine() {
94 if (location == null) {
95 return "";
96 }
97
98 StringBuilder builder = new StringBuilder();
99 PathFragment path = location.getPath();
100 if (path != null) {
101 builder.append(path.getPathString());
102 }
103
104 LineAndColumn position = location.getStartLineAndColumn();
105 if (position != null) {
106 builder.append(":").append(position.getLine());
107 }
108 return builder.toString();
109 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100110}