Operators.java revision 3827:44bdefe64114
145405Smsmith/*
245405Smsmith * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
345405Smsmith * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
445405Smsmith *
545405Smsmith * This code is free software; you can redistribute it and/or modify it
645405Smsmith * under the terms of the GNU General Public License version 2 only, as
745405Smsmith * published by the Free Software Foundation.  Oracle designates this
845405Smsmith * particular file as subject to the "Classpath" exception as provided
945405Smsmith * by Oracle in the LICENSE file that accompanied this code.
1045405Smsmith *
1145405Smsmith * This code is distributed in the hope that it will be useful, but WITHOUT
1245405Smsmith * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1345405Smsmith * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1445405Smsmith * version 2 for more details (a copy is included in the LICENSE file that
1545405Smsmith * accompanied this code).
1645405Smsmith *
1745405Smsmith * You should have received a copy of the GNU General Public License version
1845405Smsmith * 2 along with this work; if not, write to the Free Software Foundation,
1945405Smsmith * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
2045405Smsmith *
2145405Smsmith * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2245405Smsmith * or visit www.oracle.com if you need additional information or have any
2345405Smsmith * questions.
2445405Smsmith */
2545405Smsmith
2645405Smsmithpackage com.sun.tools.javac.comp;
27115683Sobrien
28115683Sobrienimport com.sun.tools.javac.code.Symbol;
29115683Sobrienimport com.sun.tools.javac.code.Symbol.OperatorSymbol;
3045405Smsmithimport com.sun.tools.javac.code.Symtab;
3145405Smsmithimport com.sun.tools.javac.code.Type;
3245405Smsmithimport com.sun.tools.javac.code.Type.MethodType;
3345405Smsmithimport com.sun.tools.javac.code.TypeTag;
3445405Smsmithimport com.sun.tools.javac.code.Types;
3576078Sjhbimport com.sun.tools.javac.jvm.ByteCodes;
36106842Smdoddimport com.sun.tools.javac.resources.CompilerProperties.Errors;
3745405Smsmithimport com.sun.tools.javac.tree.JCTree;
3845405Smsmithimport com.sun.tools.javac.tree.JCTree.Tag;
3945405Smsmithimport com.sun.tools.javac.util.Assert;
4045405Smsmithimport com.sun.tools.javac.util.Context;
4145405Smsmithimport com.sun.tools.javac.util.JCDiagnostic;
4245405Smsmithimport com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
4345405Smsmithimport com.sun.tools.javac.util.List;
4445405Smsmithimport com.sun.tools.javac.util.Log;
4545405Smsmithimport com.sun.tools.javac.util.Name;
4645405Smsmithimport com.sun.tools.javac.util.Names;
4745405Smsmith
4845405Smsmithimport java.util.HashMap;
4945405Smsmithimport java.util.Map;
50177070Sjhbimport java.util.Optional;
5145405Smsmithimport java.util.function.BiPredicate;
52177070Sjhbimport java.util.function.Function;
53177070Sjhbimport java.util.function.Predicate;
54177070Sjhbimport java.util.function.Supplier;
55177070Sjhbimport java.util.stream.Stream;
5645405Smsmith
57177070Sjhbimport static com.sun.tools.javac.jvm.ByteCodes.*;
58177070Sjhbimport static com.sun.tools.javac.comp.Operators.OperatorType.*;
59177070Sjhb
60177070Sjhb/**
61177070Sjhb * This class contains the logic for unary and binary operator resolution/lookup.
6245405Smsmith *
63177070Sjhb * <p><b>This is NOT part of any supported API.
64177070Sjhb * If you write code that depends on this, you do so at your own risk.
6545405Smsmith * This code and its internal interfaces are subject to change or
66177070Sjhb * deletion without notice.</b>
67106842Smdodd */
68121307Ssilbypublic class Operators {
69177070Sjhb    protected static final Context.Key<Operators> operatorsKey = new Context.Key<>();
70106842Smdodd
71177070Sjhb    private final Names names;
72177070Sjhb    private final Log log;
73177070Sjhb    private final Symtab syms;
74177070Sjhb    private final Types types;
7545405Smsmith
7645405Smsmith    /** Unary operators map. */
77177070Sjhb    private Map<Name, List<UnaryOperatorHelper>> unaryOperators = new HashMap<>(Tag.getNumberOfOperators());
78177070Sjhb
79177070Sjhb    /** Binary operators map. */
8045405Smsmith    private Map<Name, List<BinaryOperatorHelper>> binaryOperators = new HashMap<>(Tag.getNumberOfOperators());
8145405Smsmith
8246215Smsmith    /** The names of all operators. */
83177070Sjhb    private Name[] opname = new Name[Tag.getNumberOfOperators()];
8446215Smsmith
85177070Sjhb    public static Operators instance(Context context) {
86177070Sjhb        Operators instance = context.get(operatorsKey);
87177070Sjhb        if (instance == null)
88177070Sjhb            instance = new Operators(context);
89177070Sjhb        return instance;
90177070Sjhb    }
91177070Sjhb
92177070Sjhb    protected Operators(Context context) {
93177070Sjhb        context.put(operatorsKey, this);
94177070Sjhb        syms = Symtab.instance(context);
95177070Sjhb        names = Names.instance(context);
96177070Sjhb        log = Log.instance(context);
97177070Sjhb        types = Types.instance(context);
98177070Sjhb        noOpSymbol = new OperatorSymbol(names.empty, Type.noType, -1, syms.noSymbol);
9945405Smsmith        initOperatorNames();
10045405Smsmith        initUnaryOperators();
10145405Smsmith        initBinaryOperators();
102177070Sjhb    }
103177070Sjhb
104177070Sjhb    /**
105177070Sjhb     * Perform unary promotion of a type; this routine implements JLS 5.6.1.
106177070Sjhb     * If the input type is not supported by unary promotion, it is returned unaltered.
107177070Sjhb     */
108177070Sjhb    Type unaryPromotion(Type t) {
10945405Smsmith        Type unboxed = types.unboxedTypeOrType(t);
11045405Smsmith        switch (unboxed.getTag()) {
111177070Sjhb            case BYTE:
11294683Sdwmalone            case SHORT:
11394683Sdwmalone            case CHAR:
114177070Sjhb                return syms.intType;
115177070Sjhb            default:
116177070Sjhb                return unboxed;
11794683Sdwmalone        }
118177070Sjhb    }
119177070Sjhb
12094683Sdwmalone    /**
12194683Sdwmalone     * Perform binary promotion of a pair of types; this routine implements JLS 5.6.2.
122177070Sjhb     * If the input types are not supported by unary promotion, if such types are identical to
12394683Sdwmalone     * a type C, then C is returned, otherwise Object is returned.
12448925Smsmith     */
12594683Sdwmalone    Type binaryPromotion(Type t1, Type t2) {
126177070Sjhb        Type unboxedT1 = types.unboxedTypeOrType(t1);
127177070Sjhb        Type unboxedT2 = types.unboxedTypeOrType(t2);
128177070Sjhb
12994683Sdwmalone        if (unboxedT1.isNumeric() && unboxedT2.isNumeric()) {
13094683Sdwmalone            if (unboxedT1.hasTag(TypeTag.DOUBLE) || unboxedT2.hasTag(TypeTag.DOUBLE)) {
13194683Sdwmalone                return syms.doubleType;
13294683Sdwmalone            } else if (unboxedT1.hasTag(TypeTag.FLOAT) || unboxedT2.hasTag(TypeTag.FLOAT)) {
13394683Sdwmalone                return syms.floatType;
134177070Sjhb            } else if (unboxedT1.hasTag(TypeTag.LONG) || unboxedT2.hasTag(TypeTag.LONG)) {
135177070Sjhb                return syms.longType;
13694683Sdwmalone            } else {
13745405Smsmith                return syms.intType;
13845405Smsmith            }
13945405Smsmith        } else if (types.isSameType(unboxedT1, unboxedT2)) {
14045405Smsmith            return unboxedT1;
14145405Smsmith        } else {
142177070Sjhb            return syms.objectType;
14345405Smsmith        }
144177070Sjhb    }
145177070Sjhb
146177070Sjhb    /**
147177070Sjhb     * Entry point for resolving a unary operator given an operator tag and an argument type.
148177070Sjhb     */
149177070Sjhb    OperatorSymbol resolveUnary(DiagnosticPosition pos, JCTree.Tag tag, Type op) {
150177070Sjhb        return resolve(tag,
151177070Sjhb                unaryOperators,
15245405Smsmith                unop -> unop.test(op),
15345405Smsmith                unop -> unop.resolve(op),
15445405Smsmith                () -> reportErrorIfNeeded(pos, tag, op));
155177070Sjhb    }
156177070Sjhb
157177070Sjhb    /**
158177070Sjhb     * Entry point for resolving a binary operator given an operator tag and a pair of argument types.
15945405Smsmith     */
16045405Smsmith    OperatorSymbol resolveBinary(DiagnosticPosition pos, JCTree.Tag tag, Type op1, Type op2) {
16145405Smsmith        return resolve(tag,
16245405Smsmith                binaryOperators,
163177070Sjhb                binop -> binop.test(op1, op2),
164177070Sjhb                binop -> binop.resolve(op1, op2),
165177070Sjhb                () -> reportErrorIfNeeded(pos, tag, op1, op2));
16645405Smsmith    }
167177070Sjhb
16845405Smsmith    /**
169177070Sjhb     * Main operator lookup routine; lookup an operator (either unary or binary) in its corresponding
170177070Sjhb     * map. If there's a matching operator, its resolve routine is called and the result is returned;
171177070Sjhb     * otherwise the result of a fallback function is returned.
172177070Sjhb     */
173177070Sjhb    private <O> OperatorSymbol resolve(Tag tag, Map<Name, List<O>> opMap, Predicate<O> opTestFunc,
174177070Sjhb                       Function<O, OperatorSymbol> resolveFunc, Supplier<OperatorSymbol> noResultFunc) {
175177070Sjhb        return opMap.get(operatorName(tag)).stream()
176177070Sjhb                .filter(opTestFunc)
177177070Sjhb                .map(resolveFunc)
178177070Sjhb                .findFirst()
179177070Sjhb                .orElseGet(noResultFunc);
180177070Sjhb    }
181177070Sjhb
182177070Sjhb    /**
183177070Sjhb     * Creates an operator symbol.
184177070Sjhb     */
185177070Sjhb    private OperatorSymbol makeOperator(Name name, List<OperatorType> formals, OperatorType res, int... opcodes) {
186177070Sjhb        MethodType opType = new MethodType(
187177070Sjhb                formals.stream()
188177070Sjhb                        .map(o -> o.asType(syms))
189177070Sjhb                        .collect(List.collector()),
190177070Sjhb                res.asType(syms), List.nil(), syms.methodClass);
191177070Sjhb        return new OperatorSymbol(name, opType, mergeOpcodes(opcodes), syms.noSymbol);
192177070Sjhb    }
193177070Sjhb
194177070Sjhb    /**
195177070Sjhb     * Fold two opcodes in a single int value (if required).
196177070Sjhb     */
197177070Sjhb    private int mergeOpcodes(int... opcodes) {
198177070Sjhb        int opcodesLen = opcodes.length;
199177070Sjhb        Assert.check(opcodesLen == 1 || opcodesLen == 2);
200177070Sjhb        return (opcodesLen == 1) ?
201177070Sjhb                opcodes[0] :
202177070Sjhb                ((opcodes[0] << ByteCodes.preShift) | opcodes[1]);
203177070Sjhb    }
204177070Sjhb
205177070Sjhb    /** A symbol that stands for a missing operator.
206177070Sjhb     */
20745405Smsmith    public final OperatorSymbol noOpSymbol;
208177070Sjhb
209177070Sjhb    /**
210177070Sjhb     * Report an operator lookup error.
211177070Sjhb     */
212177070Sjhb    private OperatorSymbol reportErrorIfNeeded(DiagnosticPosition pos, Tag tag, Type... args) {
21345405Smsmith        if (Stream.of(args).noneMatch(Type::isErroneous)) {
214177070Sjhb            Name opName = operatorName(tag);
215177070Sjhb            JCDiagnostic.Error opError = (args.length) == 1 ?
216177070Sjhb                    Errors.OperatorCantBeApplied(opName, args[0]) :
217177070Sjhb                    Errors.OperatorCantBeApplied1(opName, args[0], args[1]);
218177070Sjhb            log.error(pos, opError);
219177070Sjhb        }
220177070Sjhb        return noOpSymbol;
221177070Sjhb    }
222177070Sjhb
223177070Sjhb    /**
224177070Sjhb     * Return name of operator with given tree tag.
225177070Sjhb     */
226177070Sjhb    public Name operatorName(JCTree.Tag tag) {
227177070Sjhb        return opname[tag.operatorIndex()];
228177070Sjhb    }
229177070Sjhb
23045405Smsmith    /**
23145405Smsmith     * The constants in this enum represent the types upon which all the operator helpers
23245405Smsmith     * operate upon. This allows lazy and consise mapping between a type name and a type instance.
23345405Smsmith     */
23445405Smsmith    enum OperatorType {
23545405Smsmith        BYTE(syms -> syms.byteType),
23645405Smsmith        SHORT(syms -> syms.shortType),
23745405Smsmith        INT(syms -> syms.intType),
23845405Smsmith        LONG(syms -> syms.longType),
239177070Sjhb        FLOAT(syms -> syms.floatType),
24045405Smsmith        DOUBLE(syms -> syms.doubleType),
241177070Sjhb        CHAR(syms -> syms.charType),
24245405Smsmith        BOOLEAN(syms -> syms.booleanType),
243177070Sjhb        OBJECT(syms -> syms.objectType),
244177070Sjhb        STRING(syms -> syms.stringType),
245177070Sjhb        BOT(syms -> syms.botType);
246177070Sjhb
247177070Sjhb        final Function<Symtab, Type> asTypeFunc;
248177070Sjhb
249177070Sjhb        OperatorType(Function<Symtab, Type> asTypeFunc) {
25045405Smsmith            this.asTypeFunc = asTypeFunc;
25145405Smsmith        }
25294683Sdwmalone
25394683Sdwmalone        Type asType(Symtab syms) {
25494683Sdwmalone            return asTypeFunc.apply(syms);
25594683Sdwmalone        }
25694683Sdwmalone    }
25794683Sdwmalone
258177070Sjhb    /**
259177070Sjhb     * Common root for all operator helpers. An operator helper instance is associated with a
26094683Sdwmalone     * given operator (i.e. '+'); it contains routines to perform operator lookup, i.e. find
26194683Sdwmalone     * which version of the '+' operator is the best given an argument type list. Supported
26245405Smsmith     * operator symbols are initialized lazily upon first lookup request - this is in order to avoid
26346215Smsmith     * initialization circularities between this class and {@code Symtab}.
26446215Smsmith     */
26546215Smsmith    abstract class OperatorHelper {
26646215Smsmith
26745405Smsmith        /** The operator name. */
26848925Smsmith        final Name name;
26945405Smsmith
27045405Smsmith        /** The list of symbols associated with this operator (lazily populated). */
27145405Smsmith        Optional<OperatorSymbol[]> alternatives = Optional.empty();
272177070Sjhb
273177070Sjhb        /** An array of operator symbol suppliers (used to lazily populate the symbol list). */
274177070Sjhb        List<Supplier<OperatorSymbol>> operatorSuppliers = List.nil();
275177070Sjhb
276177070Sjhb        @SuppressWarnings("varargs")
277177070Sjhb        OperatorHelper(Tag tag) {
278177070Sjhb            this.name = operatorName(tag);
27948925Smsmith        }
280177070Sjhb
281177070Sjhb        /**
282177070Sjhb         * This routine implements the main operator lookup process. Each operator is tested
28348925Smsmith         * using an applicability predicate; if the test suceeds that same operator is returned,
28446215Smsmith         * otherwise a dummy symbol is returned.
28546215Smsmith         */
28646215Smsmith        final OperatorSymbol doLookup(Predicate<OperatorSymbol> applicabilityTest) {
28746215Smsmith            return Stream.of(alternatives.orElseGet(this::initOperators))
288177070Sjhb                    .filter(applicabilityTest)
289177070Sjhb                    .findFirst()
29046215Smsmith                    .orElse(noOpSymbol);
29148925Smsmith        }
29248925Smsmith
29346215Smsmith        /**
294177070Sjhb         * This routine performs lazy instantiation of the operator symbols supported by this helper.
295177070Sjhb         * After initialization is done, the suppliers are cleared, to free up memory.
296177070Sjhb         */
297177070Sjhb        private OperatorSymbol[] initOperators() {
298177070Sjhb            OperatorSymbol[] operators = operatorSuppliers.stream()
29946215Smsmith                    .map(Supplier::get)
300177070Sjhb                    .toArray(OperatorSymbol[]::new);
30146215Smsmith            alternatives = Optional.of(operators);
302177070Sjhb            operatorSuppliers = null; //let GC do its work
303177070Sjhb            return operators;
304177070Sjhb        }
305177070Sjhb    }
30645405Smsmith
307177070Sjhb    /**
308177070Sjhb     * Common superclass for all unary operator helpers.
309177070Sjhb     */
310177070Sjhb    abstract class UnaryOperatorHelper extends OperatorHelper implements Predicate<Type> {
311177070Sjhb
312177070Sjhb        UnaryOperatorHelper(Tag tag) {
313177070Sjhb            super(tag);
314177070Sjhb        }
315177070Sjhb
316177070Sjhb        /**
317177070Sjhb         * This routine implements the unary operator lookup process. It customizes the behavior
318177070Sjhb         * of the shared lookup routine in {@link OperatorHelper}, by using an unary applicability test
319177070Sjhb         * (see {@link UnaryOperatorHelper#isUnaryOperatorApplicable(OperatorOperatorSymbol, Type)}
320177070Sjhb         */
321177070Sjhb        final OperatorSymbol doLookup(Type t) {
322177070Sjhb            return doLookup(op -> isUnaryOperatorApplicable(op, t));
323177070Sjhb        }
324177070Sjhb
325177070Sjhb        /**
326177070Sjhb         * Unary operator applicability test - is the input type the same as the expected operand type?
327177070Sjhb         */
328177070Sjhb        boolean isUnaryOperatorApplicable(OperatorSymbol op, Type t) {
329177070Sjhb            return types.isSameType(op.type.getParameterTypes().head, t);
330177070Sjhb        }
331177070Sjhb
332177070Sjhb        /**
333177070Sjhb         * Adds a unary operator symbol.
334177070Sjhb         */
335177070Sjhb        final UnaryOperatorHelper addUnaryOperator(OperatorType arg, OperatorType res, int... opcode) {
336177070Sjhb            operatorSuppliers = operatorSuppliers.prepend(() -> makeOperator(name, List.of(arg), res, opcode));
337177070Sjhb            return this;
338177070Sjhb        }
339177070Sjhb
340177070Sjhb        /**
341177070Sjhb         * This method will be overridden by unary operator helpers to provide custom resolution
342177070Sjhb         * logic.
343177070Sjhb         */
344177070Sjhb        abstract OperatorSymbol resolve(Type t);
345177070Sjhb    }
346177070Sjhb
347177070Sjhb    abstract class BinaryOperatorHelper extends OperatorHelper implements BiPredicate<Type, Type> {
348177070Sjhb
349177070Sjhb        BinaryOperatorHelper(Tag tag) {
350177070Sjhb            super(tag);
351177070Sjhb        }
352177070Sjhb
353177070Sjhb        /**
35445405Smsmith         * This routine implements the binary operator lookup process. It customizes the behavior
355177070Sjhb         * of the shared lookup routine in {@link OperatorHelper}, by using an unary applicability test
356177070Sjhb         * (see {@link BinaryOperatorHelper#isBinaryOperatorApplicable(OperatorSymbol, Type, Type)}
357177070Sjhb         */
358177070Sjhb        final OperatorSymbol doLookup(Type t1, Type t2) {
359177070Sjhb            return doLookup(op -> isBinaryOperatorApplicable(op, t1, t2));
360177070Sjhb        }
361177070Sjhb
362177070Sjhb        /**
363177070Sjhb         * Binary operator applicability test - are the input types the same as the expected operand types?
364177070Sjhb         */
365177070Sjhb        boolean isBinaryOperatorApplicable(OperatorSymbol op, Type t1, Type t2) {
366177070Sjhb            List<Type> formals = op.type.getParameterTypes();
367177070Sjhb            return types.isSameType(formals.head, t1) &&
368177070Sjhb                    types.isSameType(formals.tail.head, t2);
369177070Sjhb        }
370177070Sjhb
371177070Sjhb        /**
372177070Sjhb         * Adds a binary operator symbol.
373177070Sjhb         */
374177070Sjhb        final BinaryOperatorHelper addBinaryOperator(OperatorType arg1, OperatorType arg2, OperatorType res, int... opcode) {
375177070Sjhb            operatorSuppliers = operatorSuppliers.prepend(() -> makeOperator(name, List.of(arg1, arg2), res, opcode));
376177070Sjhb            return this;
37745405Smsmith        }
37845405Smsmith
379177070Sjhb        /**
380177070Sjhb         * This method will be overridden by binary operator helpers to provide custom resolution
381177070Sjhb         * logic.
382177070Sjhb         */
383177070Sjhb        abstract OperatorSymbol resolve(Type t1, Type t2);
384177070Sjhb    }
385177070Sjhb
386177070Sjhb    /**
387177070Sjhb     * Class representing unary operator helpers that operate on reference types.
388177070Sjhb     */
389177070Sjhb    class UnaryReferenceOperator extends UnaryOperatorHelper {
39045405Smsmith
39145405Smsmith        UnaryReferenceOperator(Tag tag) {
39245405Smsmith            super(tag);
39345405Smsmith        }
39445405Smsmith
39545405Smsmith        @Override
39645405Smsmith        public boolean test(Type type) {
39745405Smsmith            return type.isNullOrReference();
398177070Sjhb        }
399177070Sjhb
400177070Sjhb        @Override
401177070Sjhb        public OperatorSymbol resolve(Type arg) {
402177070Sjhb            return doLookup(syms.objectType);
403177070Sjhb        }
404177070Sjhb    }
405177070Sjhb
406177070Sjhb    /**
40745405Smsmith     * Class representing unary operator helpers that operate on numeric types (either boxed or unboxed).
40845405Smsmith     * Operator lookup is performed after applying numeric promotion of the input type.
40945405Smsmith     */
410177070Sjhb    class UnaryNumericOperator extends UnaryOperatorHelper {
411177070Sjhb
41245405Smsmith        Predicate<Type> numericTest;
413177070Sjhb
414177070Sjhb        UnaryNumericOperator(Tag tag) {
415177070Sjhb            this(tag, Type::isNumeric);
41645405Smsmith        }
417177070Sjhb
418177070Sjhb        UnaryNumericOperator(Tag tag, Predicate<Type> numericTest) {
41945405Smsmith            super(tag);
42045405Smsmith            this.numericTest = numericTest;
42145405Smsmith        }
42245405Smsmith
423177070Sjhb        @Override
42445405Smsmith        public boolean test(Type type) {
425177070Sjhb            return numericTest.test(unaryPromotion(type));
426177070Sjhb        }
427177070Sjhb
428177070Sjhb        @Override
42945405Smsmith        public OperatorSymbol resolve(Type arg) {
430177070Sjhb            return doLookup(unaryPromotion(arg));
431177070Sjhb        }
432177070Sjhb    }
433177070Sjhb
434177070Sjhb    /**
435177070Sjhb     * Class representing unary operator helpers that operate on boolean types  (either boxed or unboxed).
436177070Sjhb     * Operator lookup is performed assuming the input type is a boolean type.
437177070Sjhb     */
438103346Sdwmalone    class UnaryBooleanOperator extends UnaryOperatorHelper {
439177070Sjhb
440177070Sjhb        UnaryBooleanOperator(Tag tag) {
441177070Sjhb            super(tag);
442103346Sdwmalone        }
443103346Sdwmalone
444177070Sjhb        @Override
44545405Smsmith        public boolean test(Type type) {
44645405Smsmith            return types.unboxedTypeOrType(type).hasTag(TypeTag.BOOLEAN);
44745405Smsmith        }
44845405Smsmith
44945405Smsmith        @Override
45045405Smsmith        public OperatorSymbol resolve(Type arg) {
45145405Smsmith            return doLookup(syms.booleanType);
45245405Smsmith        }
453177070Sjhb    }
454177070Sjhb
45545405Smsmith    /**
456177070Sjhb     * Class representing prefix/postfix unary operator helpers. Operates on numeric types (either
457177070Sjhb     * boxed or unboxed). Operator lookup is performed on the unboxed version of the input type.
458177070Sjhb     */
459177070Sjhb    class UnaryPrefixPostfixOperator extends UnaryNumericOperator {
460177070Sjhb
461177070Sjhb        UnaryPrefixPostfixOperator(Tag tag) {
462177070Sjhb            super(tag);
463177070Sjhb        }
464177070Sjhb
465177070Sjhb        @Override
466177070Sjhb        public OperatorSymbol resolve(Type arg) {
467177070Sjhb            return doLookup(types.unboxedTypeOrType(arg));
468177070Sjhb        }
469177070Sjhb    }
470177070Sjhb
471177070Sjhb    /**
472177070Sjhb     * Class representing binary operator helpers that operate on numeric types (either boxed or unboxed).
473177070Sjhb     * Operator lookup is performed after applying binary numeric promotion of the input types.
474177070Sjhb     */
475177070Sjhb    class BinaryNumericOperator extends BinaryOperatorHelper {
476177070Sjhb
477177070Sjhb        Predicate<Type> numericTest;
478177070Sjhb
479177070Sjhb        BinaryNumericOperator(Tag tag) {
480177070Sjhb            this(tag, Type::isNumeric);
481177070Sjhb        }
482177070Sjhb
483177070Sjhb        BinaryNumericOperator(Tag tag, Predicate<Type> numericTest) {
484177070Sjhb            super(tag);
485177070Sjhb            this.numericTest = numericTest;
486177070Sjhb        }
487177070Sjhb
488177070Sjhb        @Override
489177070Sjhb        public OperatorSymbol resolve(Type arg1, Type arg2) {
490177070Sjhb            Type t = binaryPromotion(arg1, arg2);
491177070Sjhb            return doLookup(t, t);
492177070Sjhb        }
493177070Sjhb
494177070Sjhb        @Override
495177070Sjhb        public boolean test(Type arg1, Type arg2) {
496177070Sjhb            return numericTest.test(unaryPromotion(arg1)) &&
497177070Sjhb                    numericTest.test(unaryPromotion(arg2));
498177070Sjhb        }
499177070Sjhb    }
500177070Sjhb
50145405Smsmith    /**
50245405Smsmith     * Class representing bitwise operator helpers that operate on boolean types (either boxed or unboxed).
503177070Sjhb     * Operator lookup is performed assuming both input types are boolean types.
504177070Sjhb     */
505177070Sjhb    class BinaryBooleanOperator extends BinaryOperatorHelper {
506177070Sjhb
507177070Sjhb        BinaryBooleanOperator(Tag tag) {
508177070Sjhb            super(tag);
509177070Sjhb        }
510177070Sjhb
511177070Sjhb        @Override
512177070Sjhb        public OperatorSymbol resolve(Type arg1, Type arg2) {
51345405Smsmith            return doLookup(syms.booleanType, syms.booleanType);
51445405Smsmith        }
51545405Smsmith
51645405Smsmith        @Override
51745405Smsmith        public boolean test(Type arg1, Type arg2) {
51845405Smsmith            return types.unboxedTypeOrType(arg1).hasTag(TypeTag.BOOLEAN) &&
51945405Smsmith                    types.unboxedTypeOrType(arg2).hasTag(TypeTag.BOOLEAN);
52045405Smsmith        }
521177070Sjhb    }
522177070Sjhb
52345405Smsmith    /**
524177070Sjhb     * Class representing string concatenation operator helper that operates on at least an
525177070Sjhb     * string operand. Input types subject to an operator lookup undergoes a special string promotion
526177070Sjhb     * (see {@link BinaryStringOperator#stringPromotion(Type)}.
527177070Sjhb     */
528177070Sjhb    class BinaryStringOperator extends BinaryOperatorHelper {
529177070Sjhb
530177070Sjhb        BinaryStringOperator(Tag tag) {
531177070Sjhb            super(tag);
532177070Sjhb        }
53345405Smsmith
534177070Sjhb        @Override
53545405Smsmith        public OperatorSymbol resolve(Type arg1, Type arg2) {
536177070Sjhb            return doLookup(stringPromotion(arg1), stringPromotion(arg2));
537177070Sjhb        }
538177070Sjhb
539177070Sjhb        @Override
540177070Sjhb        public boolean test(Type arg1, Type arg2) {
541177070Sjhb            boolean hasStringOp = types.isSameType(arg1, syms.stringType) ||
542177070Sjhb                    types.isSameType(arg2, syms.stringType);
543177070Sjhb            boolean hasVoidOp = arg1.hasTag(TypeTag.VOID) || arg2.hasTag(TypeTag.VOID);
544177070Sjhb            return hasStringOp && !hasVoidOp;
545177070Sjhb        }
546177070Sjhb
547177070Sjhb        /**
548177070Sjhb         * This routine applies following mappings:
549177070Sjhb         * - if input type is primitive, apply numeric promotion
550177070Sjhb         * - if input type is either 'void', 'null' or 'String' leave it untouched
551177070Sjhb         * - otherwise return 'Object'
552177070Sjhb         */
553177070Sjhb        private Type stringPromotion(Type t) {
554177070Sjhb            if (t.isPrimitive()) {
555177070Sjhb                return unaryPromotion(t);
556177070Sjhb            } else if (t.hasTag(TypeTag.VOID) || t.hasTag(TypeTag.BOT) ||
557177070Sjhb                    types.isSameType(t, syms.stringType)) {
558177070Sjhb                return t;
559177070Sjhb            } else if (t.hasTag(TypeTag.TYPEVAR)) {
560177070Sjhb                return stringPromotion(t.getUpperBound());
56145405Smsmith            } else {
56245405Smsmith                return syms.objectType;
563177070Sjhb            }
564177070Sjhb        }
56545405Smsmith    }
566177070Sjhb
567177070Sjhb    /**
568177070Sjhb     * Class representing shift operator helper that operates on integral operand types (either boxed
56945405Smsmith     * or unboxed). Operator lookup is performed after applying unary numeric promotion to each input type.
57045405Smsmith     */
57145405Smsmith    class BinaryShiftOperator extends BinaryOperatorHelper {
572177070Sjhb
573177070Sjhb        BinaryShiftOperator(Tag tag) {
57445405Smsmith            super(tag);
57545405Smsmith        }
57645405Smsmith
57745405Smsmith        @Override
578177070Sjhb        public OperatorSymbol resolve(Type arg1, Type arg2) {
579177070Sjhb            return doLookup(unaryPromotion(arg1), unaryPromotion(arg2));
58045405Smsmith        }
581177070Sjhb
582177070Sjhb        @Override
58345405Smsmith        public boolean test(Type arg1, Type arg2) {
584177070Sjhb            TypeTag op1 = unaryPromotion(arg1).getTag();
585177070Sjhb            TypeTag op2 = unaryPromotion(arg2).getTag();
586177070Sjhb            return (op1 == TypeTag.LONG || op1 == TypeTag.INT) &&
587177070Sjhb                    (op2 == TypeTag.LONG || op2 == TypeTag.INT);
588177070Sjhb        }
589177070Sjhb    }
590177070Sjhb
59145405Smsmith    /**
592177070Sjhb     * This enum represent the possible kinds of an comparison test ('==' and '!=').
59345405Smsmith     */
594177070Sjhb    enum ComparisonKind {
595177070Sjhb        /** equality between numeric or boolean operands. */
596177070Sjhb        NUMERIC_OR_BOOLEAN,
597177070Sjhb        /** equality between reference operands. */
598177070Sjhb        REFERENCE,
59945405Smsmith        /** erroneous equality */
600177070Sjhb        INVALID
601177070Sjhb    }
602177070Sjhb
60345405Smsmith    /**
604177070Sjhb     * Class representing equality operator helper that operates on either numeric, boolean or reference
60545405Smsmith     * types. Operator lookup for numeric/boolean equality test is performed after binary numeric
606177070Sjhb     * promotion to the input types. Operator lookup for reference equality test is performed assuming
607177070Sjhb     * the input type is 'Object'.
608177070Sjhb     */
609177070Sjhb    class BinaryEqualityOperator extends BinaryOperatorHelper {
610177070Sjhb
611177070Sjhb        BinaryEqualityOperator(Tag tag) {
612177070Sjhb            super(tag);
613177070Sjhb        }
614177070Sjhb
615177070Sjhb        @Override
616177070Sjhb        public boolean test(Type arg1, Type arg2) {
617177070Sjhb            return getKind(arg1, arg2) != ComparisonKind.INVALID;
618177070Sjhb        }
619177070Sjhb
620177070Sjhb        @Override
621177070Sjhb        public OperatorSymbol resolve(Type t1, Type t2) {
622177070Sjhb            ComparisonKind kind = getKind(t1, t2);
623177070Sjhb            Type t = (kind == ComparisonKind.NUMERIC_OR_BOOLEAN) ?
624177070Sjhb                    binaryPromotion(t1, t2) :
625177070Sjhb                    syms.objectType;
62645405Smsmith            return doLookup(t, t);
627177070Sjhb        }
628177070Sjhb
629177070Sjhb        /**
630177070Sjhb         * Retrieve the comparison kind associated with the given argument type pair.
631177070Sjhb         */
632177070Sjhb        private ComparisonKind getKind(Type arg1, Type arg2) {
633177070Sjhb            boolean arg1Primitive = arg1.isPrimitive();
634177070Sjhb            boolean arg2Primitive = arg2.isPrimitive();
635177070Sjhb            if (arg1Primitive && arg2Primitive) {
636177070Sjhb                return ComparisonKind.NUMERIC_OR_BOOLEAN;
637177070Sjhb            } else if (arg1Primitive) {
63845405Smsmith                return unaryPromotion(arg2).isPrimitive() ?
63945405Smsmith                        ComparisonKind.NUMERIC_OR_BOOLEAN : ComparisonKind.INVALID;
64045405Smsmith            } else if (arg2Primitive) {
64146215Smsmith                return unaryPromotion(arg1).isPrimitive() ?
64246215Smsmith                        ComparisonKind.NUMERIC_OR_BOOLEAN : ComparisonKind.INVALID;
64346215Smsmith            } else {
64445405Smsmith                return arg1.isNullOrReference() && arg2.isNullOrReference() ?
64546215Smsmith                        ComparisonKind.REFERENCE : ComparisonKind.INVALID;
64646215Smsmith            }
647177070Sjhb        }
648177070Sjhb    }
649177070Sjhb
65046215Smsmith    /**
65146215Smsmith     * Initialize all unary operators.
65246215Smsmith     */
65345405Smsmith    private void initUnaryOperators() {
65445405Smsmith        initOperators(unaryOperators,
655177070Sjhb                new UnaryNumericOperator(Tag.POS)
656177124Sjhb                        .addUnaryOperator(DOUBLE, DOUBLE, nop)
657177124Sjhb                        .addUnaryOperator(FLOAT, FLOAT, nop)
658177124Sjhb                        .addUnaryOperator(LONG, LONG, nop)
659177124Sjhb                        .addUnaryOperator(INT, INT, nop),
660177124Sjhb                new UnaryNumericOperator(Tag.NEG)
661177124Sjhb                        .addUnaryOperator(DOUBLE, DOUBLE, dneg)
662177124Sjhb                        .addUnaryOperator(FLOAT, FLOAT, fneg)
663177124Sjhb                        .addUnaryOperator(LONG, LONG, lneg)
664177124Sjhb                        .addUnaryOperator(INT, INT, ineg),
665177124Sjhb                new UnaryNumericOperator(Tag.COMPL, Type::isIntegral)
66645405Smsmith                        .addUnaryOperator(LONG, LONG, lxor)
667177070Sjhb                        .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