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