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