Operators.java revision 3541:9f8da995da88
1/*
2 * Copyright (c) 2015, 2016, 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        Predicate<Type> numericTest;
408
409        UnaryNumericOperator(Tag tag) {
410            this(tag, Type::isNumeric);
411        }
412
413        UnaryNumericOperator(Tag tag, Predicate<Type> numericTest) {
414            super(tag);
415            this.numericTest = numericTest;
416        }
417
418        @Override
419        public boolean test(Type type) {
420            return numericTest.test(unaryPromotion(type));
421        }
422
423        @Override
424        public Symbol resolve(Type arg) {
425            return doLookup(unaryPromotion(arg));
426        }
427    }
428
429    /**
430     * Class representing unary operator helpers that operate on boolean types  (either boxed or unboxed).
431     * Operator lookup is performed assuming the input type is a boolean type.
432     */
433    class UnaryBooleanOperator extends UnaryOperatorHelper {
434
435        UnaryBooleanOperator(Tag tag) {
436            super(tag);
437        }
438
439        @Override
440        public boolean test(Type type) {
441            return types.unboxedTypeOrType(type).hasTag(TypeTag.BOOLEAN);
442        }
443
444        @Override
445        public Symbol resolve(Type arg) {
446            return doLookup(syms.booleanType);
447        }
448    }
449
450    /**
451     * Class representing prefix/postfix unary operator helpers. Operates on numeric types (either
452     * boxed or unboxed). Operator lookup is performed on the unboxed version of the input type.
453     */
454    class UnaryPrefixPostfixOperator extends UnaryNumericOperator {
455
456        UnaryPrefixPostfixOperator(Tag tag) {
457            super(tag);
458        }
459
460        @Override
461        public Symbol resolve(Type arg) {
462            return doLookup(types.unboxedTypeOrType(arg));
463        }
464    }
465
466    /**
467     * Class representing binary operator helpers that operate on numeric types (either boxed or unboxed).
468     * Operator lookup is performed after applying binary numeric promotion of the input types.
469     */
470    class BinaryNumericOperator extends BinaryOperatorHelper {
471
472        Predicate<Type> numericTest;
473
474        BinaryNumericOperator(Tag tag) {
475            this(tag, Type::isNumeric);
476        }
477
478        BinaryNumericOperator(Tag tag, Predicate<Type> numericTest) {
479            super(tag);
480            this.numericTest = numericTest;
481        }
482
483        @Override
484        public Symbol resolve(Type arg1, Type arg2) {
485            Type t = binaryPromotion(arg1, arg2);
486            return doLookup(t, t);
487        }
488
489        @Override
490        public boolean test(Type arg1, Type arg2) {
491            return numericTest.test(unaryPromotion(arg1)) &&
492                    numericTest.test(unaryPromotion(arg2));
493        }
494    }
495
496    /**
497     * Class representing bitwise operator helpers that operate on boolean types (either boxed or unboxed).
498     * Operator lookup is performed assuming both input types are boolean types.
499     */
500    class BinaryBooleanOperator extends BinaryOperatorHelper {
501
502        BinaryBooleanOperator(Tag tag) {
503            super(tag);
504        }
505
506        @Override
507        public Symbol resolve(Type arg1, Type arg2) {
508            return doLookup(syms.booleanType, syms.booleanType);
509        }
510
511        @Override
512        public boolean test(Type arg1, Type arg2) {
513            return types.unboxedTypeOrType(arg1).hasTag(TypeTag.BOOLEAN) &&
514                    types.unboxedTypeOrType(arg2).hasTag(TypeTag.BOOLEAN);
515        }
516    }
517
518    /**
519     * Class representing string concatenation operator helper that operates on at least an
520     * string operand. Input types subject to an operator lookup undergoes a special string promotion
521     * (see {@link BinaryStringOperator#stringPromotion(Type)}.
522     */
523    class BinaryStringOperator extends BinaryOperatorHelper {
524
525        BinaryStringOperator(Tag tag) {
526            super(tag);
527        }
528
529        @Override
530        public Symbol resolve(Type arg1, Type arg2) {
531            return doLookup(stringPromotion(arg1), stringPromotion(arg2));
532        }
533
534        @Override
535        public boolean test(Type arg1, Type arg2) {
536            boolean hasStringOp = types.isSameType(arg1, syms.stringType) ||
537                    types.isSameType(arg2, syms.stringType);
538            boolean hasVoidOp = arg1.hasTag(TypeTag.VOID) || arg2.hasTag(TypeTag.VOID);
539            return hasStringOp && !hasVoidOp;
540        }
541
542        /**
543         * This routine applies following mappings:
544         * - if input type is primitive, apply numeric promotion
545         * - if input type is either 'void', 'null' or 'String' leave it untouched
546         * - otherwise return 'Object'
547         */
548        private Type stringPromotion(Type t) {
549            if (t.isPrimitive()) {
550                return unaryPromotion(t);
551            } else if (t.hasTag(TypeTag.VOID) || t.hasTag(TypeTag.BOT) ||
552                    types.isSameType(t, syms.stringType)) {
553                return t;
554            } else if (t.hasTag(TypeTag.TYPEVAR)) {
555                return stringPromotion(t.getUpperBound());
556            } else {
557                return syms.objectType;
558            }
559        }
560    }
561
562    /**
563     * Class representing shift operator helper that operates on integral operand types (either boxed
564     * or unboxed). Operator lookup is performed after applying unary numeric promotion to each input type.
565     */
566    class BinaryShiftOperator extends BinaryOperatorHelper {
567
568        BinaryShiftOperator(Tag tag) {
569            super(tag);
570        }
571
572        @Override
573        public Symbol resolve(Type arg1, Type arg2) {
574            return doLookup(unaryPromotion(arg1), unaryPromotion(arg2));
575        }
576
577        @Override
578        public boolean test(Type arg1, Type arg2) {
579            TypeTag op1 = unaryPromotion(arg1).getTag();
580            TypeTag op2 = unaryPromotion(arg2).getTag();
581            return (op1 == TypeTag.LONG || op1 == TypeTag.INT) &&
582                    (op2 == TypeTag.LONG || op2 == TypeTag.INT);
583        }
584    }
585
586    /**
587     * This enum represent the possible kinds of an comparison test ('==' and '!=').
588     */
589    enum ComparisonKind {
590        /** equality between numeric or boolean operands. */
591        NUMERIC_OR_BOOLEAN,
592        /** equality between reference operands. */
593        REFERENCE,
594        /** erroneous equality */
595        INVALID
596    }
597
598    /**
599     * Class representing equality operator helper that operates on either numeric, boolean or reference
600     * types. Operator lookup for numeric/boolean equality test is performed after binary numeric
601     * promotion to the input types. Operator lookup for reference equality test is performed assuming
602     * the input type is 'Object'.
603     */
604    class BinaryEqualityOperator extends BinaryOperatorHelper {
605
606        BinaryEqualityOperator(Tag tag) {
607            super(tag);
608        }
609
610        @Override
611        public boolean test(Type arg1, Type arg2) {
612            return getKind(arg1, arg2) != ComparisonKind.INVALID;
613        }
614
615        @Override
616        public Symbol resolve(Type t1, Type t2) {
617            ComparisonKind kind = getKind(t1, t2);
618            Type t = (kind == ComparisonKind.NUMERIC_OR_BOOLEAN) ?
619                    binaryPromotion(t1, t2) :
620                    syms.objectType;
621            return doLookup(t, t);
622        }
623
624        /**
625         * Retrieve the comparison kind associated with the given argument type pair.
626         */
627        private ComparisonKind getKind(Type arg1, Type arg2) {
628            boolean arg1Primitive = arg1.isPrimitive();
629            boolean arg2Primitive = arg2.isPrimitive();
630            if (arg1Primitive && arg2Primitive) {
631                return ComparisonKind.NUMERIC_OR_BOOLEAN;
632            } else if (arg1Primitive) {
633                return unaryPromotion(arg2).isPrimitive() ?
634                        ComparisonKind.NUMERIC_OR_BOOLEAN : ComparisonKind.INVALID;
635            } else if (arg2Primitive) {
636                return unaryPromotion(arg1).isPrimitive() ?
637                        ComparisonKind.NUMERIC_OR_BOOLEAN : ComparisonKind.INVALID;
638            } else {
639                return arg1.isNullOrReference() && arg2.isNullOrReference() ?
640                        ComparisonKind.REFERENCE : ComparisonKind.INVALID;
641            }
642        }
643    }
644
645    /**
646     * Initialize all unary operators.
647     */
648    private void initUnaryOperators() {
649        initOperators(unaryOperators,
650                new UnaryNumericOperator(Tag.POS)
651                        .addUnaryOperator(DOUBLE, DOUBLE, nop)
652                        .addUnaryOperator(FLOAT, FLOAT, nop)
653                        .addUnaryOperator(LONG, LONG, nop)
654                        .addUnaryOperator(INT, INT, nop),
655                new UnaryNumericOperator(Tag.NEG)
656                        .addUnaryOperator(DOUBLE, DOUBLE, dneg)
657                        .addUnaryOperator(FLOAT, FLOAT, fneg)
658                        .addUnaryOperator(LONG, LONG, lneg)
659                        .addUnaryOperator(INT, INT, ineg),
660                new UnaryNumericOperator(Tag.COMPL, Type::isIntegral)
661                        .addUnaryOperator(LONG, LONG, lxor)
662                        .addUnaryOperator(INT, INT, ixor),
663                new UnaryPrefixPostfixOperator(Tag.POSTINC)
664                        .addUnaryOperator(DOUBLE, DOUBLE, dadd)
665                        .addUnaryOperator(FLOAT, FLOAT, fadd)
666                        .addUnaryOperator(LONG, LONG, ladd)
667                        .addUnaryOperator(INT, INT, iadd)
668                        .addUnaryOperator(CHAR, CHAR, iadd)
669                        .addUnaryOperator(SHORT, SHORT, iadd)
670                        .addUnaryOperator(BYTE, BYTE, iadd),
671                new UnaryPrefixPostfixOperator(Tag.POSTDEC)
672                        .addUnaryOperator(DOUBLE, DOUBLE, dsub)
673                        .addUnaryOperator(FLOAT, FLOAT, fsub)
674                        .addUnaryOperator(LONG, LONG, lsub)
675                        .addUnaryOperator(INT, INT, isub)
676                        .addUnaryOperator(CHAR, CHAR, isub)
677                        .addUnaryOperator(SHORT, SHORT, isub)
678                        .addUnaryOperator(BYTE, BYTE, isub),
679                new UnaryBooleanOperator(Tag.NOT)
680                        .addUnaryOperator(BOOLEAN, BOOLEAN, bool_not),
681                new UnaryReferenceOperator(Tag.NULLCHK)
682                        .addUnaryOperator(OBJECT, OBJECT, nullchk));
683    }
684
685    /**
686     * Initialize all binary operators.
687     */
688    private void initBinaryOperators() {
689        initOperators(binaryOperators,
690            new BinaryStringOperator(Tag.PLUS)
691                    .addBinaryOperator(STRING, OBJECT, STRING, string_add)
692                    .addBinaryOperator(OBJECT, STRING, STRING, string_add)
693                    .addBinaryOperator(STRING, STRING, STRING, string_add)
694                    .addBinaryOperator(STRING, INT, STRING, string_add)
695                    .addBinaryOperator(STRING, LONG, STRING, string_add)
696                    .addBinaryOperator(STRING, FLOAT, STRING, string_add)
697                    .addBinaryOperator(STRING, DOUBLE, STRING, string_add)
698                    .addBinaryOperator(STRING, BOOLEAN, STRING, string_add)
699                    .addBinaryOperator(STRING, BOT, STRING, string_add)
700                    .addBinaryOperator(INT, STRING, STRING, string_add)
701                    .addBinaryOperator(LONG, STRING, STRING, string_add)
702                    .addBinaryOperator(FLOAT, STRING, STRING, string_add)
703                    .addBinaryOperator(DOUBLE, STRING, STRING, string_add)
704                    .addBinaryOperator(BOOLEAN, STRING, STRING, string_add)
705                    .addBinaryOperator(BOT, STRING, STRING, string_add),
706            new BinaryNumericOperator(Tag.PLUS)
707                    .addBinaryOperator(DOUBLE, DOUBLE, DOUBLE, dadd)
708                    .addBinaryOperator(FLOAT, FLOAT, FLOAT, fadd)
709                    .addBinaryOperator(LONG, LONG, LONG, ladd)
710                    .addBinaryOperator(INT, INT, INT, iadd),
711            new BinaryNumericOperator(Tag.MINUS)
712                    .addBinaryOperator(DOUBLE, DOUBLE, DOUBLE, dsub)
713                    .addBinaryOperator(FLOAT, FLOAT, FLOAT, fsub)
714                    .addBinaryOperator(LONG, LONG, LONG, lsub)
715                    .addBinaryOperator(INT, INT, INT, isub),
716            new BinaryNumericOperator(Tag.MUL)
717                    .addBinaryOperator(DOUBLE, DOUBLE, DOUBLE, dmul)
718                    .addBinaryOperator(FLOAT, FLOAT, FLOAT, fmul)
719                    .addBinaryOperator(LONG, LONG, LONG, lmul)
720                    .addBinaryOperator(INT, INT, INT, imul),
721            new BinaryNumericOperator(Tag.DIV)
722                    .addBinaryOperator(DOUBLE, DOUBLE, DOUBLE, ddiv)
723                    .addBinaryOperator(FLOAT, FLOAT, FLOAT, fdiv)
724                    .addBinaryOperator(LONG, LONG, LONG, ldiv)
725                    .addBinaryOperator(INT, INT, INT, idiv),
726            new BinaryNumericOperator(Tag.MOD)
727                    .addBinaryOperator(DOUBLE, DOUBLE, DOUBLE, dmod)
728                    .addBinaryOperator(FLOAT, FLOAT, FLOAT, fmod)
729                    .addBinaryOperator(LONG, LONG, LONG, lmod)
730                    .addBinaryOperator(INT, INT, INT, imod),
731            new BinaryBooleanOperator(Tag.BITAND)
732                    .addBinaryOperator(BOOLEAN, BOOLEAN, BOOLEAN, iand),
733            new BinaryNumericOperator(Tag.BITAND, Type::isIntegral)
734                    .addBinaryOperator(LONG, LONG, LONG, land)
735                    .addBinaryOperator(INT, INT, INT, iand),
736            new BinaryBooleanOperator(Tag.BITOR)
737                    .addBinaryOperator(BOOLEAN, BOOLEAN, BOOLEAN, ior),
738            new BinaryNumericOperator(Tag.BITOR, Type::isIntegral)
739                    .addBinaryOperator(LONG, LONG, LONG, lor)
740                    .addBinaryOperator(INT, INT, INT, ior),
741            new BinaryBooleanOperator(Tag.BITXOR)
742                    .addBinaryOperator(BOOLEAN, BOOLEAN, BOOLEAN, ixor),
743            new BinaryNumericOperator(Tag.BITXOR, Type::isIntegral)
744                    .addBinaryOperator(LONG, LONG, LONG, lxor)
745                    .addBinaryOperator(INT, INT, INT, ixor),
746            new BinaryShiftOperator(Tag.SL)
747                    .addBinaryOperator(INT, INT, INT, ishl)
748                    .addBinaryOperator(INT, LONG, INT, ishll)
749                    .addBinaryOperator(LONG, INT, LONG, lshl)
750                    .addBinaryOperator(LONG, LONG, LONG, lshll),
751            new BinaryShiftOperator(Tag.SR)
752                    .addBinaryOperator(INT, INT, INT, ishr)
753                    .addBinaryOperator(INT, LONG, INT, ishrl)
754                    .addBinaryOperator(LONG, INT, LONG, lshr)
755                    .addBinaryOperator(LONG, LONG, LONG, lshrl),
756            new BinaryShiftOperator(Tag.USR)
757                    .addBinaryOperator(INT, INT, INT, iushr)
758                    .addBinaryOperator(INT, LONG, INT, iushrl)
759                    .addBinaryOperator(LONG, INT, LONG, lushr)
760                    .addBinaryOperator(LONG, LONG, LONG, lushrl),
761            new BinaryNumericOperator(Tag.LT)
762                    .addBinaryOperator(DOUBLE, DOUBLE, BOOLEAN, dcmpg, iflt)
763                    .addBinaryOperator(FLOAT, FLOAT, BOOLEAN, fcmpg, iflt)
764                    .addBinaryOperator(LONG, LONG, BOOLEAN, lcmp, iflt)
765                    .addBinaryOperator(INT, INT, BOOLEAN, if_icmplt),
766            new BinaryNumericOperator(Tag.GT)
767                    .addBinaryOperator(DOUBLE, DOUBLE, BOOLEAN, dcmpl, ifgt)
768                    .addBinaryOperator(FLOAT, FLOAT, BOOLEAN, fcmpl, ifgt)
769                    .addBinaryOperator(LONG, LONG, BOOLEAN, lcmp, ifgt)
770                    .addBinaryOperator(INT, INT, BOOLEAN, if_icmpgt),
771            new BinaryNumericOperator(Tag.LE)
772                    .addBinaryOperator(DOUBLE, DOUBLE, BOOLEAN, dcmpg, ifle)
773                    .addBinaryOperator(FLOAT, FLOAT, BOOLEAN, fcmpg, ifle)
774                    .addBinaryOperator(LONG, LONG, BOOLEAN, lcmp, ifle)
775                    .addBinaryOperator(INT, INT, BOOLEAN, if_icmple),
776            new BinaryNumericOperator(Tag.GE)
777                    .addBinaryOperator(DOUBLE, DOUBLE, BOOLEAN, dcmpl, ifge)
778                    .addBinaryOperator(FLOAT, FLOAT, BOOLEAN, fcmpl, ifge)
779                    .addBinaryOperator(LONG, LONG, BOOLEAN, lcmp, ifge)
780                    .addBinaryOperator(INT, INT, BOOLEAN, if_icmpge),
781            new BinaryEqualityOperator(Tag.EQ)
782                    .addBinaryOperator(OBJECT, OBJECT, BOOLEAN, if_acmpeq)
783                    .addBinaryOperator(BOOLEAN, BOOLEAN, BOOLEAN, if_icmpeq)
784                    .addBinaryOperator(DOUBLE, DOUBLE, BOOLEAN, dcmpl, ifeq)
785                    .addBinaryOperator(FLOAT, FLOAT, BOOLEAN, fcmpl, ifeq)
786                    .addBinaryOperator(LONG, LONG, BOOLEAN, lcmp, ifeq)
787                    .addBinaryOperator(INT, INT, BOOLEAN, if_icmpeq),
788            new BinaryEqualityOperator(Tag.NE)
789                    .addBinaryOperator(OBJECT, OBJECT, BOOLEAN, if_acmpne)
790                    .addBinaryOperator(BOOLEAN, BOOLEAN, BOOLEAN, if_icmpne)
791                    .addBinaryOperator(DOUBLE, DOUBLE, BOOLEAN, dcmpl, ifne)
792                    .addBinaryOperator(FLOAT, FLOAT, BOOLEAN, fcmpl, ifne)
793                    .addBinaryOperator(LONG, LONG, BOOLEAN, lcmp, ifne)
794                    .addBinaryOperator(INT, INT, BOOLEAN, if_icmpne),
795            new BinaryBooleanOperator(Tag.AND)
796                    .addBinaryOperator(BOOLEAN, BOOLEAN, BOOLEAN, bool_and),
797            new BinaryBooleanOperator(Tag.OR)
798                    .addBinaryOperator(BOOLEAN, BOOLEAN, BOOLEAN, bool_or));
799    }
800
801    Symbol lookupBinaryOp(Predicate<Symbol> applicabilityTest) {
802        return binaryOperators.values().stream()
803                .flatMap(List::stream)
804                .map(helper -> helper.doLookup(applicabilityTest))
805                .distinct()
806                .filter(sym -> sym != syms.noSymbol)
807                .findFirst().get();
808    }
809
810    /**
811     * Complete the initialization of an operator helper by storing it into the corresponding operator map.
812     */
813    @SafeVarargs
814    private final <O extends OperatorHelper> void initOperators(Map<Name, List<O>> opsMap, O... ops) {
815        for (O o : ops) {
816            Name opName = o.name;
817            List<O> helpers = opsMap.getOrDefault(opName, List.nil());
818            opsMap.put(opName, helpers.prepend(o));
819        }
820    }
821
822    /**
823     * Initialize operator name array.
824     */
825    private void initOperatorNames() {
826        setOperatorName(Tag.POS, "+");
827        setOperatorName(Tag.NEG, "-");
828        setOperatorName(Tag.NOT, "!");
829        setOperatorName(Tag.COMPL, "~");
830        setOperatorName(Tag.PREINC, "++");
831        setOperatorName(Tag.PREDEC, "--");
832        setOperatorName(Tag.POSTINC, "++");
833        setOperatorName(Tag.POSTDEC, "--");
834        setOperatorName(Tag.NULLCHK, "<*nullchk*>");
835        setOperatorName(Tag.OR, "||");
836        setOperatorName(Tag.AND, "&&");
837        setOperatorName(Tag.EQ, "==");
838        setOperatorName(Tag.NE, "!=");
839        setOperatorName(Tag.LT, "<");
840        setOperatorName(Tag.GT, ">");
841        setOperatorName(Tag.LE, "<=");
842        setOperatorName(Tag.GE, ">=");
843        setOperatorName(Tag.BITOR, "|");
844        setOperatorName(Tag.BITXOR, "^");
845        setOperatorName(Tag.BITAND, "&");
846        setOperatorName(Tag.SL, "<<");
847        setOperatorName(Tag.SR, ">>");
848        setOperatorName(Tag.USR, ">>>");
849        setOperatorName(Tag.PLUS, "+");
850        setOperatorName(Tag.MINUS, names.hyphen);
851        setOperatorName(Tag.MUL, names.asterisk);
852        setOperatorName(Tag.DIV, names.slash);
853        setOperatorName(Tag.MOD, "%");
854    }
855    //where
856        private void setOperatorName(Tag tag, String name) {
857            setOperatorName(tag, names.fromString(name));
858        }
859
860        private void setOperatorName(Tag tag, Name name) {
861            opname[tag.operatorIndex()] = name;
862        }
863}
864