Operators.java revision 3053:79e637c1e083
11638Srgrimes/*
252655Sphantom * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
31638Srgrimes * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
474856Sru *
579129Smsmith * This code is free software; you can redistribute it and/or modify it
666693Sscottl * under the terms of the GNU General Public License version 2 only, as
763711Ssheldonh * published by the Free Software Foundation.  Oracle designates this
884533Syar * particular file as subject to the "Classpath" exception as provided
963711Ssheldonh * by Oracle in the LICENSE file that accompanied this code.
1063711Ssheldonh *
1163711Ssheldonh * This code is distributed in the hope that it will be useful, but WITHOUT
12102768Sscottl * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13105860Sdes * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1463711Ssheldonh * version 2 for more details (a copy is included in the LICENSE file that
1563711Ssheldonh * accompanied this code).
1663711Ssheldonh *
17113769Sobrien * You should have received a copy of the GNU General Public License version
1863711Ssheldonh * 2 along with this work; if not, write to the Free Software Foundation,
19106248Sthomas * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
2063711Ssheldonh *
2163711Ssheldonh * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22116761Ssam * or visit www.oracle.com if you need additional information or have any
23116761Ssam * questions.
2463711Ssheldonh */
2578821Sonoe
26113757Swpaulpackage com.sun.tools.javac.comp;
2784059Swpaul
2863711Ssheldonhimport com.sun.tools.javac.code.Symbol;
2963711Ssheldonhimport com.sun.tools.javac.code.Symbol.OperatorSymbol;
3063711Ssheldonhimport com.sun.tools.javac.code.Symtab;
3163711Ssheldonhimport com.sun.tools.javac.code.Type;
3263711Ssheldonhimport com.sun.tools.javac.code.Type.MethodType;
3399639Simpimport com.sun.tools.javac.code.TypeTag;
3463711Ssheldonhimport com.sun.tools.javac.code.Types;
3563711Ssheldonhimport com.sun.tools.javac.jvm.ByteCodes;
3663711Ssheldonhimport com.sun.tools.javac.resources.CompilerProperties.Errors;
37108472Strhodesimport com.sun.tools.javac.tree.JCTree;
38104488Ssamimport com.sun.tools.javac.tree.JCTree.Tag;
3963711Ssheldonhimport com.sun.tools.javac.util.Assert;
4063711Ssheldonhimport com.sun.tools.javac.util.Context;
4163711Ssheldonhimport com.sun.tools.javac.util.JCDiagnostic;
4263711Ssheldonhimport com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
4363711Ssheldonhimport com.sun.tools.javac.util.List;
4463711Ssheldonhimport com.sun.tools.javac.util.Log;
45107759Simpimport com.sun.tools.javac.util.Name;
4697452Sbrooksimport com.sun.tools.javac.util.Names;
4763711Ssheldonh
4863711Ssheldonhimport java.util.HashMap;
4963711Ssheldonhimport java.util.Map;
5063711Ssheldonhimport java.util.Optional;
5163726Ssheldonhimport java.util.function.BiPredicate;
52115140Strhodesimport java.util.function.Function;
5387191Spdeuskarimport java.util.function.Predicate;
54115766Shartiimport java.util.function.Supplier;
55111375Simpimport java.util.stream.Stream;
5663711Ssheldonh
57109614Ssamimport static com.sun.tools.javac.jvm.ByteCodes.*;
58116735Shartiimport static com.sun.tools.javac.comp.Operators.OperatorType.*;
5963711Ssheldonh
6063711Ssheldonh/**
61105934Ssimokawa * This class contains the logic for unary and binary operator resolution/lookup.
62117538Sobrien *
6363711Ssheldonh * <p><b>This is NOT part of any supported API.
64106611Ssimokawa * If you write code that depends on this, you do so at your own risk.
65105934Ssimokawa * This code and its internal interfaces are subject to change or
6663711Ssheldonh * deletion without notice.</b>
67105483Sphk */
68116166Stmmpublic class Operators {
6993274Sphk    protected static final Context.Key<Operators> operatorsKey = new Context.Key<>();
7063711Ssheldonh
71103027Ssobomax    private final Names names;
7263711Ssheldonh    private final Log log;
7385134Sjlemon    private final Symtab syms;
74116492Sharti    private final Types types;
75104488Ssam
76116166Stmm    /** Unary operators map. */
7768809Sarchie    private Map<Name, List<UnaryOperatorHelper>> unaryOperators = new HashMap<>(Tag.getNumberOfOperators());
7863711Ssheldonh
7963711Ssheldonh    /** Binary operators map. */
80108768Strhodes    private Map<Name, List<BinaryOperatorHelper>> binaryOperators = new HashMap<>(Tag.getNumberOfOperators());
8177217Sphk
8263711Ssheldonh    /** The names of all operators. */
8363711Ssheldonh    private Name[] opname = new Name[Tag.getNumberOfOperators()];
8463711Ssheldonh
8563711Ssheldonh    public static Operators instance(Context context) {
8663711Ssheldonh        Operators instance = context.get(operatorsKey);
87108768Strhodes        if (instance == null)
8863711Ssheldonh            instance = new Operators(context);
8963711Ssheldonh        return instance;
9063711Ssheldonh    }
9163711Ssheldonh
9263711Ssheldonh    protected Operators(Context context) {
9363711Ssheldonh        context.put(operatorsKey, this);
9463711Ssheldonh        syms = Symtab.instance(context);
9563711Ssheldonh        names = Names.instance(context);
9663711Ssheldonh        log = Log.instance(context);
9763711Ssheldonh        types = Types.instance(context);
9863711Ssheldonh        initOperatorNames();
9963711Ssheldonh        initUnaryOperators();
10063711Ssheldonh        initBinaryOperators();
10163711Ssheldonh    }
10263711Ssheldonh
10374013Sjhb    /**
10477542Swpaul     * Perform unary promotion of a type; this routine implements JLS 5.6.1.
105108699Sobrien     * If the input type is not supported by unary promotion, it is returned unaltered.
10663711Ssheldonh     */
10786698Sgreen    Type unaryPromotion(Type t) {
10863711Ssheldonh        Type unboxed = types.unboxedTypeOrType(t);
10963711Ssheldonh        switch (unboxed.getTag()) {
11063711Ssheldonh            case BYTE:
11184961Sscottl            case SHORT:
11263711Ssheldonh            case CHAR:
11363711Ssheldonh                return syms.intType;
114110940Strhodes            default:
11563711Ssheldonh                return unboxed;
11664988Smsmith        }
117109270Schris    }
118107626Schris
119107626Schris    /**
120107735Schris     * Perform binary promotion of a pair of types; this routine implements JLS 5.6.2.
121108933Schris     * If the input types are not supported by unary promotion, if such types are identical to
122107626Schris     * a type C, then C is returned, otherwise Object is returned.
123107717Schris     */
124107739Schris    Type binaryPromotion(Type t1, Type t2) {
125112860Schris        Type unboxedT1 = types.unboxedTypeOrType(t1);
126107717Schris        Type unboxedT2 = types.unboxedTypeOrType(t2);
127107717Schris
12863711Ssheldonh        if (unboxedT1.isNumeric() && unboxedT2.isNumeric()) {
12963711Ssheldonh            if (unboxedT1.hasTag(TypeTag.DOUBLE) || unboxedT2.hasTag(TypeTag.DOUBLE)) {
130115138Shmp                return syms.doubleType;
13163711Ssheldonh            } else if (unboxedT1.hasTag(TypeTag.FLOAT) || unboxedT2.hasTag(TypeTag.FLOAT)) {
132116296Sharti                return syms.floatType;
13363711Ssheldonh            } else if (unboxedT1.hasTag(TypeTag.LONG) || unboxedT2.hasTag(TypeTag.LONG)) {
13463714Ssheldonh                return syms.longType;
13563711Ssheldonh            } else {
136116810Sharti                return syms.intType;
13763714Ssheldonh            }
13863714Ssheldonh        } else if (types.isSameType(unboxedT1, unboxedT2)) {
139107122Sjulian            return unboxedT1;
14063714Ssheldonh        } else {
14165311Sarchie            return syms.objectType;
142107122Sjulian        }
143107122Sjulian    }
14463714Ssheldonh
14563714Ssheldonh    /**
14694317Sjulian     * Entry point for resolving a unary operator given an operator tag and an argument type.
14763711Ssheldonh     */
14863714Ssheldonh    Symbol resolveUnary(DiagnosticPosition pos, JCTree.Tag tag, Type op) {
14983998Sbrooks        return resolve(tag,
150107122Sjulian                unaryOperators,
151107122Sjulian                unop -> unop.test(op),
15263714Ssheldonh                unop -> unop.resolve(op),
15363714Ssheldonh                () -> reportErrorIfNeeded(pos, tag, op));
15484053Sbrooks    }
15563714Ssheldonh
156107122Sjulian    /**
157102196Sarchie     * Entry point for resolving a binary operator given an operator tag and a pair of argument types.
15863714Ssheldonh     */
15963714Ssheldonh    Symbol resolveBinary(DiagnosticPosition pos, JCTree.Tag tag, Type op1, Type op2) {
16068810Sarchie        return resolve(tag,
16163714Ssheldonh                binaryOperators,
16263714Ssheldonh                binop -> binop.test(op1, op2),
16363714Ssheldonh                binop -> binop.resolve(op1, op2),
16463714Ssheldonh                () -> reportErrorIfNeeded(pos, tag, op1, op2));
16563714Ssheldonh    }
16663714Ssheldonh
16763714Ssheldonh    /**
168107122Sjulian     * Main operator lookup routine; lookup an operator (either unary or binary) in its corresponding
16963714Ssheldonh     * map. If there's a matching operator, its resolve routine is called and the result is returned;
17076479Swpaul     * otherwise the result of a fallback function is returned.
17179417Sjulian     */
17263711Ssheldonh    private <O> Symbol resolve(Tag tag, Map<Name, List<O>> opMap, Predicate<O> opTestFunc,
17363711Ssheldonh                       Function<O, Symbol> resolveFunc, Supplier<Symbol> noResultFunc) {
17499635Simp        return opMap.get(operatorName(tag)).stream()
17577597Simp                .filter(opTestFunc)
17663711Ssheldonh                .map(resolveFunc)
177117633Sharti                .findFirst()
17863711Ssheldonh                .orElseGet(noResultFunc);
17999639Simp    }
18099639Simp
18182095Simp    /**
18263711Ssheldonh     * Creates an operator symbol.
18366131Swpaul     */
18463711Ssheldonh    private Symbol makeOperator(Name name, List<OperatorType> formals, OperatorType res, int... opcodes) {
18590687Sluigi        MethodType opType = new MethodType(
18663711Ssheldonh                formals.stream()
18763711Ssheldonh                        .map(o -> o.asType(syms))
18863711Ssheldonh                        .collect(List.collector()),
18963711Ssheldonh                res.asType(syms), List.nil(), syms.methodClass);
19063711Ssheldonh        return new OperatorSymbol(name, opType, mergeOpcodes(opcodes), syms.noSymbol);
191104041Ssos    }
19263711Ssheldonh
19363711Ssheldonh    /**
19490731Sjhay     * Fold two opcodes in a single int value (if required).
195105503Sscottl     */
19672671Smarkm    private int mergeOpcodes(int... opcodes) {
19763711Ssheldonh        int opcodesLen = opcodes.length;
198112102Ssam        Assert.check(opcodesLen == 1 || opcodesLen == 2);
19963711Ssheldonh        return (opcodesLen == 1) ?
20083113Sobrien                opcodes[0] :
201114577Sakiyama                ((opcodes[0] << ByteCodes.preShift) | opcodes[1]);
20263711Ssheldonh    }
20363711Ssheldonh
204105934Ssimokawa    /**
205113429Sfjoe     * Report an operator lookup error.
20663711Ssheldonh     */
20763711Ssheldonh    private Symbol reportErrorIfNeeded(DiagnosticPosition pos, Tag tag, Type... args) {
20863711Ssheldonh        if (Stream.of(args).noneMatch(Type::isErroneous)) {
209109218Stjr            Name opName = operatorName(tag);
21063711Ssheldonh            JCDiagnostic.Error opError = (args.length) == 1 ?
21163711Ssheldonh                    Errors.OperatorCantBeApplied(opName, args[0]) :
21263711Ssheldonh                    Errors.OperatorCantBeApplied1(opName, args[0], args[1]);
21363711Ssheldonh            log.error(pos, opError);
21463711Ssheldonh        }
21563711Ssheldonh        return syms.noSymbol;
21663711Ssheldonh    }
21763711Ssheldonh
21863711Ssheldonh    /**
21963711Ssheldonh     * Return name of operator with given tree tag.
22068480Simp     */
22163711Ssheldonh    public Name operatorName(JCTree.Tag tag) {
22297010Swill        return opname[tag.operatorIndex()];
22363711Ssheldonh    }
22463711Ssheldonh
22563711Ssheldonh    /**
22663711Ssheldonh     * The constants in this enum represent the types upon which all the operator helpers
22763711Ssheldonh     * operate upon. This allows lazy and consise mapping between a type name and a type instance.
22863711Ssheldonh     */
229107191Sru    enum OperatorType {
23063711Ssheldonh        BYTE(syms -> syms.byteType),
23163711Ssheldonh        SHORT(syms -> syms.shortType),
23263711Ssheldonh        INT(syms -> syms.intType),
23363711Ssheldonh        LONG(syms -> syms.longType),
234107238Snjl        FLOAT(syms -> syms.floatType),
23563711Ssheldonh        DOUBLE(syms -> syms.doubleType),
23674047Sphk        CHAR(syms -> syms.charType),
23763711Ssheldonh        BOOLEAN(syms -> syms.booleanType),
23863711Ssheldonh        OBJECT(syms -> syms.objectType),
23963711Ssheldonh        STRING(syms -> syms.stringType),
240107945Scognet        BOT(syms -> syms.botType);
24163711Ssheldonh
24263711Ssheldonh        final Function<Symtab, Type> asTypeFunc;
24363711Ssheldonh
24463711Ssheldonh        OperatorType(Function<Symtab, Type> asTypeFunc) {
24580219Swpaul            this.asTypeFunc = asTypeFunc;
246107754Skan        }
247104488Ssam
248114878Sjulian        Type asType(Symtab syms) {
24995066Strhodes            return asTypeFunc.apply(syms);
25063711Ssheldonh        }
25163711Ssheldonh    }
252101703Sjoe
25386659Sjoe    /**
25463711Ssheldonh     * Common root for all operator helpers. An operator helper instance is associated with a
25595066Strhodes     * given operator (i.e. '+'); it contains routines to perform operator lookup, i.e. find
25695066Strhodes     * which version of the '+' operator is the best given an argument type list. Supported
25763711Ssheldonh     * operator symbols are initialized lazily upon first lookup request - this is in order to avoid
25895066Strhodes     * initialization circularities between this class and {@code Symtab}.
25963711Ssheldonh     */
26095066Strhodes    abstract class OperatorHelper {
26163711Ssheldonh
26263711Ssheldonh        /** The operator name. */
26395066Strhodes        final Name name;
26463711Ssheldonh
26563711Ssheldonh        /** The list of symbols associated with this operator (lazily populated). */
26667628Sasmodai        Optional<Symbol[]> alternatives = Optional.empty();
267116258Sharti
268101766Strhodes        /** An array of operator symbol suppliers (used to lazily populate the symbol list). */
26995066Strhodes        List<Supplier<Symbol>> operatorSuppliers = List.nil();
27063711Ssheldonh
27163711Ssheldonh        @SuppressWarnings("varargs")
27280494Syar        OperatorHelper(Tag tag) {
27363711Ssheldonh            this.name = operatorName(tag);
27463711Ssheldonh        }
275116874Ssmkelly
27663711Ssheldonh        /**
27763711Ssheldonh         * This routine implements the main operator lookup process. Each operator is tested
27874931Sjhb         * using an applicability predicate; if the test suceeds that same operator is returned,
279113135Strhodes         * otherwise a dummy symbol is returned.
28063711Ssheldonh         */
28163711Ssheldonh        final Symbol doLookup(Predicate<Symbol> applicabilityTest) {
28263711Ssheldonh            return Stream.of(alternatives.orElseGet(this::initOperators))
28379727Sschweikh                    .filter(applicabilityTest)
28415136Smpp                    .findFirst()
28584533Syar                    .orElse(syms.noSymbol);
286110961Strhodes        }
28784533Syar
288110961Strhodes        /**
289110961Strhodes         * This routine performs lazy instantiation of the operator symbols supported by this helper.
290110961Strhodes         * After initialization is done, the suppliers are cleared, to free up memory.
29162586Sroberto         */
292110961Strhodes        private Symbol[] initOperators() {
293110961Strhodes            Symbol[] operators = operatorSuppliers.stream()
294110961Strhodes                    .map(op -> op.get())
295110961Strhodes                    .toArray(Symbol[]::new);
296110961Strhodes            alternatives = Optional.of(operators);
297110961Strhodes            operatorSuppliers = null; //let GC do its work
298110961Strhodes            return operators;
299115766Sharti        }
300110961Strhodes    }
301116735Sharti
30260990Sobrien    /**
30345678Sbde     * Common superclass for all unary operator helpers.
304110961Strhodes     */
305110961Strhodes    abstract class UnaryOperatorHelper extends OperatorHelper implements Predicate<Type> {
306116166Stmm
307110961Strhodes        UnaryOperatorHelper(Tag tag) {
308110961Strhodes            super(tag);
309110961Strhodes        }
310116492Sharti
311116166Stmm        /**
31245678Sbde         * This routine implements the unary operator lookup process. It customizes the behavior
313110961Strhodes         * of the shared lookup routine in {@link OperatorHelper}, by using an unary applicability test
314110961Strhodes         * (see {@link UnaryOperatorHelper#isUnaryOperatorApplicable(OperatorSymbol, Type)}
31588988Sdd         */
31645678Sbde        final Symbol doLookup(Type t) {
31774047Sphk            return doLookup(op -> isUnaryOperatorApplicable((OperatorSymbol)op, t));
31853197Sphantom        }
3191638Srgrimes
320110961Strhodes        /**
321112738Simp         * Unary operator applicability test - is the input type the same as the expected operand type?
322117633Sharti         */
323115880Sru        boolean isUnaryOperatorApplicable(OperatorSymbol op, Type t) {
32463665Ssheldonh            return types.isSameType(op.type.getParameterTypes().head, t);
325110961Strhodes        }
32653197Sphantom
327110961Strhodes        /**
328110961Strhodes         * Adds a unary operator symbol.
329113429Sfjoe         */
33060443Sphantom        final UnaryOperatorHelper addUnaryOperator(OperatorType arg, OperatorType res, int... opcode) {
33160443Sphantom            operatorSuppliers = operatorSuppliers.prepend(() -> makeOperator(name, List.of(arg), res, opcode));
33245678Sbde            return this;
33391996Sdd        }
334110961Strhodes
335110961Strhodes        /**
336110961Strhodes         * This method will be overridden by unary operator helpers to provide custom resolution
337110961Strhodes         * logic.
33854171Sbde         */
339110961Strhodes        abstract Symbol resolve(Type t);
340110961Strhodes    }
341110961Strhodes
34253197Sphantom    abstract class BinaryOperatorHelper extends OperatorHelper implements BiPredicate<Type, Type> {
343107191Sru
34453197Sphantom        BinaryOperatorHelper(Tag tag) {
345110961Strhodes            super(tag);
346110961Strhodes        }
347110961Strhodes
348110961Strhodes        /**
349110961Strhodes         * This routine implements the binary operator lookup process. It customizes the behavior
350110961Strhodes         * of the shared lookup routine in {@link OperatorHelper}, by using an unary applicability test
35143102Snsouch         * (see {@link BinaryOperatorHelper#isBinaryOperatorApplicable(OperatorSymbol, Type, Type)}
352110961Strhodes         */
353110961Strhodes        final Symbol doLookup(Type t1, Type t2) {
354110961Strhodes            return doLookup(op -> isBinaryOperatorApplicable((OperatorSymbol)op, t1, t2));
355110961Strhodes        }
35617013Swosch
3571788Srgrimes        /**
35872891Sru         * Binary operator applicability test - are the input types the same as the expected operand types?
35972891Sru         */
36017013Swosch        boolean isBinaryOperatorApplicable(OperatorSymbol op, Type t1, Type t2) {
3611638Srgrimes            List<Type> formals = op.type.getParameterTypes();
36217013Swosch            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    /**
802     * Complete the initialization of an operator helper by storing it into the corresponding operator map.
803     */
804    @SafeVarargs
805    private final <O extends OperatorHelper> void initOperators(Map<Name, List<O>> opsMap, O... ops) {
806        for (O o : ops) {
807            Name opName = o.name;
808            List<O> helpers = opsMap.getOrDefault(opName, List.nil());
809            opsMap.put(opName, helpers.prepend(o));
810        }
811    }
812
813    /**
814     * Initialize operator name array.
815     */
816    private void initOperatorNames() {
817        setOperatorName(Tag.POS, "+");
818        setOperatorName(Tag.NEG, "-");
819        setOperatorName(Tag.NOT, "!");
820        setOperatorName(Tag.COMPL, "~");
821        setOperatorName(Tag.PREINC, "++");
822        setOperatorName(Tag.PREDEC, "--");
823        setOperatorName(Tag.POSTINC, "++");
824        setOperatorName(Tag.POSTDEC, "--");
825        setOperatorName(Tag.NULLCHK, "<*nullchk*>");
826        setOperatorName(Tag.OR, "||");
827        setOperatorName(Tag.AND, "&&");
828        setOperatorName(Tag.EQ, "==");
829        setOperatorName(Tag.NE, "!=");
830        setOperatorName(Tag.LT, "<");
831        setOperatorName(Tag.GT, ">");
832        setOperatorName(Tag.LE, "<=");
833        setOperatorName(Tag.GE, ">=");
834        setOperatorName(Tag.BITOR, "|");
835        setOperatorName(Tag.BITXOR, "^");
836        setOperatorName(Tag.BITAND, "&");
837        setOperatorName(Tag.SL, "<<");
838        setOperatorName(Tag.SR, ">>");
839        setOperatorName(Tag.USR, ">>>");
840        setOperatorName(Tag.PLUS, "+");
841        setOperatorName(Tag.MINUS, names.hyphen);
842        setOperatorName(Tag.MUL, names.asterisk);
843        setOperatorName(Tag.DIV, names.slash);
844        setOperatorName(Tag.MOD, "%");
845    }
846    //where
847        private void setOperatorName(Tag tag, String name) {
848            setOperatorName(tag, names.fromString(name));
849        }
850
851        private void setOperatorName(Tag tag, Name name) {
852            opname[tag.operatorIndex()] = name;
853        }
854}
855