View Javadoc
1   /*
2    * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package jdk.nashorn.internal.ir;
27  
28  import java.util.Collections;
29  import java.util.List;
30  import jdk.nashorn.internal.codegen.types.Type;
31  import jdk.nashorn.internal.ir.annotations.Ignore;
32  import jdk.nashorn.internal.ir.annotations.Immutable;
33  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
34  
35  /**
36   * IR representation for a function call.
37   */
38  @Immutable
39  public final class CallNode extends LexicalContextExpression {
40  
41      /** Function identifier or function body. */
42      private final Expression function;
43  
44      /** Call arguments. */
45      private final List<Expression> args;
46  
47      /** Is this a "new" operation */
48      public static final int IS_NEW        = 0x1;
49  
50      private final int flags;
51  
52      private final int lineNumber;
53  
54      /**
55       * Arguments to be passed to builtin {@code eval} function
56       */
57      public static class EvalArgs {
58          /** evaluated code */
59          private final Expression code;
60  
61          /** 'this' passed to evaluated code */
62          private final IdentNode evalThis;
63  
64          /** location string for the eval call */
65          private final String location;
66  
67          /** is this call from a strict context? */
68          private final boolean strictMode;
69  
70          /**
71           * Constructor
72           *
73           * @param code       code to evaluate
74           * @param evalThis   this node
75           * @param location   location for the eval call
76           * @param strictMode is this a call from a strict context?
77           */
78          public EvalArgs(final Expression code, final IdentNode evalThis, final String location, final boolean strictMode) {
79              this.code = code;
80              this.evalThis = evalThis;
81              this.location = location;
82              this.strictMode = strictMode;
83          }
84  
85          /**
86           * Return the code that is to be eval:ed by this eval function
87           * @return code as an AST node
88           */
89          public Expression getCode() {
90              return code;
91          }
92  
93          private EvalArgs setCode(final Expression code) {
94              if (this.code == code) {
95                  return this;
96              }
97              return new EvalArgs(code, evalThis, location, strictMode);
98          }
99  
100         /**
101          * Get the {@code this} symbol used to invoke this eval call
102          * @return the {@code this} symbol
103          */
104         public IdentNode getThis() {
105             return this.evalThis;
106         }
107 
108         private EvalArgs setThis(final IdentNode evalThis) {
109             if (this.evalThis == evalThis) {
110                 return this;
111             }
112             return new EvalArgs(code, evalThis, location, strictMode);
113         }
114 
115         /**
116          * Get the human readable location for this eval call
117          * @return the location
118          */
119         public String getLocation() {
120             return this.location;
121         }
122 
123         /**
124          * Check whether this eval call is executed in strict mode
125          * @return true if executed in strict mode, false otherwise
126          */
127         public boolean getStrictMode() {
128             return this.strictMode;
129         }
130     }
131 
132     /** arguments for 'eval' call. Non-null only if this call node is 'eval' */
133     @Ignore
134     private final EvalArgs evalArgs;
135 
136     /**
137      * Constructors
138      *
139      * @param lineNumber line number
140      * @param token      token
141      * @param finish     finish
142      * @param function   the function to call
143      * @param args       args to the call
144      */
145     public CallNode(final int lineNumber, final long token, final int finish, final Expression function, final List<Expression> args) {
146         super(token, finish);
147 
148         this.function   = function;
149         this.args       = args;
150         this.flags      = 0;
151         this.evalArgs   = null;
152         this.lineNumber = lineNumber;
153     }
154 
155     private CallNode(final CallNode callNode, final Expression function, final List<Expression> args, final int flags, final EvalArgs evalArgs) {
156         super(callNode);
157         this.lineNumber = callNode.lineNumber;
158         this.function = function;
159         this.args = args;
160         this.flags = flags;
161         this.evalArgs = evalArgs;
162     }
163 
164     /**
165      * Returns the line number.
166      * @return the line number.
167      */
168     public int getLineNumber() {
169         return lineNumber;
170     }
171 
172     @Override
173     public Type getType() {
174         return function instanceof FunctionNode ? ((FunctionNode)function).getReturnType() : Type.OBJECT;
175     }
176 
177     /**
178      * Assist in IR navigation.
179      *
180      * @param visitor IR navigating visitor.
181      *
182      * @return node or replacement
183      */
184     @Override
185     public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
186         if (visitor.enterCallNode(this)) {
187             final CallNode newCallNode = (CallNode)visitor.leaveCallNode(
188                     setFunction((Expression)function.accept(visitor)).
189                     setArgs(Node.accept(visitor, Expression.class, args)).
190                     setFlags(flags).
191                     setEvalArgs(evalArgs == null ?
192                             null :
193                             evalArgs.setCode((Expression)evalArgs.getCode().accept(visitor)).
194                                 setThis((IdentNode)evalArgs.getThis().accept(visitor))));
195             // Theoretically, we'd need to instead pass lc to every setter and do a replacement on each. In practice,
196             // setType from TypeOverride can't accept a lc, and we don't necessarily want to go there now.
197             if(this != newCallNode) {
198                 return Node.replaceInLexicalContext(lc, this, newCallNode);
199             }
200         }
201 
202         return this;
203     }
204 
205     @Override
206     public void toString(final StringBuilder sb) {
207         function.toString(sb);
208 
209         sb.append('(');
210 
211         boolean first = true;
212 
213         for (final Node arg : args) {
214             if (!first) {
215                 sb.append(", ");
216             } else {
217                 first = false;
218             }
219 
220             arg.toString(sb);
221         }
222 
223         sb.append(')');
224     }
225 
226     /**
227      * Get the arguments for the call
228      * @return a list of arguments
229      */
230     public List<Expression> getArgs() {
231         return Collections.unmodifiableList(args);
232     }
233 
234     /**
235      * Reset the arguments for the call
236      * @param args new arguments list
237      */
238     private CallNode setArgs(final List<Expression> args) {
239         if (this.args == args) {
240             return this;
241         }
242         return new CallNode(this, function, args, flags, evalArgs);
243     }
244 
245     /**
246      * If this call is an {@code eval} call, get its EvalArgs structure
247      * @return EvalArgs for call
248      */
249     public EvalArgs getEvalArgs() {
250         return evalArgs;
251     }
252 
253     /**
254      * Set the EvalArgs structure for this call, if it has been determined it is an
255      * {@code eval}
256      *
257      * @param evalArgs eval args
258      * @return same node or new one on state change
259      */
260     public CallNode setEvalArgs(final EvalArgs evalArgs) {
261         if (this.evalArgs == evalArgs) {
262             return this;
263         }
264         return new CallNode(this, function, args, flags, evalArgs);
265     }
266 
267     /**
268      * Check if this call is a call to {@code eval}
269      * @return true if this is a call to {@code eval}
270      */
271     public boolean isEval() {
272         return evalArgs != null;
273     }
274 
275     /**
276      * Return the function expression that this call invokes
277      * @return the function
278      */
279     public Expression getFunction() {
280         return function;
281     }
282 
283     /**
284      * Reset the function expression that this call invokes
285      * @param function the function
286      * @return same node or new one on state change
287      */
288     public CallNode setFunction(final Expression function) {
289         if (this.function == function) {
290             return this;
291         }
292         return new CallNode(this, function, args, flags, evalArgs);
293     }
294 
295     /**
296      * Check if this call is a new operation
297      * @return true if this a new operation
298      */
299     public boolean isNew() {
300         return (flags & IS_NEW) == IS_NEW;
301     }
302 
303     /**
304      * Flag this call as a new operation
305      * @return same node or new one on state change
306      */
307     public CallNode setIsNew() {
308         return setFlags(IS_NEW);
309     }
310 
311     private CallNode setFlags(final int flags) {
312         if (this.flags == flags) {
313             return this;
314         }
315         return new CallNode(this, function, args, flags, evalArgs);
316     }
317 }