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