CallNode.java revision 1152:5f6a840fc19d
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
26package jdk.nashorn.internal.ir;
27
28import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
29
30import java.io.Serializable;
31import java.util.Collections;
32import java.util.List;
33import jdk.nashorn.internal.codegen.types.Type;
34import jdk.nashorn.internal.ir.annotations.Ignore;
35import jdk.nashorn.internal.ir.annotations.Immutable;
36import jdk.nashorn.internal.ir.visitor.NodeVisitor;
37
38/**
39 * IR representation for a function call.
40 */
41@Immutable
42public final class CallNode extends LexicalContextExpression implements Optimistic {
43    private static final long serialVersionUID = 1L;
44
45    /** Function identifier or function body. */
46    private final Expression function;
47
48    /** Call arguments. */
49    private final List<Expression> args;
50
51    /** Is this a "new" operation */
52    private static final int IS_NEW = 1 << 0;
53
54    /** Can this be a Function.call? */
55    private static final int IS_APPLY_TO_CALL = 1 << 1;
56
57    private final int flags;
58
59    private final int lineNumber;
60
61    private final int programPoint;
62
63    private final Type optimisticType;
64
65    /**
66     * Arguments to be passed to builtin {@code eval} function
67     */
68    public static class EvalArgs implements Serializable {
69        private static final long serialVersionUID = 1L;
70        private final List<Expression> args;
71
72        /** location string for the eval call */
73        private final String location;
74
75        /**
76         * Constructor
77         *
78         * @param args     arguments to eval
79         * @param location location for the eval call
80         */
81        public EvalArgs(final List<Expression> args, final String location) {
82            this.args = args;
83            this.location = location;
84        }
85
86        /**
87         * Return the code that is to be eval:ed by this eval function
88         * @return code as an AST node
89         */
90        public List<Expression> getArgs() {
91            return Collections.unmodifiableList(args);
92        }
93
94        private EvalArgs setArgs(final List<Expression> args) {
95            if (this.args == args) {
96                return this;
97            }
98            return new EvalArgs(args, location);
99        }
100
101        /**
102         * Get the human readable location for this eval call
103         * @return the location
104         */
105        public String getLocation() {
106            return this.location;
107        }
108    }
109
110    /** arguments for 'eval' call. Non-null only if this call node is 'eval' */
111    @Ignore
112    private final EvalArgs evalArgs;
113
114    /**
115     * Constructors
116     *
117     * @param lineNumber line number
118     * @param token      token
119     * @param finish     finish
120     * @param function   the function to call
121     * @param args       args to the call
122     * @param isNew      true if this is a constructor call with the "new" keyword
123     */
124    public CallNode(final int lineNumber, final long token, final int finish, final Expression function, final List<Expression> args, final boolean isNew) {
125        super(token, finish);
126
127        this.function       = function;
128        this.args           = args;
129        this.flags          = isNew ? IS_NEW : 0;
130        this.evalArgs       = null;
131        this.lineNumber     = lineNumber;
132        this.programPoint   = INVALID_PROGRAM_POINT;
133        this.optimisticType = null;
134    }
135
136    private CallNode(final CallNode callNode, final Expression function, final List<Expression> args, final int flags, final Type optimisticType, final EvalArgs evalArgs, final int programPoint) {
137        super(callNode);
138        this.lineNumber = callNode.lineNumber;
139        this.function = function;
140        this.args = args;
141        this.flags = flags;
142        this.evalArgs = evalArgs;
143        this.programPoint = programPoint;
144        this.optimisticType = optimisticType;
145    }
146
147    /**
148     * Returns the line number.
149     * @return the line number.
150     */
151    public int getLineNumber() {
152        return lineNumber;
153    }
154
155    @Override
156    public Type getType() {
157        return optimisticType == null ? Type.OBJECT : optimisticType;
158    }
159
160    @Override
161    public Optimistic setType(final Type optimisticType) {
162        if (this.optimisticType == optimisticType) {
163            return this;
164        }
165        return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint);
166    }
167
168    /**
169     * Assist in IR navigation.
170     *
171     * @param visitor IR navigating visitor.
172     *
173     * @return node or replacement
174     */
175    @Override
176    public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
177        if (visitor.enterCallNode(this)) {
178            final CallNode newCallNode = (CallNode)visitor.leaveCallNode(
179                    setFunction((Expression)function.accept(visitor)).
180                    setArgs(Node.accept(visitor, args)).
181                    setEvalArgs(evalArgs == null ?
182                            null :
183                            evalArgs.setArgs(Node.accept(visitor, evalArgs.getArgs()))));
184            // Theoretically, we'd need to instead pass lc to every setter and do a replacement on each. In practice,
185            // setType from TypeOverride can't accept a lc, and we don't necessarily want to go there now.
186            if (this != newCallNode) {
187                return Node.replaceInLexicalContext(lc, this, newCallNode);
188            }
189        }
190
191        return this;
192    }
193
194    @Override
195    public void toString(final StringBuilder sb, final boolean printType) {
196        if (printType) {
197            optimisticTypeToString(sb);
198        }
199
200        final StringBuilder fsb = new StringBuilder();
201        function.toString(fsb, printType);
202
203        if (isApplyToCall()) {
204            sb.append(fsb.toString().replace("apply", "[apply => call]"));
205        } else {
206            sb.append(fsb);
207        }
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, printType);
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     * @return new callnode, or same if unchanged
238     */
239    public CallNode setArgs(final List<Expression> args) {
240        if (this.args == args) {
241            return this;
242        }
243        return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint);
244    }
245
246    /**
247     * If this call is an {@code eval} call, get its EvalArgs structure
248     * @return EvalArgs for call
249     */
250    public EvalArgs getEvalArgs() {
251        return evalArgs;
252    }
253
254    /**
255     * Set the EvalArgs structure for this call, if it has been determined it is an
256     * {@code eval}
257     *
258     * @param evalArgs eval args
259     * @return same node or new one on state change
260     */
261    public CallNode setEvalArgs(final EvalArgs evalArgs) {
262        if (this.evalArgs == evalArgs) {
263            return this;
264        }
265        return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint);
266    }
267
268    /**
269     * Check if this call is a call to {@code eval}
270     * @return true if this is a call to {@code eval}
271     */
272    public boolean isEval() {
273        return evalArgs != null;
274    }
275
276    /**
277     * Is this an apply call that we optimistically should try to turn into
278     * a call instead
279     * @return true if apply to call
280     */
281    public boolean isApplyToCall() {
282        return (flags & IS_APPLY_TO_CALL) != 0;
283    }
284
285    /**
286     * Flag this call node as one that tries to call call instead of apply
287     * @return new call node with changed flags, if not already flagged as apply to call, then the same node
288     */
289    public CallNode setIsApplyToCall() {
290        return setFlags(flags | IS_APPLY_TO_CALL);
291    }
292
293    /**
294     * Return the function expression that this call invokes
295     * @return the function
296     */
297    public Expression getFunction() {
298        return function;
299    }
300
301    /**
302     * Reset the function expression that this call invokes
303     * @param function the function
304     * @return same node or new one on state change
305     */
306    public CallNode setFunction(final Expression function) {
307        if (this.function == function) {
308            return this;
309        }
310        return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint);
311    }
312
313    /**
314     * Check if this call is a new operation
315     * @return true if this a new operation
316     */
317    public boolean isNew() {
318        return (flags & IS_NEW) != 0;
319    }
320
321    private CallNode setFlags(final int flags) {
322        if (this.flags == flags) {
323            return this;
324        }
325        return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint);
326    }
327
328    @Override
329    public int getProgramPoint() {
330        return programPoint;
331    }
332
333    @Override
334    public CallNode setProgramPoint(final int programPoint) {
335        if (this.programPoint == programPoint) {
336            return this;
337        }
338        return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint);
339    }
340
341    @Override
342    public Type getMostOptimisticType() {
343        return Type.INT;
344    }
345
346    @Override
347    public Type getMostPessimisticType() {
348        return Type.OBJECT;
349    }
350
351    @Override
352    public boolean canBeOptimistic() {
353        return true;
354    }
355}
356