Operators.java revision 2957:098657cc98c9
1/*
2 * Copyright (c) 2015, 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 com.sun.tools.javac.comp;
27
28import com.sun.tools.javac.code.Symbol;
29import com.sun.tools.javac.code.Symbol.OperatorSymbol;
30import com.sun.tools.javac.code.Symtab;
31import com.sun.tools.javac.code.Type;
32import com.sun.tools.javac.code.Type.MethodType;
33import com.sun.tools.javac.code.TypeTag;
34import com.sun.tools.javac.code.Types;
35import com.sun.tools.javac.jvm.ByteCodes;
36import com.sun.tools.javac.resources.CompilerProperties.Errors;
37import com.sun.tools.javac.tree.JCTree;
38import com.sun.tools.javac.tree.JCTree.Tag;
39import com.sun.tools.javac.util.Assert;
40import com.sun.tools.javac.util.Context;
41import com.sun.tools.javac.util.JCDiagnostic;
42import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
43import com.sun.tools.javac.util.List;
44import com.sun.tools.javac.util.Log;
45import com.sun.tools.javac.util.Name;
46import com.sun.tools.javac.util.Names;
47
48import java.util.HashMap;
49import java.util.Map;
50import java.util.Optional;
51import java.util.function.BiPredicate;
52import java.util.function.Function;
53import java.util.function.Predicate;
54import java.util.function.Supplier;
55import java.util.stream.Stream;
56
57import static com.sun.tools.javac.jvm.ByteCodes.*;
58import static com.sun.tools.javac.comp.Operators.OperatorType.*;
59
60/**
61 * This class contains the logic for unary and binary operator resolution/lookup.
62 *
63 * <p><b>This is NOT part of any supported API.
64 * If you write code that depends on this, you do so at your own risk.
65 * This code and its internal interfaces are subject to change or
66 * deletion without notice.</b>
67 */
68public class Operators {
69    protected static final Context.Key<Operators> operatorsKey = new Context.Key<>();
70
71    private final Names names;
72    private final Log log;
73    private final Symtab syms;
74    private final Types types;
75
76    /** Unary operators map. */
77    private Map<Name, List<UnaryOperatorHelper>> unaryOperators = new HashMap<>(Tag.getNumberOfOperators());
78
79    /** Binary operators map. */
80    private Map<Name, List<BinaryOperatorHelper>> binaryOperators = new HashMap<>(Tag.getNumberOfOperators());
81
82    /** The names of all operators. */
83    private Name[] opname = new Name[Tag.getNumberOfOperators()];
84
85    public static Operators instance(Context context) {
86        Operators instance = context.get(operatorsKey);
87        if (instance == null)
88            instance = new Operators(context);
89        return instance;
90    }
91
92    protected Operators(Context context) {
93        context.put(operatorsKey, this);
94        syms = Symtab.instance(context);
95        names = Names.instance(context);
96        log = Log.instance(context);
97        types = Types.instance(context);
98        initOperatorNames();
99        initUnaryOperators();
100        initBinaryOperators();
101    }
102
103    /**
104     * Perform unary promotion of a type; this routine implements JLS 5.6.1.
105     * If the input type is not supported by unary promotion, it is returned unaltered.
106     */
107    Type unaryPromotion(Type t) {
108        Type unboxed = types.unboxedTypeOrType(t);
109        switch (unboxed.getTag()) {
110            case BYTE:
111            case SHORT:
112            case CHAR:
113                return syms.intType;
114            default:
115                return unboxed;
116        }
117    }
118
119    /**
120     * Perform binary promotion of a pair of types; this routine implements JLS 5.6.2.
121     * If the input types are not supported by unary promotion, if such types are identical to
122     * a type C, then C is returned, otherwise Object is returned.
123     */
124    Type binaryPromotion(Type t1, Type t2) {
125        Type unboxedT1 = types.unboxedTypeOrType(t1);
126        Type unboxedT2 = types.unboxedTypeOrType(t2);
127
128        if (unboxedT1.isNumeric() && unboxedT2.isNumeric()) {
129            if (unboxedT1.hasTag(TypeTag.DOUBLE) || unboxedT2.hasTag(TypeTag.DOUBLE)) {
130                return syms.doubleType;
131            } else if (unboxedT1.hasTag(TypeTag.FLOAT) || unboxedT2.hasTag(TypeTag.FLOAT)) {
132                return syms.floatType;
133            } else if (unboxedT1.hasTag(TypeTag.LONG) || unboxedT2.hasTag(TypeTag.LONG)) {
134                return syms.longType;
135            } else {
136                return syms.intType;
137            }
138        } else if (types.isSameType(unboxedT1, unboxedT2)) {
139            return unboxedT1;
140        } else {
141            return syms.objectType;
142        }
143    }
144
145    /**
146     * Entry point for resolving a unary operator given an operator tag and an argument type.
147     */
148    Symbol resolveUnary(DiagnosticPosition pos, JCTree.Tag tag, Type op) {
149        return resolve(tag,
150                unaryOperators,
151                unop -> unop.test(op),
152                unop -> unop.resolve(op),
153                () -> reportErrorIfNeeded(pos, tag, op));
154    }
155
156    /**
157     * Entry point for resolving a binary operator given an operator tag and a pair of argument types.
158     */
159    Symbol resolveBinary(DiagnosticPosition pos, JCTree.Tag tag, Type op1, Type op2) {
160        return resolve(tag,
161                binaryOperators,
162                binop -> binop.test(op1, op2),
163                binop -> binop.resolve(op1, op2),
164                () -> reportErrorIfNeeded(pos, tag, op1, op2));
165    }
166
167    /**
168     * Main operator lookup routine; lookup an operator (either unary or binary) in its corresponding
169     * map. If there's a matching operator, its resolve routine is called and the result is returned;
170     * otherwise the result of a fallback function is returned.
171     */
172    private <O> Symbol resolve(Tag tag, Map<Name, List<O>> opMap, Predicate<O> opTestFunc,
173                       Function<O, Symbol> resolveFunc, Supplier<Symbol> noResultFunc) {
174        return opMap.get(operatorName(tag)).stream()
175                .filter(opTestFunc)
176                .map(resolveFunc)
177                .findFirst()
178                .orElseGet(noResultFunc);
179    }
180
181    /**
182     * Creates an operator symbol.
183     */
184    private Symbol makeOperator(Name name, List<OperatorType> formals, OperatorType res, int... opcodes) {
185        MethodType opType = new MethodType(
186                formals.stream()
187                        .map(o -> o.asType(syms))
188                        .collect(List.collector()),
189                res.asType(syms), List.nil(), syms.methodClass);
190        return new OperatorSymbol(name, opType, mergeOpcodes(opcodes), syms.noSymbol);
191    }
192
193    /**
194     * Fold two opcodes in a single int value (if required).
195     */
196    private int mergeOpcodes(int... opcodes) {
197        int opcodesLen = opcodes.length;
198        Assert.check(opcodesLen == 1 || opcodesLen == 2);
199        return (opcodesLen == 1) ?
200                opcodes[0] :
201                ((opcodes[0] << ByteCodes.preShift) | opcodes[1]);
202    }
203
204    /**
205     * Report an operator lookup error.
206     */
207    private Symbol reportErrorIfNeeded(DiagnosticPosition pos, Tag tag, Type... args) {
208        if (Stream.of(args).noneMatch(Type::isErroneous)) {
209            Name opName = operatorName(tag);
210            JCDiagnostic.Error opError = (args.length) == 1 ?
211                    Errors.OperatorCantBeApplied(opName, args[0]) :
212                    Errors.OperatorCantBeApplied1(opName, args[0], args[1]);
213            log.error(pos, opError);
214        }
215        return syms.noSymbol;
216    }
217
218    /**
219     * Return name of operator with given tree tag.
220     */
221    public Name operatorName(JCTree.Tag tag) {
222        return opname[tag.operatorIndex()];
223    }
224
225    /**
226     * The constants in this enum represent the types upon which all the operator helpers
227     * operate upon. This allows lazy and consise mapping between a type name and a type instance.
228     */
229    enum OperatorType {
230        BYTE(syms -> syms.byteType),
231        SHORT(syms -> syms.shortType),
232        INT(syms -> syms.intType),
233        LONG(syms -> syms.longType),
234        FLOAT(syms -> syms.floatType),
235        DOUBLE(syms -> syms.doubleType),
236        CHAR(syms -> syms.charType),
237        BOOLEAN(syms -> syms.booleanType),
238        OBJECT(syms -> syms.objectType),
239        STRING(syms -> syms.stringType),
240        BOT(syms -> syms.botType);
241
242        final Function<Symtab, Type> asTypeFunc;
243
244        OperatorType(Function<Symtab, Type> asTypeFunc) {
245            this.asTypeFunc = asTypeFunc;
246        }
247
248        Type asType(Symtab syms) {
249            return asTypeFunc.apply(syms);
250        }
251    }
252
253    /**
254     * Common root for all operator helpers. An operator helper instance is associated with a
255     * given operator (i.e. '+'); it contains routines to perform operator lookup, i.e. find
256     * which version of the '+' operator is the best given an argument type list. Supported
257     * operator symbols are initialized lazily upon first lookup request - this is in order to avoid
258     * initialization circularities between this class and {@code Symtab}.
259     */
260    abstract class OperatorHelper {
261
262        /** The operator name. */
263        final Name name;
264
265        /** The list of symbols associated with this operator (lazily populated). */
266        Optional<Symbol[]> alternatives = Optional.empty();
267
268        /** An array of operator symbol suppliers (used to lazily populate the symbol list). */
269        List<Supplier<Symbol>> operatorSuppliers = List.nil();
270
271        @SuppressWarnings("varargs")
272        OperatorHelper(Tag tag) {
273            this.name = operatorName(tag);
274        }
275
276        /**
277         * This routine implements the main operator lookup process. Each operator is tested
278         * using an applicability predicate; if the test suceeds that same operator is returned,
279         * otherwise a dummy symbol is returned.
280         */
281        final Symbol doLookup(Predicate<Symbol> applicabilityTest) {
282            return Stream.of(alternatives.orElseGet(this::initOperators))
283                    .filter(applicabilityTest)
284                    .findFirst()
285                    .orElse(syms.noSymbol);
286        }
287
288        /**
289         * This routine performs lazy instantiation of the operator symbols supported by this helper.
290         * After initialization is done, the suppliers are cleared, to free up memory.
291         */
292        private Symbol[] initOperators() {
293            Symbol[] operators = operatorSuppliers.stream()
294                    .map(op -> op.get())
295                    .toArray(Symbol[]::new);
296            alternatives = Optional.of(operators);
297            operatorSuppliers = null; //let GC do its work
298            return operators;
299        }
300    }
301
302    /**
303     * Common superclass for all unary operator helpers.
304     */
305    abstract class UnaryOperatorHelper extends OperatorHelper implements Predicate<Type> {
306
307        UnaryOperatorHelper(Tag tag) {
308            super(tag);
309        }
310
311        /**
312         * This routine implements the unary operator lookup process. It customizes the behavior
313         * of the shared lookup routine in {@link OperatorHelper}, by using an unary applicability test
314         * (see {@link UnaryOperatorHelper#isUnaryOperatorApplicable(OperatorSymbol, Type)}
315         */
316        final Symbol doLookup(Type t) {
317            return doLookup(op -> isUnaryOperatorApplicable((OperatorSymbol)op, t));
318        }
319
320        /**
321         * Unary operator applicability test - is the input type the same as the expected operand type?
322         */
323        boolean isUnaryOperatorApplicable(OperatorSymbol op, Type t) {
324            return types.isSameType(op.type.getParameterTypes().head, t);
325        }
326
327        /**
328         * Adds a unary operator symbol.
329         */
330        final UnaryOperatorHelper addUnaryOperator(OperatorType arg, OperatorType res, int... opcode) {
331            operatorSuppliers = operatorSuppliers.prepend(() -> makeOperator(name, List.of(arg), res, opcode));
332            return this;
333        }
334
335        /**
336         * This method will be overridden by unary operator helpers to provide custom resolution
337         * logic.
338         */
339        abstract Symbol resolve(Type t);
340    }
341
342    abstract class BinaryOperatorHelper extends OperatorHelper implements BiPredicate<Type, Type> {
343
344        BinaryOperatorHelper(Tag tag) {
345            super(tag);
346        }
347
348        /**
349         * This routine implements the binary operator lookup process. It customizes the behavior
350         * of the shared lookup routine in {@link OperatorHelper}, by using an unary applicability test
351         * (see {@link BinaryOperatorHelper#isBinaryOperatorApplicable(OperatorSymbol, Type, Type)}
352         */
353        final Symbol doLookup(Type t1, Type t2) {
354            return doLookup(op -> isBinaryOperatorApplicable((OperatorSymbol)op, t1, t2));
355        }
356
357        /**
358         * Binary operator applicability test - are the input types the same as the expected operand types?
359         */
360        boolean isBinaryOperatorApplicable(OperatorSymbol op, Type t1, Type t2) {
361            List<Type> formals = op.type.getParameterTypes();
362            return types.isSameType(formals.head, t1) &&
363                    types.isSameType(formals.tail.head, t2);
364        }
365
366        /**
367         * Adds a binary operator symbol.
368         */
369        final BinaryOperatorHelper addBinaryOperator(OperatorType arg1, OperatorType arg2, OperatorType res, int... opcode) {
370            operatorSuppliers = operatorSuppliers.prepend(() -> makeOperator(name, List.of(arg1, arg2), res, opcode));
371            return this;
372        }
373
374        /**
375         * This method will be overridden by binary operator helpers to provide custom resolution
376         * logic.
377         */
378        abstract Symbol resolve(Type t1, Type t2);
379    }
380
381    /**
382     * Class representing unary operator helpers that operate on reference types.
383     */
384    class UnaryReferenceOperator extends UnaryOperatorHelper {
385
386        UnaryReferenceOperator(Tag tag) {
387            super(tag);
388        }
389
390        @Override
391        public boolean test(Type type) {
392            return type.isNullOrReference();
393        }
394
395        @Override
396        public Symbol resolve(Type arg) {
397            return doLookup(syms.objectType);
398        }
399    }
400
401    /**
402     * Class representing unary operator helpers that operate on numeric types (either boxed or unboxed).
403     * Operator lookup is performed after applying numeric promotion of the input type.
404     */
405    class UnaryNumericOperator extends UnaryOperatorHelper {
406
407        UnaryNumericOperator(Tag tag) {
408            super(tag);
409        }
410
411        @Override
412        public boolean test(Type type) {
413            return unaryPromotion(type).isNumeric();
414        }
415
416        @Override
417        public Symbol resolve(Type arg) {
418            return doLookup(unaryPromotion(arg));
419        }
420    }
421
422    /**
423     * Class representing unary operator helpers that operate on boolean types  (either boxed or unboxed).
424     * Operator lookup is performed assuming the input type is a boolean type.
425     */
426    class UnaryBooleanOperator extends UnaryOperatorHelper {
427
428        UnaryBooleanOperator(Tag tag) {
429            super(tag);
430        }
431
432        @Override
433        public boolean test(Type type) {
434            return types.unboxedTypeOrType(type).hasTag(TypeTag.BOOLEAN);
435        }
436
437        @Override
438        public Symbol resolve(Type arg) {
439            return doLookup(syms.booleanType);
440        }
441    }
442
443    /**
444     * Class representing prefix/postfix unary operator helpers. Operates on numeric types (either
445     * boxed or unboxed). Operator lookup is performed on the unboxed version of the input type.
446     */
447    class UnaryPrefixPostfixOperator extends UnaryNumericOperator {
448
449        UnaryPrefixPostfixOperator(Tag tag) {
450            super(tag);
451        }
452
453        @Override
454        public Symbol resolve(Type arg) {
455            return doLookup(types.unboxedTypeOrType(arg));
456        }
457    }
458
459    /**
460     * Class representing binary operator helpers that operate on numeric types (either boxed or unboxed).
461     * Operator lookup is performed after applying binary numeric promotion of the input types.
462     */
463    class BinaryNumericOperator extends BinaryOperatorHelper {
464
465        BinaryNumericOperator(Tag tag) {
466            super(tag);
467        }
468
469        @Override
470        public Symbol resolve(Type arg1, Type arg2) {
471            Type t = binaryPromotion(arg1, arg2);
472            return doLookup(t, t);
473        }
474
475        @Override
476        public boolean test(Type arg1, Type arg2) {
477            return unaryPromotion(arg1).isNumeric() && unaryPromotion(arg2).isNumeric();
478        }
479    }
480
481    /**
482     * Class representing bitwise operator helpers that operate on boolean types (either boxed or unboxed).
483     * Operator lookup is performed assuming both input types are boolean types.
484     */
485    class BinaryBooleanOperator extends BinaryOperatorHelper {
486
487        BinaryBooleanOperator(Tag tag) {
488            super(tag);
489        }
490
491        @Override
492        public Symbol resolve(Type arg1, Type arg2) {
493            return doLookup(syms.booleanType, syms.booleanType);
494        }
495
496        @Override
497        public boolean test(Type arg1, Type arg2) {
498            return types.unboxedTypeOrType(arg1).hasTag(TypeTag.BOOLEAN) &&
499                    types.unboxedTypeOrType(arg2).hasTag(TypeTag.BOOLEAN);
500        }
501    }
502
503    /**
504     * Class representing string concatenation operator helper that operates on at least an
505     * string operand. Input types subject to an operator lookup undergoes a special string promotion
506     * (see {@link BinaryStringOperator#stringPromotion(Type)}.
507     */
508    class BinaryStringOperator extends BinaryOperatorHelper {
509
510        BinaryStringOperator(Tag tag) {
511            super(tag);
512        }
513
514        @Override
515        public Symbol resolve(Type arg1, Type arg2) {
516            return doLookup(stringPromotion(arg1), stringPromotion(arg2));
517        }
518
519        @Override
520        public boolean test(Type arg1, Type arg2) {
521            return types.isSameType(arg1, syms.stringType) ||
522                    types.isSameType(arg2, syms.stringType);
523        }
524
525        /**
526         * This routine applies following mappings:
527         * - if input type is primitive, apply numeric promotion
528         * - if input type is either 'null' or 'String' leave it untouched
529         * - otherwise return 'Object'
530         */
531        private Type stringPromotion(Type t) {
532            if (t.isPrimitive()) {
533                return unaryPromotion(t);
534            } else if (t.hasTag(TypeTag.BOT) ||
535                    types.isSameType(t, syms.stringType)) {
536                return t;
537            } else if (t.hasTag(TypeTag.TYPEVAR)) {
538                return stringPromotion(t.getUpperBound());
539            } else {
540                return syms.objectType;
541            }
542        }
543    }
544
545    /**
546     * Class representing shift operator helper that operates on integral operand types (either boxed
547     * or unboxed). Operator lookup is performed after applying unary numeric promotion to each input type.
548     */
549    class BinaryShiftOperator extends BinaryOperatorHelper {
550
551        BinaryShiftOperator(Tag tag) {
552            super(tag);
553        }
554
555        @Override
556        public Symbol resolve(Type arg1, Type arg2) {
557            return doLookup(unaryPromotion(arg1), unaryPromotion(arg2));
558        }
559
560        @Override
561        public boolean test(Type arg1, Type arg2) {
562            TypeTag op1 = unaryPromotion(arg1).getTag();
563            TypeTag op2 = unaryPromotion(arg2).getTag();
564            return (op1 == TypeTag.LONG || op1 == TypeTag.INT) &&
565                    (op2 == TypeTag.LONG || op2 == TypeTag.INT);
566        }
567    }
568
569    /**
570     * This enum represent the possible kinds of an comparison test ('==' and '!=').
571     */
572    enum ComparisonKind {
573        /** equality between numeric or boolean operands. */
574        NUMERIC_OR_BOOLEAN,
575        /** equality between reference operands. */
576        REFERENCE,
577        /** erroneous equality */
578        INVALID
579    }
580
581    /**
582     * Class representing equality operator helper that operates on either numeric, boolean or reference
583     * types. Operator lookup for numeric/boolean equality test is performed after binary numeric
584     * promotion to the input types. Operator lookup for reference equality test is performed assuming
585     * the input type is 'Object'.
586     */
587    class BinaryEqualityOperator extends BinaryOperatorHelper {
588
589        BinaryEqualityOperator(Tag tag) {
590            super(tag);
591        }
592
593        @Override
594        public boolean test(Type arg1, Type arg2) {
595            return getKind(arg1, arg2) != ComparisonKind.INVALID;
596        }
597
598        @Override
599        public Symbol resolve(Type t1, Type t2) {
600            ComparisonKind kind = getKind(t1, t2);
601            Type t = (kind == ComparisonKind.NUMERIC_OR_BOOLEAN) ?
602                    binaryPromotion(t1, t2) :
603                    syms.objectType;
604            return doLookup(t, t);
605        }
606
607        /**
608         * Retrieve the comparison kind associated with the given argument type pair.
609         */
610        private ComparisonKind getKind(Type arg1, Type arg2) {
611            boolean arg1Primitive = arg1.isPrimitive();
612            boolean arg2Primitive = arg2.isPrimitive();
613            if (arg1Primitive && arg2Primitive) {
614                return ComparisonKind.NUMERIC_OR_BOOLEAN;
615            } else if (arg1Primitive) {
616                return unaryPromotion(arg2).isPrimitive() ?
617                        ComparisonKind.NUMERIC_OR_BOOLEAN : ComparisonKind.INVALID;
618            } else if (arg2Primitive) {
619                return unaryPromotion(arg1).isPrimitive() ?
620                        ComparisonKind.NUMERIC_OR_BOOLEAN : ComparisonKind.INVALID;
621            } else {
622                return arg1.isNullOrReference() && arg2.isNullOrReference() ?
623                        ComparisonKind.REFERENCE : ComparisonKind.INVALID;
624            }
625        }
626    }
627
628    /**
629     * Initialize all unary operators.
630     */
631    private void initUnaryOperators() {
632        initOperators(unaryOperators,
633                new UnaryNumericOperator(Tag.POS)
634                        .addUnaryOperator(DOUBLE, DOUBLE, nop)
635                        .addUnaryOperator(FLOAT, FLOAT, nop)
636                        .addUnaryOperator(LONG, LONG, nop)
637                        .addUnaryOperator(INT, INT, nop),
638                new UnaryNumericOperator(Tag.NEG)
639                        .addUnaryOperator(DOUBLE, DOUBLE, dneg)
640                        .addUnaryOperator(FLOAT, FLOAT, fneg)
641                        .addUnaryOperator(LONG, LONG, lneg)
642                        .addUnaryOperator(INT, INT, ineg),
643                new UnaryNumericOperator(Tag.COMPL)
644                        .addUnaryOperator(LONG, LONG, lxor)
645                        .addUnaryOperator(INT, INT, ixor),
646                new UnaryPrefixPostfixOperator(Tag.POSTINC)
647                        .addUnaryOperator(DOUBLE, DOUBLE, dadd)
648                        .addUnaryOperator(FLOAT, FLOAT, fadd)
649                        .addUnaryOperator(LONG, LONG, ladd)
650                        .addUnaryOperator(INT, INT, iadd)
651                        .addUnaryOperator(CHAR, CHAR, iadd)
652                        .addUnaryOperator(SHORT, SHORT, iadd)
653                        .addUnaryOperator(BYTE, BYTE, iadd),
654                new UnaryPrefixPostfixOperator(Tag.POSTDEC)
655                        .addUnaryOperator(DOUBLE, DOUBLE, dsub)
656                        .addUnaryOperator(FLOAT, FLOAT, fsub)
657                        .addUnaryOperator(LONG, LONG, lsub)
658                        .addUnaryOperator(INT, INT, isub)
659                        .addUnaryOperator(CHAR, CHAR, isub)
660                        .addUnaryOperator(SHORT, SHORT, isub)
661                        .addUnaryOperator(BYTE, BYTE, isub),
662                new UnaryBooleanOperator(Tag.NOT)
663                        .addUnaryOperator(BOOLEAN, BOOLEAN, bool_not),
664                new UnaryReferenceOperator(Tag.NULLCHK)
665                        .addUnaryOperator(OBJECT, OBJECT, nullchk));
666    }
667
668    /**
669     * Initialize all binary operators.
670     */
671    private void initBinaryOperators() {
672        initOperators(binaryOperators,
673            new BinaryStringOperator(Tag.PLUS)
674                    .addBinaryOperator(STRING, OBJECT, STRING, string_add)
675                    .addBinaryOperator(OBJECT, STRING, STRING, string_add)
676                    .addBinaryOperator(STRING, STRING, STRING, string_add)
677                    .addBinaryOperator(STRING, INT, STRING, string_add)
678                    .addBinaryOperator(STRING, LONG, STRING, string_add)
679                    .addBinaryOperator(STRING, FLOAT, STRING, string_add)
680                    .addBinaryOperator(STRING, DOUBLE, STRING, string_add)
681                    .addBinaryOperator(STRING, BOOLEAN, STRING, string_add)
682                    .addBinaryOperator(STRING, BOT, STRING, string_add)
683                    .addBinaryOperator(INT, STRING, STRING, string_add)
684                    .addBinaryOperator(LONG, STRING, STRING, string_add)
685                    .addBinaryOperator(FLOAT, STRING, STRING, string_add)
686                    .addBinaryOperator(DOUBLE, STRING, STRING, string_add)
687                    .addBinaryOperator(BOOLEAN, STRING, STRING, string_add)
688                    .addBinaryOperator(BOT, STRING, STRING, string_add),
689            new BinaryNumericOperator(Tag.PLUS)
690                    .addBinaryOperator(DOUBLE, DOUBLE, DOUBLE, dadd)
691                    .addBinaryOperator(FLOAT, FLOAT, FLOAT, fadd)
692                    .addBinaryOperator(LONG, LONG, LONG, ladd)
693                    .addBinaryOperator(INT, INT, INT, iadd),
694            new BinaryNumericOperator(Tag.MINUS)
695                    .addBinaryOperator(DOUBLE, DOUBLE, DOUBLE, dsub)
696                    .addBinaryOperator(FLOAT, FLOAT, FLOAT, fsub)
697                    .addBinaryOperator(LONG, LONG, LONG, lsub)
698                    .addBinaryOperator(INT, INT, INT, isub),
699            new BinaryNumericOperator(Tag.MUL)
700                    .addBinaryOperator(DOUBLE, DOUBLE, DOUBLE, dmul)
701                    .addBinaryOperator(FLOAT, FLOAT, FLOAT, fmul)
702                    .addBinaryOperator(LONG, LONG, LONG, lmul)
703                    .addBinaryOperator(INT, INT, INT, imul),
704            new BinaryNumericOperator(Tag.DIV)
705                    .addBinaryOperator(DOUBLE, DOUBLE, DOUBLE, ddiv)
706                    .addBinaryOperator(FLOAT, FLOAT, FLOAT, fdiv)
707                    .addBinaryOperator(LONG, LONG, LONG, ldiv)
708                    .addBinaryOperator(INT, INT, INT, idiv),
709            new BinaryNumericOperator(Tag.MOD)
710                    .addBinaryOperator(DOUBLE, DOUBLE, DOUBLE, dmod)
711                    .addBinaryOperator(FLOAT, FLOAT, FLOAT, fmod)
712                    .addBinaryOperator(LONG, LONG, LONG, lmod)
713                    .addBinaryOperator(INT, INT, INT, imod),
714            new BinaryBooleanOperator(Tag.BITAND)
715                    .addBinaryOperator(BOOLEAN, BOOLEAN, BOOLEAN, iand),
716            new BinaryNumericOperator(Tag.BITAND)
717                    .addBinaryOperator(LONG, LONG, LONG, land)
718                    .addBinaryOperator(INT, INT, INT, iand),
719            new BinaryBooleanOperator(Tag.BITOR)
720                    .addBinaryOperator(BOOLEAN, BOOLEAN, BOOLEAN, ior),
721            new BinaryNumericOperator(Tag.BITOR)
722                    .addBinaryOperator(LONG, LONG, LONG, lor)
723                    .addBinaryOperator(INT, INT, INT, ior),
724            new BinaryBooleanOperator(Tag.BITXOR)
725                    .addBinaryOperator(BOOLEAN, BOOLEAN, BOOLEAN, ixor),
726            new BinaryNumericOperator(Tag.BITXOR)
727                    .addBinaryOperator(LONG, LONG, LONG, lxor)
728                    .addBinaryOperator(INT, INT, INT, ixor),
729            new BinaryShiftOperator(Tag.SL)
730                    .addBinaryOperator(INT, INT, INT, ishl)
731                    .addBinaryOperator(INT, LONG, INT, ishll)
732                    .addBinaryOperator(LONG, INT, LONG, lshl)
733                    .addBinaryOperator(LONG, LONG, LONG, lshll),
734            new BinaryShiftOperator(Tag.SR)
735                    .addBinaryOperator(INT, INT, INT, ishr)
736                    .addBinaryOperator(INT, LONG, INT, ishrl)
737                    .addBinaryOperator(LONG, INT, LONG, lshr)
738                    .addBinaryOperator(LONG, LONG, LONG, lshrl),
739            new BinaryShiftOperator(Tag.USR)
740                    .addBinaryOperator(INT, INT, INT, iushr)
741                    .addBinaryOperator(INT, LONG, INT, iushrl)
742                    .addBinaryOperator(LONG, INT, LONG, lushr)
743                    .addBinaryOperator(LONG, LONG, LONG, lushrl),
744            new BinaryNumericOperator(Tag.LT)
745                    .addBinaryOperator(DOUBLE, DOUBLE, BOOLEAN, dcmpg, iflt)
746                    .addBinaryOperator(FLOAT, FLOAT, BOOLEAN, fcmpg, iflt)
747                    .addBinaryOperator(LONG, LONG, BOOLEAN, lcmp, iflt)
748                    .addBinaryOperator(INT, INT, BOOLEAN, if_icmplt),
749            new BinaryNumericOperator(Tag.GT)
750                    .addBinaryOperator(DOUBLE, DOUBLE, BOOLEAN, dcmpl, ifgt)
751                    .addBinaryOperator(FLOAT, FLOAT, BOOLEAN, fcmpl, ifgt)
752                    .addBinaryOperator(LONG, LONG, BOOLEAN, lcmp, ifgt)
753                    .addBinaryOperator(INT, INT, BOOLEAN, if_icmpgt),
754            new BinaryNumericOperator(Tag.LE)
755                    .addBinaryOperator(DOUBLE, DOUBLE, BOOLEAN, dcmpg, ifle)
756                    .addBinaryOperator(FLOAT, FLOAT, BOOLEAN, fcmpg, ifle)
757                    .addBinaryOperator(LONG, LONG, BOOLEAN, lcmp, ifle)
758                    .addBinaryOperator(INT, INT, BOOLEAN, if_icmple),
759            new BinaryNumericOperator(Tag.GE)
760                    .addBinaryOperator(DOUBLE, DOUBLE, BOOLEAN, dcmpl, ifge)
761                    .addBinaryOperator(FLOAT, FLOAT, BOOLEAN, fcmpl, ifge)
762                    .addBinaryOperator(LONG, LONG, BOOLEAN, lcmp, ifge)
763                    .addBinaryOperator(INT, INT, BOOLEAN, if_icmpge),
764            new BinaryEqualityOperator(Tag.EQ)
765                    .addBinaryOperator(OBJECT, OBJECT, BOOLEAN, if_acmpeq)
766                    .addBinaryOperator(BOOLEAN, BOOLEAN, BOOLEAN, if_icmpeq)
767                    .addBinaryOperator(DOUBLE, DOUBLE, BOOLEAN, dcmpl, ifeq)
768                    .addBinaryOperator(FLOAT, FLOAT, BOOLEAN, fcmpl, ifeq)
769                    .addBinaryOperator(LONG, LONG, BOOLEAN, lcmp, ifeq)
770                    .addBinaryOperator(INT, INT, BOOLEAN, if_icmpeq),
771            new BinaryEqualityOperator(Tag.NE)
772                    .addBinaryOperator(OBJECT, OBJECT, BOOLEAN, if_acmpne)
773                    .addBinaryOperator(BOOLEAN, BOOLEAN, BOOLEAN, if_icmpne)
774                    .addBinaryOperator(DOUBLE, DOUBLE, BOOLEAN, dcmpl, ifne)
775                    .addBinaryOperator(FLOAT, FLOAT, BOOLEAN, fcmpl, ifne)
776                    .addBinaryOperator(LONG, LONG, BOOLEAN, lcmp, ifne)
777                    .addBinaryOperator(INT, INT, BOOLEAN, if_icmpne),
778            new BinaryBooleanOperator(Tag.AND)
779                    .addBinaryOperator(BOOLEAN, BOOLEAN, BOOLEAN, bool_and),
780            new BinaryBooleanOperator(Tag.OR)
781                    .addBinaryOperator(BOOLEAN, BOOLEAN, BOOLEAN, bool_or));
782    }
783
784    /**
785     * Complete the initialization of an operator helper by storing it into the corresponding operator map.
786     */
787    @SafeVarargs
788    private final <O extends OperatorHelper> void initOperators(Map<Name, List<O>> opsMap, O... ops) {
789        for (O o : ops) {
790            Name opName = o.name;
791            List<O> helpers = opsMap.getOrDefault(opName, List.nil());
792            opsMap.put(opName, helpers.prepend(o));
793        }
794    }
795
796    /**
797     * Initialize operator name array.
798     */
799    private void initOperatorNames() {
800        setOperatorName(Tag.POS, "+");
801        setOperatorName(Tag.NEG, "-");
802        setOperatorName(Tag.NOT, "!");
803        setOperatorName(Tag.COMPL, "~");
804        setOperatorName(Tag.PREINC, "++");
805        setOperatorName(Tag.PREDEC, "--");
806        setOperatorName(Tag.POSTINC, "++");
807        setOperatorName(Tag.POSTDEC, "--");
808        setOperatorName(Tag.NULLCHK, "<*nullchk*>");
809        setOperatorName(Tag.OR, "||");
810        setOperatorName(Tag.AND, "&&");
811        setOperatorName(Tag.EQ, "==");
812        setOperatorName(Tag.NE, "!=");
813        setOperatorName(Tag.LT, "<");
814        setOperatorName(Tag.GT, ">");
815        setOperatorName(Tag.LE, "<=");
816        setOperatorName(Tag.GE, ">=");
817        setOperatorName(Tag.BITOR, "|");
818        setOperatorName(Tag.BITXOR, "^");
819        setOperatorName(Tag.BITAND, "&");
820        setOperatorName(Tag.SL, "<<");
821        setOperatorName(Tag.SR, ">>");
822        setOperatorName(Tag.USR, ">>>");
823        setOperatorName(Tag.PLUS, "+");
824        setOperatorName(Tag.MINUS, names.hyphen);
825        setOperatorName(Tag.MUL, names.asterisk);
826        setOperatorName(Tag.DIV, names.slash);
827        setOperatorName(Tag.MOD, "%");
828    }
829    //where
830        private void setOperatorName(Tag tag, String name) {
831            setOperatorName(tag, names.fromString(name));
832        }
833
834        private void setOperatorName(Tag tag, Name name) {
835            opname[tag.operatorIndex()] = name;
836        }
837}
838