RuntimeNode.java revision 1197:20c3aef2b4cb
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 java.util.Arrays;
29import java.util.Collections;
30import java.util.List;
31import jdk.nashorn.internal.codegen.types.Type;
32import jdk.nashorn.internal.ir.annotations.Immutable;
33import jdk.nashorn.internal.ir.visitor.NodeVisitor;
34import jdk.nashorn.internal.parser.TokenType;
35
36/**
37 * IR representation for a runtime call.
38 */
39@Immutable
40public class RuntimeNode extends Expression {
41    private static final long serialVersionUID = 1L;
42
43    /**
44     * Request enum used for meta-information about the runtime request
45     */
46    public enum Request {
47        /** An addition with at least one object */
48        ADD(TokenType.ADD, Type.OBJECT, 2, true),
49        /** Request to enter debugger */
50        DEBUGGER,
51        /** New operator */
52        NEW,
53        /** Typeof operator */
54        TYPEOF,
55        /** Reference error type */
56        REFERENCE_ERROR,
57        /** Delete operator */
58        DELETE(TokenType.DELETE, Type.BOOLEAN, 1),
59        /** Delete operator that always fails -- see Lower */
60        FAIL_DELETE(TokenType.DELETE, Type.BOOLEAN, 1, false),
61        /** === operator with at least one object */
62        EQ_STRICT(TokenType.EQ_STRICT, Type.BOOLEAN, 2, true),
63        /** == operator with at least one object */
64        EQ(TokenType.EQ, Type.BOOLEAN, 2, true),
65        /** {@literal >=} operator with at least one object */
66        GE(TokenType.GE, Type.BOOLEAN, 2, true),
67        /** {@literal >} operator with at least one object */
68        GT(TokenType.GT, Type.BOOLEAN, 2, true),
69        /** in operator */
70        IN(TokenType.IN, Type.BOOLEAN, 2),
71        /** instanceof operator */
72        INSTANCEOF(TokenType.INSTANCEOF, Type.BOOLEAN, 2),
73        /** {@literal <=} operator with at least one object */
74        LE(TokenType.LE, Type.BOOLEAN, 2, true),
75        /** {@literal <} operator with at least one object */
76        LT(TokenType.LT, Type.BOOLEAN, 2, true),
77        /** !== operator with at least one object */
78        NE_STRICT(TokenType.NE_STRICT, Type.BOOLEAN, 2, true),
79        /** != operator with at least one object */
80        NE(TokenType.NE, Type.BOOLEAN, 2, true),
81        /** is undefined */
82        IS_UNDEFINED(TokenType.EQ_STRICT, Type.BOOLEAN, 2),
83        /** is not undefined */
84        IS_NOT_UNDEFINED(TokenType.NE_STRICT, Type.BOOLEAN, 2);
85
86        /** token type */
87        private final TokenType tokenType;
88
89        /** return type for request */
90        private final Type returnType;
91
92        /** arity of request */
93        private final int arity;
94
95        /** Can the specializer turn this into something that works with 1 or more primitives? */
96        private final boolean canSpecialize;
97
98        private Request() {
99            this(TokenType.VOID, Type.OBJECT, 0);
100        }
101
102        private Request(final TokenType tokenType, final Type returnType, final int arity) {
103            this(tokenType, returnType, arity, false);
104        }
105
106        private Request(final TokenType tokenType, final Type returnType, final int arity, final boolean canSpecialize) {
107            this.tokenType     = tokenType;
108            this.returnType    = returnType;
109            this.arity         = arity;
110            this.canSpecialize = canSpecialize;
111        }
112
113        /**
114         * Can this request type be specialized?
115         *
116         * @return true if request can be specialized
117         */
118        public boolean canSpecialize() {
119            return canSpecialize;
120        }
121
122        /**
123         * Get arity
124         *
125         * @return the arity of the request
126         */
127        public int getArity() {
128            return arity;
129        }
130
131        /**
132         * Get the return type
133         *
134         * @return return type for request
135         */
136        public Type getReturnType() {
137            return returnType;
138        }
139
140        /**
141         * Get token type
142         *
143         * @return token type for request
144         */
145        public TokenType getTokenType() {
146            return tokenType;
147        }
148
149        /**
150         * Get the non-strict name for this request.
151         *
152         * @return the name without _STRICT suffix
153         */
154        public String nonStrictName() {
155            switch(this) {
156            case NE_STRICT:
157                return NE.name();
158            case EQ_STRICT:
159                return EQ.name();
160            default:
161                return name();
162            }
163        }
164
165        /**
166         * Derive a runtime node request type for a node
167         * @param node the node
168         * @return request type
169         */
170        public static Request requestFor(final Expression node) {
171            switch (node.tokenType()) {
172            case TYPEOF:
173                return Request.TYPEOF;
174            case IN:
175                return Request.IN;
176            case INSTANCEOF:
177                return Request.INSTANCEOF;
178            case EQ_STRICT:
179                return Request.EQ_STRICT;
180            case NE_STRICT:
181                return Request.NE_STRICT;
182            case EQ:
183                return Request.EQ;
184            case NE:
185                return Request.NE;
186            case LT:
187                return Request.LT;
188            case LE:
189                return Request.LE;
190            case GT:
191                return Request.GT;
192            case GE:
193                return Request.GE;
194            default:
195                assert false;
196                return null;
197            }
198        }
199
200        /**
201         * Is this an undefined check?
202         *
203         * @param request request
204         *
205         * @return true if undefined check
206         */
207        public static boolean isUndefinedCheck(final Request request) {
208            return request == IS_UNDEFINED || request == IS_NOT_UNDEFINED;
209        }
210
211        /**
212         * Is this an EQ or EQ_STRICT?
213         *
214         * @param request a request
215         *
216         * @return true if EQ or EQ_STRICT
217         */
218        public static boolean isEQ(final Request request) {
219            return request == EQ || request == EQ_STRICT;
220        }
221
222        /**
223         * Is this an NE or NE_STRICT?
224         *
225         * @param request a request
226         *
227         * @return true if NE or NE_STRICT
228         */
229        public static boolean isNE(final Request request) {
230            return request == NE || request == NE_STRICT;
231        }
232
233        /**
234         * Is this strict?
235         *
236         * @param request a request
237         *
238         * @return true if script
239         */
240        public static boolean isStrict(final Request request) {
241            return request == EQ_STRICT || request == NE_STRICT;
242        }
243
244        /**
245         * If this request can be reversed, return the reverse request
246         * Eq EQ {@literal ->} NE.
247         *
248         * @param request request to reverse
249         *
250         * @return reversed request or null if not applicable
251         */
252        public static Request reverse(final Request request) {
253            switch (request) {
254            case EQ:
255            case EQ_STRICT:
256            case NE:
257            case NE_STRICT:
258                return request;
259            case LE:
260                return GE;
261            case LT:
262                return GT;
263            case GE:
264                return LE;
265            case GT:
266                return LT;
267            default:
268                return null;
269            }
270        }
271
272        /**
273         * Invert the request, only for non equals comparisons.
274         *
275         * @param request a request
276         *
277         * @return the inverted rquest, or null if not applicable
278         */
279        public static Request invert(final Request request) {
280            switch (request) {
281            case EQ:
282                return NE;
283            case EQ_STRICT:
284                return NE_STRICT;
285            case NE:
286                return EQ;
287            case NE_STRICT:
288                return EQ_STRICT;
289            case LE:
290                return GT;
291            case LT:
292                return GE;
293            case GE:
294                return LT;
295            case GT:
296                return LE;
297            default:
298                return null;
299            }
300        }
301
302        /**
303         * Check if this is a comparison
304         *
305         * @param request a request
306         *
307         * @return true if this is a comparison, null otherwise
308         */
309        public static boolean isComparison(final Request request) {
310            switch (request) {
311            case EQ:
312            case EQ_STRICT:
313            case NE:
314            case NE_STRICT:
315            case LE:
316            case LT:
317            case GE:
318            case GT:
319            case IS_UNDEFINED:
320            case IS_NOT_UNDEFINED:
321                return true;
322            default:
323                return false;
324            }
325        }
326    }
327
328    /** Runtime request. */
329    private final Request request;
330
331    /** Call arguments. */
332    private final List<Expression> args;
333
334    /**
335     * Constructor
336     *
337     * @param token   token
338     * @param finish  finish
339     * @param request the request
340     * @param args    arguments to request
341     */
342    public RuntimeNode(final long token, final int finish, final Request request, final List<Expression> args) {
343        super(token, finish);
344
345        this.request      = request;
346        this.args         = args;
347    }
348
349    private RuntimeNode(final RuntimeNode runtimeNode, final Request request, final List<Expression> args) {
350        super(runtimeNode);
351
352        this.request      = request;
353        this.args         = args;
354    }
355
356    /**
357     * Constructor
358     *
359     * @param token   token
360     * @param finish  finish
361     * @param request the request
362     * @param args    arguments to request
363     */
364    public RuntimeNode(final long token, final int finish, final Request request, final Expression... args) {
365        this(token, finish, request, Arrays.asList(args));
366    }
367
368    /**
369     * Constructor
370     *
371     * @param parent  parent node from which to inherit source, token, finish
372     * @param request the request
373     * @param args    arguments to request
374     */
375    public RuntimeNode(final Expression parent, final Request request, final Expression... args) {
376        this(parent, request, Arrays.asList(args));
377    }
378
379    /**
380     * Constructor
381     *
382     * @param parent  parent node from which to inherit source, token, finish
383     * @param request the request
384     * @param args    arguments to request
385     */
386    public RuntimeNode(final Expression parent, final Request request, final List<Expression> args) {
387        super(parent);
388
389        this.request      = request;
390        this.args         = args;
391    }
392
393    /**
394     * Constructor
395     *
396     * @param parent  parent node from which to inherit source, token, finish and arguments
397     * @param request the request
398     */
399    public RuntimeNode(final UnaryNode parent, final Request request) {
400        this(parent, request, parent.getExpression());
401    }
402
403    /**
404     * Constructor used to replace a binary node with a runtime request.
405     *
406     * @param parent  parent node from which to inherit source, token, finish and arguments
407     */
408    public RuntimeNode(final BinaryNode parent) {
409        this(parent, Request.requestFor(parent), parent.lhs(), parent.rhs());
410    }
411
412    /**
413     * Reset the request for this runtime node
414     * @param request request
415     * @return new runtime node or same if same request
416     */
417    public RuntimeNode setRequest(final Request request) {
418        if (this.request == request) {
419            return this;
420        }
421        return new RuntimeNode(this, request, args);
422   }
423
424    /**
425     * Return type for the ReferenceNode
426     */
427    @Override
428    public Type getType() {
429        return request.getReturnType();
430    }
431
432    @Override
433    public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
434        if (visitor.enterRuntimeNode(this)) {
435            return visitor.leaveRuntimeNode(setArgs(Node.accept(visitor, args)));
436        }
437
438        return this;
439    }
440
441    @Override
442    public void toString(final StringBuilder sb, final boolean printType) {
443        sb.append("ScriptRuntime.");
444        sb.append(request);
445        sb.append('(');
446
447        boolean first = true;
448
449        for (final Node arg : args) {
450            if (!first) {
451                sb.append(", ");
452            } else {
453                first = false;
454            }
455
456            arg.toString(sb, printType);
457        }
458
459        sb.append(')');
460    }
461
462    /**
463     * Get the arguments for this runtime node
464     * @return argument list
465     */
466    public List<Expression> getArgs() {
467        return Collections.unmodifiableList(args);
468    }
469
470    /**
471     * Set the arguments of this runtime node
472     * @param args new arguments
473     * @return new runtime node, or identical if no change
474     */
475    public RuntimeNode setArgs(final List<Expression> args) {
476        if (this.args == args) {
477            return this;
478        }
479        return new RuntimeNode(this, request, args);
480    }
481
482    /**
483     * Get the request that this runtime node implements
484     * @return the request
485     */
486    public Request getRequest() {
487        return request;
488    }
489
490    /**
491     * Is this runtime node, engineered to handle the "at least one object" case of the defined
492     * requests and specialize on demand, really primitive. This can happen e.g. after AccessSpecializer
493     * In that case it can be turned into a simpler primitive form in CodeGenerator
494     *
495     * @return true if all arguments now are primitive
496     */
497    public boolean isPrimitive() {
498        for (final Expression arg : args) {
499            if (arg.getType().isObject()) {
500                return false;
501            }
502        }
503        return true;
504    }
505}
506