1 /*
   2  * Copyright (c) 2015, 2018, 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 
  26 package com.sun.tools.javac.comp;
  27 
  28 import com.sun.tools.javac.code.Symbol;
  29 import com.sun.tools.javac.code.Symbol.OperatorSymbol;
  30 import com.sun.tools.javac.code.Symtab;
  31 import com.sun.tools.javac.code.Type;
  32 import com.sun.tools.javac.code.Type.MethodType;
  33 import com.sun.tools.javac.code.TypeTag;
  34 import com.sun.tools.javac.code.Types;
  35 import com.sun.tools.javac.jvm.ByteCodes;
  36 import com.sun.tools.javac.resources.CompilerProperties.Errors;
  37 import com.sun.tools.javac.tree.JCTree;
  38 import com.sun.tools.javac.tree.JCTree.Tag;
  39 import com.sun.tools.javac.util.Assert;
  40 import com.sun.tools.javac.util.Context;
  41 import com.sun.tools.javac.util.JCDiagnostic;
  42 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
  43 import com.sun.tools.javac.util.List;
  44 import com.sun.tools.javac.util.Log;
  45 import com.sun.tools.javac.util.Name;
  46 import com.sun.tools.javac.util.Names;
  47 
  48 import java.util.HashMap;
  49 import java.util.Map;
  50 import java.util.Optional;
  51 import java.util.function.BiPredicate;
  52 import java.util.function.Function;
  53 import java.util.function.Predicate;
  54 import java.util.function.Supplier;
  55 import java.util.stream.Stream;
  56 
  57 import static com.sun.tools.javac.jvm.ByteCodes.*;
  58 import 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  */
  68 public 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(t -> t.isErroneous() || t.hasTag(TypeTag.NONE))) {
 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(Supplier::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 }