Operators.java revision 3053:79e637c1e083
11638Srgrimes/* 252655Sphantom * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 31638Srgrimes * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 474856Sru * 579129Smsmith * This code is free software; you can redistribute it and/or modify it 666693Sscottl * under the terms of the GNU General Public License version 2 only, as 763711Ssheldonh * published by the Free Software Foundation. Oracle designates this 884533Syar * particular file as subject to the "Classpath" exception as provided 963711Ssheldonh * by Oracle in the LICENSE file that accompanied this code. 1063711Ssheldonh * 1163711Ssheldonh * This code is distributed in the hope that it will be useful, but WITHOUT 12102768Sscottl * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13105860Sdes * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1463711Ssheldonh * version 2 for more details (a copy is included in the LICENSE file that 1563711Ssheldonh * accompanied this code). 1663711Ssheldonh * 17113769Sobrien * You should have received a copy of the GNU General Public License version 1863711Ssheldonh * 2 along with this work; if not, write to the Free Software Foundation, 19106248Sthomas * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 2063711Ssheldonh * 2163711Ssheldonh * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22116761Ssam * or visit www.oracle.com if you need additional information or have any 23116761Ssam * questions. 2463711Ssheldonh */ 2578821Sonoe 26113757Swpaulpackage com.sun.tools.javac.comp; 2784059Swpaul 2863711Ssheldonhimport com.sun.tools.javac.code.Symbol; 2963711Ssheldonhimport com.sun.tools.javac.code.Symbol.OperatorSymbol; 3063711Ssheldonhimport com.sun.tools.javac.code.Symtab; 3163711Ssheldonhimport com.sun.tools.javac.code.Type; 3263711Ssheldonhimport com.sun.tools.javac.code.Type.MethodType; 3399639Simpimport com.sun.tools.javac.code.TypeTag; 3463711Ssheldonhimport com.sun.tools.javac.code.Types; 3563711Ssheldonhimport com.sun.tools.javac.jvm.ByteCodes; 3663711Ssheldonhimport com.sun.tools.javac.resources.CompilerProperties.Errors; 37108472Strhodesimport com.sun.tools.javac.tree.JCTree; 38104488Ssamimport com.sun.tools.javac.tree.JCTree.Tag; 3963711Ssheldonhimport com.sun.tools.javac.util.Assert; 4063711Ssheldonhimport com.sun.tools.javac.util.Context; 4163711Ssheldonhimport com.sun.tools.javac.util.JCDiagnostic; 4263711Ssheldonhimport com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; 4363711Ssheldonhimport com.sun.tools.javac.util.List; 4463711Ssheldonhimport com.sun.tools.javac.util.Log; 45107759Simpimport com.sun.tools.javac.util.Name; 4697452Sbrooksimport com.sun.tools.javac.util.Names; 4763711Ssheldonh 4863711Ssheldonhimport java.util.HashMap; 4963711Ssheldonhimport java.util.Map; 5063711Ssheldonhimport java.util.Optional; 5163726Ssheldonhimport java.util.function.BiPredicate; 52115140Strhodesimport java.util.function.Function; 5387191Spdeuskarimport java.util.function.Predicate; 54115766Shartiimport java.util.function.Supplier; 55111375Simpimport java.util.stream.Stream; 5663711Ssheldonh 57109614Ssamimport static com.sun.tools.javac.jvm.ByteCodes.*; 58116735Shartiimport static com.sun.tools.javac.comp.Operators.OperatorType.*; 5963711Ssheldonh 6063711Ssheldonh/** 61105934Ssimokawa * This class contains the logic for unary and binary operator resolution/lookup. 62117538Sobrien * 6363711Ssheldonh * <p><b>This is NOT part of any supported API. 64106611Ssimokawa * If you write code that depends on this, you do so at your own risk. 65105934Ssimokawa * This code and its internal interfaces are subject to change or 6663711Ssheldonh * deletion without notice.</b> 67105483Sphk */ 68116166Stmmpublic class Operators { 6993274Sphk protected static final Context.Key<Operators> operatorsKey = new Context.Key<>(); 7063711Ssheldonh 71103027Ssobomax private final Names names; 7263711Ssheldonh private final Log log; 7385134Sjlemon private final Symtab syms; 74116492Sharti private final Types types; 75104488Ssam 76116166Stmm /** Unary operators map. */ 7768809Sarchie private Map<Name, List<UnaryOperatorHelper>> unaryOperators = new HashMap<>(Tag.getNumberOfOperators()); 7863711Ssheldonh 7963711Ssheldonh /** Binary operators map. */ 80108768Strhodes private Map<Name, List<BinaryOperatorHelper>> binaryOperators = new HashMap<>(Tag.getNumberOfOperators()); 8177217Sphk 8263711Ssheldonh /** The names of all operators. */ 8363711Ssheldonh private Name[] opname = new Name[Tag.getNumberOfOperators()]; 8463711Ssheldonh 8563711Ssheldonh public static Operators instance(Context context) { 8663711Ssheldonh Operators instance = context.get(operatorsKey); 87108768Strhodes if (instance == null) 8863711Ssheldonh instance = new Operators(context); 8963711Ssheldonh return instance; 9063711Ssheldonh } 9163711Ssheldonh 9263711Ssheldonh protected Operators(Context context) { 9363711Ssheldonh context.put(operatorsKey, this); 9463711Ssheldonh syms = Symtab.instance(context); 9563711Ssheldonh names = Names.instance(context); 9663711Ssheldonh log = Log.instance(context); 9763711Ssheldonh types = Types.instance(context); 9863711Ssheldonh initOperatorNames(); 9963711Ssheldonh initUnaryOperators(); 10063711Ssheldonh initBinaryOperators(); 10163711Ssheldonh } 10263711Ssheldonh 10374013Sjhb /** 10477542Swpaul * Perform unary promotion of a type; this routine implements JLS 5.6.1. 105108699Sobrien * If the input type is not supported by unary promotion, it is returned unaltered. 10663711Ssheldonh */ 10786698Sgreen Type unaryPromotion(Type t) { 10863711Ssheldonh Type unboxed = types.unboxedTypeOrType(t); 10963711Ssheldonh switch (unboxed.getTag()) { 11063711Ssheldonh case BYTE: 11184961Sscottl case SHORT: 11263711Ssheldonh case CHAR: 11363711Ssheldonh return syms.intType; 114110940Strhodes default: 11563711Ssheldonh return unboxed; 11664988Smsmith } 117109270Schris } 118107626Schris 119107626Schris /** 120107735Schris * Perform binary promotion of a pair of types; this routine implements JLS 5.6.2. 121108933Schris * If the input types are not supported by unary promotion, if such types are identical to 122107626Schris * a type C, then C is returned, otherwise Object is returned. 123107717Schris */ 124107739Schris Type binaryPromotion(Type t1, Type t2) { 125112860Schris Type unboxedT1 = types.unboxedTypeOrType(t1); 126107717Schris Type unboxedT2 = types.unboxedTypeOrType(t2); 127107717Schris 12863711Ssheldonh if (unboxedT1.isNumeric() && unboxedT2.isNumeric()) { 12963711Ssheldonh if (unboxedT1.hasTag(TypeTag.DOUBLE) || unboxedT2.hasTag(TypeTag.DOUBLE)) { 130115138Shmp return syms.doubleType; 13163711Ssheldonh } else if (unboxedT1.hasTag(TypeTag.FLOAT) || unboxedT2.hasTag(TypeTag.FLOAT)) { 132116296Sharti return syms.floatType; 13363711Ssheldonh } else if (unboxedT1.hasTag(TypeTag.LONG) || unboxedT2.hasTag(TypeTag.LONG)) { 13463714Ssheldonh return syms.longType; 13563711Ssheldonh } else { 136116810Sharti return syms.intType; 13763714Ssheldonh } 13863714Ssheldonh } else if (types.isSameType(unboxedT1, unboxedT2)) { 139107122Sjulian return unboxedT1; 14063714Ssheldonh } else { 14165311Sarchie return syms.objectType; 142107122Sjulian } 143107122Sjulian } 14463714Ssheldonh 14563714Ssheldonh /** 14694317Sjulian * Entry point for resolving a unary operator given an operator tag and an argument type. 14763711Ssheldonh */ 14863714Ssheldonh Symbol resolveUnary(DiagnosticPosition pos, JCTree.Tag tag, Type op) { 14983998Sbrooks return resolve(tag, 150107122Sjulian unaryOperators, 151107122Sjulian unop -> unop.test(op), 15263714Ssheldonh unop -> unop.resolve(op), 15363714Ssheldonh () -> reportErrorIfNeeded(pos, tag, op)); 15484053Sbrooks } 15563714Ssheldonh 156107122Sjulian /** 157102196Sarchie * Entry point for resolving a binary operator given an operator tag and a pair of argument types. 15863714Ssheldonh */ 15963714Ssheldonh Symbol resolveBinary(DiagnosticPosition pos, JCTree.Tag tag, Type op1, Type op2) { 16068810Sarchie return resolve(tag, 16163714Ssheldonh binaryOperators, 16263714Ssheldonh binop -> binop.test(op1, op2), 16363714Ssheldonh binop -> binop.resolve(op1, op2), 16463714Ssheldonh () -> reportErrorIfNeeded(pos, tag, op1, op2)); 16563714Ssheldonh } 16663714Ssheldonh 16763714Ssheldonh /** 168107122Sjulian * Main operator lookup routine; lookup an operator (either unary or binary) in its corresponding 16963714Ssheldonh * map. If there's a matching operator, its resolve routine is called and the result is returned; 17076479Swpaul * otherwise the result of a fallback function is returned. 17179417Sjulian */ 17263711Ssheldonh private <O> Symbol resolve(Tag tag, Map<Name, List<O>> opMap, Predicate<O> opTestFunc, 17363711Ssheldonh Function<O, Symbol> resolveFunc, Supplier<Symbol> noResultFunc) { 17499635Simp return opMap.get(operatorName(tag)).stream() 17577597Simp .filter(opTestFunc) 17663711Ssheldonh .map(resolveFunc) 177117633Sharti .findFirst() 17863711Ssheldonh .orElseGet(noResultFunc); 17999639Simp } 18099639Simp 18182095Simp /** 18263711Ssheldonh * Creates an operator symbol. 18366131Swpaul */ 18463711Ssheldonh private Symbol makeOperator(Name name, List<OperatorType> formals, OperatorType res, int... opcodes) { 18590687Sluigi MethodType opType = new MethodType( 18663711Ssheldonh formals.stream() 18763711Ssheldonh .map(o -> o.asType(syms)) 18863711Ssheldonh .collect(List.collector()), 18963711Ssheldonh res.asType(syms), List.nil(), syms.methodClass); 19063711Ssheldonh return new OperatorSymbol(name, opType, mergeOpcodes(opcodes), syms.noSymbol); 191104041Ssos } 19263711Ssheldonh 19363711Ssheldonh /** 19490731Sjhay * Fold two opcodes in a single int value (if required). 195105503Sscottl */ 19672671Smarkm private int mergeOpcodes(int... opcodes) { 19763711Ssheldonh int opcodesLen = opcodes.length; 198112102Ssam Assert.check(opcodesLen == 1 || opcodesLen == 2); 19963711Ssheldonh return (opcodesLen == 1) ? 20083113Sobrien opcodes[0] : 201114577Sakiyama ((opcodes[0] << ByteCodes.preShift) | opcodes[1]); 20263711Ssheldonh } 20363711Ssheldonh 204105934Ssimokawa /** 205113429Sfjoe * Report an operator lookup error. 20663711Ssheldonh */ 20763711Ssheldonh private Symbol reportErrorIfNeeded(DiagnosticPosition pos, Tag tag, Type... args) { 20863711Ssheldonh if (Stream.of(args).noneMatch(Type::isErroneous)) { 209109218Stjr Name opName = operatorName(tag); 21063711Ssheldonh JCDiagnostic.Error opError = (args.length) == 1 ? 21163711Ssheldonh Errors.OperatorCantBeApplied(opName, args[0]) : 21263711Ssheldonh Errors.OperatorCantBeApplied1(opName, args[0], args[1]); 21363711Ssheldonh log.error(pos, opError); 21463711Ssheldonh } 21563711Ssheldonh return syms.noSymbol; 21663711Ssheldonh } 21763711Ssheldonh 21863711Ssheldonh /** 21963711Ssheldonh * Return name of operator with given tree tag. 22068480Simp */ 22163711Ssheldonh public Name operatorName(JCTree.Tag tag) { 22297010Swill return opname[tag.operatorIndex()]; 22363711Ssheldonh } 22463711Ssheldonh 22563711Ssheldonh /** 22663711Ssheldonh * The constants in this enum represent the types upon which all the operator helpers 22763711Ssheldonh * operate upon. This allows lazy and consise mapping between a type name and a type instance. 22863711Ssheldonh */ 229107191Sru enum OperatorType { 23063711Ssheldonh BYTE(syms -> syms.byteType), 23163711Ssheldonh SHORT(syms -> syms.shortType), 23263711Ssheldonh INT(syms -> syms.intType), 23363711Ssheldonh LONG(syms -> syms.longType), 234107238Snjl FLOAT(syms -> syms.floatType), 23563711Ssheldonh DOUBLE(syms -> syms.doubleType), 23674047Sphk CHAR(syms -> syms.charType), 23763711Ssheldonh BOOLEAN(syms -> syms.booleanType), 23863711Ssheldonh OBJECT(syms -> syms.objectType), 23963711Ssheldonh STRING(syms -> syms.stringType), 240107945Scognet BOT(syms -> syms.botType); 24163711Ssheldonh 24263711Ssheldonh final Function<Symtab, Type> asTypeFunc; 24363711Ssheldonh 24463711Ssheldonh OperatorType(Function<Symtab, Type> asTypeFunc) { 24580219Swpaul this.asTypeFunc = asTypeFunc; 246107754Skan } 247104488Ssam 248114878Sjulian Type asType(Symtab syms) { 24995066Strhodes return asTypeFunc.apply(syms); 25063711Ssheldonh } 25163711Ssheldonh } 252101703Sjoe 25386659Sjoe /** 25463711Ssheldonh * Common root for all operator helpers. An operator helper instance is associated with a 25595066Strhodes * given operator (i.e. '+'); it contains routines to perform operator lookup, i.e. find 25695066Strhodes * which version of the '+' operator is the best given an argument type list. Supported 25763711Ssheldonh * operator symbols are initialized lazily upon first lookup request - this is in order to avoid 25895066Strhodes * initialization circularities between this class and {@code Symtab}. 25963711Ssheldonh */ 26095066Strhodes abstract class OperatorHelper { 26163711Ssheldonh 26263711Ssheldonh /** The operator name. */ 26395066Strhodes final Name name; 26463711Ssheldonh 26563711Ssheldonh /** The list of symbols associated with this operator (lazily populated). */ 26667628Sasmodai Optional<Symbol[]> alternatives = Optional.empty(); 267116258Sharti 268101766Strhodes /** An array of operator symbol suppliers (used to lazily populate the symbol list). */ 26995066Strhodes List<Supplier<Symbol>> operatorSuppliers = List.nil(); 27063711Ssheldonh 27163711Ssheldonh @SuppressWarnings("varargs") 27280494Syar OperatorHelper(Tag tag) { 27363711Ssheldonh this.name = operatorName(tag); 27463711Ssheldonh } 275116874Ssmkelly 27663711Ssheldonh /** 27763711Ssheldonh * This routine implements the main operator lookup process. Each operator is tested 27874931Sjhb * using an applicability predicate; if the test suceeds that same operator is returned, 279113135Strhodes * otherwise a dummy symbol is returned. 28063711Ssheldonh */ 28163711Ssheldonh final Symbol doLookup(Predicate<Symbol> applicabilityTest) { 28263711Ssheldonh return Stream.of(alternatives.orElseGet(this::initOperators)) 28379727Sschweikh .filter(applicabilityTest) 28415136Smpp .findFirst() 28584533Syar .orElse(syms.noSymbol); 286110961Strhodes } 28784533Syar 288110961Strhodes /** 289110961Strhodes * This routine performs lazy instantiation of the operator symbols supported by this helper. 290110961Strhodes * After initialization is done, the suppliers are cleared, to free up memory. 29162586Sroberto */ 292110961Strhodes private Symbol[] initOperators() { 293110961Strhodes Symbol[] operators = operatorSuppliers.stream() 294110961Strhodes .map(op -> op.get()) 295110961Strhodes .toArray(Symbol[]::new); 296110961Strhodes alternatives = Optional.of(operators); 297110961Strhodes operatorSuppliers = null; //let GC do its work 298110961Strhodes return operators; 299115766Sharti } 300110961Strhodes } 301116735Sharti 30260990Sobrien /** 30345678Sbde * Common superclass for all unary operator helpers. 304110961Strhodes */ 305110961Strhodes abstract class UnaryOperatorHelper extends OperatorHelper implements Predicate<Type> { 306116166Stmm 307110961Strhodes UnaryOperatorHelper(Tag tag) { 308110961Strhodes super(tag); 309110961Strhodes } 310116492Sharti 311116166Stmm /** 31245678Sbde * This routine implements the unary operator lookup process. It customizes the behavior 313110961Strhodes * of the shared lookup routine in {@link OperatorHelper}, by using an unary applicability test 314110961Strhodes * (see {@link UnaryOperatorHelper#isUnaryOperatorApplicable(OperatorSymbol, Type)} 31588988Sdd */ 31645678Sbde final Symbol doLookup(Type t) { 31774047Sphk return doLookup(op -> isUnaryOperatorApplicable((OperatorSymbol)op, t)); 31853197Sphantom } 3191638Srgrimes 320110961Strhodes /** 321112738Simp * Unary operator applicability test - is the input type the same as the expected operand type? 322117633Sharti */ 323115880Sru boolean isUnaryOperatorApplicable(OperatorSymbol op, Type t) { 32463665Ssheldonh return types.isSameType(op.type.getParameterTypes().head, t); 325110961Strhodes } 32653197Sphantom 327110961Strhodes /** 328110961Strhodes * Adds a unary operator symbol. 329113429Sfjoe */ 33060443Sphantom final UnaryOperatorHelper addUnaryOperator(OperatorType arg, OperatorType res, int... opcode) { 33160443Sphantom operatorSuppliers = operatorSuppliers.prepend(() -> makeOperator(name, List.of(arg), res, opcode)); 33245678Sbde return this; 33391996Sdd } 334110961Strhodes 335110961Strhodes /** 336110961Strhodes * This method will be overridden by unary operator helpers to provide custom resolution 337110961Strhodes * logic. 33854171Sbde */ 339110961Strhodes abstract Symbol resolve(Type t); 340110961Strhodes } 341110961Strhodes 34253197Sphantom abstract class BinaryOperatorHelper extends OperatorHelper implements BiPredicate<Type, Type> { 343107191Sru 34453197Sphantom BinaryOperatorHelper(Tag tag) { 345110961Strhodes super(tag); 346110961Strhodes } 347110961Strhodes 348110961Strhodes /** 349110961Strhodes * This routine implements the binary operator lookup process. It customizes the behavior 350110961Strhodes * of the shared lookup routine in {@link OperatorHelper}, by using an unary applicability test 35143102Snsouch * (see {@link BinaryOperatorHelper#isBinaryOperatorApplicable(OperatorSymbol, Type, Type)} 352110961Strhodes */ 353110961Strhodes final Symbol doLookup(Type t1, Type t2) { 354110961Strhodes return doLookup(op -> isBinaryOperatorApplicable((OperatorSymbol)op, t1, t2)); 355110961Strhodes } 35617013Swosch 3571788Srgrimes /** 35872891Sru * Binary operator applicability test - are the input types the same as the expected operand types? 35972891Sru */ 36017013Swosch boolean isBinaryOperatorApplicable(OperatorSymbol op, Type t1, Type t2) { 3611638Srgrimes List<Type> formals = op.type.getParameterTypes(); 36217013Swosch return types.isSameType(formals.head, t1) && 363 types.isSameType(formals.tail.head, t2); 364 } 365 366 /** 367 * Adds a binary operator symbol. 368 */ 369 final BinaryOperatorHelper addBinaryOperator(OperatorType arg1, OperatorType arg2, OperatorType res, int... opcode) { 370 operatorSuppliers = operatorSuppliers.prepend(() -> makeOperator(name, List.of(arg1, arg2), res, opcode)); 371 return this; 372 } 373 374 /** 375 * This method will be overridden by binary operator helpers to provide custom resolution 376 * logic. 377 */ 378 abstract Symbol resolve(Type t1, Type t2); 379 } 380 381 /** 382 * Class representing unary operator helpers that operate on reference types. 383 */ 384 class UnaryReferenceOperator extends UnaryOperatorHelper { 385 386 UnaryReferenceOperator(Tag tag) { 387 super(tag); 388 } 389 390 @Override 391 public boolean test(Type type) { 392 return type.isNullOrReference(); 393 } 394 395 @Override 396 public Symbol resolve(Type arg) { 397 return doLookup(syms.objectType); 398 } 399 } 400 401 /** 402 * Class representing unary operator helpers that operate on numeric types (either boxed or unboxed). 403 * Operator lookup is performed after applying numeric promotion of the input type. 404 */ 405 class UnaryNumericOperator extends UnaryOperatorHelper { 406 407 Predicate<Type> numericTest; 408 409 UnaryNumericOperator(Tag tag) { 410 this(tag, Type::isNumeric); 411 } 412 413 UnaryNumericOperator(Tag tag, Predicate<Type> numericTest) { 414 super(tag); 415 this.numericTest = numericTest; 416 } 417 418 @Override 419 public boolean test(Type type) { 420 return numericTest.test(unaryPromotion(type)); 421 } 422 423 @Override 424 public Symbol resolve(Type arg) { 425 return doLookup(unaryPromotion(arg)); 426 } 427 } 428 429 /** 430 * Class representing unary operator helpers that operate on boolean types (either boxed or unboxed). 431 * Operator lookup is performed assuming the input type is a boolean type. 432 */ 433 class UnaryBooleanOperator extends UnaryOperatorHelper { 434 435 UnaryBooleanOperator(Tag tag) { 436 super(tag); 437 } 438 439 @Override 440 public boolean test(Type type) { 441 return types.unboxedTypeOrType(type).hasTag(TypeTag.BOOLEAN); 442 } 443 444 @Override 445 public Symbol resolve(Type arg) { 446 return doLookup(syms.booleanType); 447 } 448 } 449 450 /** 451 * Class representing prefix/postfix unary operator helpers. Operates on numeric types (either 452 * boxed or unboxed). Operator lookup is performed on the unboxed version of the input type. 453 */ 454 class UnaryPrefixPostfixOperator extends UnaryNumericOperator { 455 456 UnaryPrefixPostfixOperator(Tag tag) { 457 super(tag); 458 } 459 460 @Override 461 public Symbol resolve(Type arg) { 462 return doLookup(types.unboxedTypeOrType(arg)); 463 } 464 } 465 466 /** 467 * Class representing binary operator helpers that operate on numeric types (either boxed or unboxed). 468 * Operator lookup is performed after applying binary numeric promotion of the input types. 469 */ 470 class BinaryNumericOperator extends BinaryOperatorHelper { 471 472 Predicate<Type> numericTest; 473 474 BinaryNumericOperator(Tag tag) { 475 this(tag, Type::isNumeric); 476 } 477 478 BinaryNumericOperator(Tag tag, Predicate<Type> numericTest) { 479 super(tag); 480 this.numericTest = numericTest; 481 } 482 483 @Override 484 public Symbol resolve(Type arg1, Type arg2) { 485 Type t = binaryPromotion(arg1, arg2); 486 return doLookup(t, t); 487 } 488 489 @Override 490 public boolean test(Type arg1, Type arg2) { 491 return numericTest.test(unaryPromotion(arg1)) && 492 numericTest.test(unaryPromotion(arg2)); 493 } 494 } 495 496 /** 497 * Class representing bitwise operator helpers that operate on boolean types (either boxed or unboxed). 498 * Operator lookup is performed assuming both input types are boolean types. 499 */ 500 class BinaryBooleanOperator extends BinaryOperatorHelper { 501 502 BinaryBooleanOperator(Tag tag) { 503 super(tag); 504 } 505 506 @Override 507 public Symbol resolve(Type arg1, Type arg2) { 508 return doLookup(syms.booleanType, syms.booleanType); 509 } 510 511 @Override 512 public boolean test(Type arg1, Type arg2) { 513 return types.unboxedTypeOrType(arg1).hasTag(TypeTag.BOOLEAN) && 514 types.unboxedTypeOrType(arg2).hasTag(TypeTag.BOOLEAN); 515 } 516 } 517 518 /** 519 * Class representing string concatenation operator helper that operates on at least an 520 * string operand. Input types subject to an operator lookup undergoes a special string promotion 521 * (see {@link BinaryStringOperator#stringPromotion(Type)}. 522 */ 523 class BinaryStringOperator extends BinaryOperatorHelper { 524 525 BinaryStringOperator(Tag tag) { 526 super(tag); 527 } 528 529 @Override 530 public Symbol resolve(Type arg1, Type arg2) { 531 return doLookup(stringPromotion(arg1), stringPromotion(arg2)); 532 } 533 534 @Override 535 public boolean test(Type arg1, Type arg2) { 536 boolean hasStringOp = types.isSameType(arg1, syms.stringType) || 537 types.isSameType(arg2, syms.stringType); 538 boolean hasVoidOp = arg1.hasTag(TypeTag.VOID) || arg2.hasTag(TypeTag.VOID); 539 return hasStringOp && !hasVoidOp; 540 } 541 542 /** 543 * This routine applies following mappings: 544 * - if input type is primitive, apply numeric promotion 545 * - if input type is either 'void', 'null' or 'String' leave it untouched 546 * - otherwise return 'Object' 547 */ 548 private Type stringPromotion(Type t) { 549 if (t.isPrimitive()) { 550 return unaryPromotion(t); 551 } else if (t.hasTag(TypeTag.VOID) || t.hasTag(TypeTag.BOT) || 552 types.isSameType(t, syms.stringType)) { 553 return t; 554 } else if (t.hasTag(TypeTag.TYPEVAR)) { 555 return stringPromotion(t.getUpperBound()); 556 } else { 557 return syms.objectType; 558 } 559 } 560 } 561 562 /** 563 * Class representing shift operator helper that operates on integral operand types (either boxed 564 * or unboxed). Operator lookup is performed after applying unary numeric promotion to each input type. 565 */ 566 class BinaryShiftOperator extends BinaryOperatorHelper { 567 568 BinaryShiftOperator(Tag tag) { 569 super(tag); 570 } 571 572 @Override 573 public Symbol resolve(Type arg1, Type arg2) { 574 return doLookup(unaryPromotion(arg1), unaryPromotion(arg2)); 575 } 576 577 @Override 578 public boolean test(Type arg1, Type arg2) { 579 TypeTag op1 = unaryPromotion(arg1).getTag(); 580 TypeTag op2 = unaryPromotion(arg2).getTag(); 581 return (op1 == TypeTag.LONG || op1 == TypeTag.INT) && 582 (op2 == TypeTag.LONG || op2 == TypeTag.INT); 583 } 584 } 585 586 /** 587 * This enum represent the possible kinds of an comparison test ('==' and '!='). 588 */ 589 enum ComparisonKind { 590 /** equality between numeric or boolean operands. */ 591 NUMERIC_OR_BOOLEAN, 592 /** equality between reference operands. */ 593 REFERENCE, 594 /** erroneous equality */ 595 INVALID 596 } 597 598 /** 599 * Class representing equality operator helper that operates on either numeric, boolean or reference 600 * types. Operator lookup for numeric/boolean equality test is performed after binary numeric 601 * promotion to the input types. Operator lookup for reference equality test is performed assuming 602 * the input type is 'Object'. 603 */ 604 class BinaryEqualityOperator extends BinaryOperatorHelper { 605 606 BinaryEqualityOperator(Tag tag) { 607 super(tag); 608 } 609 610 @Override 611 public boolean test(Type arg1, Type arg2) { 612 return getKind(arg1, arg2) != ComparisonKind.INVALID; 613 } 614 615 @Override 616 public Symbol resolve(Type t1, Type t2) { 617 ComparisonKind kind = getKind(t1, t2); 618 Type t = (kind == ComparisonKind.NUMERIC_OR_BOOLEAN) ? 619 binaryPromotion(t1, t2) : 620 syms.objectType; 621 return doLookup(t, t); 622 } 623 624 /** 625 * Retrieve the comparison kind associated with the given argument type pair. 626 */ 627 private ComparisonKind getKind(Type arg1, Type arg2) { 628 boolean arg1Primitive = arg1.isPrimitive(); 629 boolean arg2Primitive = arg2.isPrimitive(); 630 if (arg1Primitive && arg2Primitive) { 631 return ComparisonKind.NUMERIC_OR_BOOLEAN; 632 } else if (arg1Primitive) { 633 return unaryPromotion(arg2).isPrimitive() ? 634 ComparisonKind.NUMERIC_OR_BOOLEAN : ComparisonKind.INVALID; 635 } else if (arg2Primitive) { 636 return unaryPromotion(arg1).isPrimitive() ? 637 ComparisonKind.NUMERIC_OR_BOOLEAN : ComparisonKind.INVALID; 638 } else { 639 return arg1.isNullOrReference() && arg2.isNullOrReference() ? 640 ComparisonKind.REFERENCE : ComparisonKind.INVALID; 641 } 642 } 643 } 644 645 /** 646 * Initialize all unary operators. 647 */ 648 private void initUnaryOperators() { 649 initOperators(unaryOperators, 650 new UnaryNumericOperator(Tag.POS) 651 .addUnaryOperator(DOUBLE, DOUBLE, nop) 652 .addUnaryOperator(FLOAT, FLOAT, nop) 653 .addUnaryOperator(LONG, LONG, nop) 654 .addUnaryOperator(INT, INT, nop), 655 new UnaryNumericOperator(Tag.NEG) 656 .addUnaryOperator(DOUBLE, DOUBLE, dneg) 657 .addUnaryOperator(FLOAT, FLOAT, fneg) 658 .addUnaryOperator(LONG, LONG, lneg) 659 .addUnaryOperator(INT, INT, ineg), 660 new UnaryNumericOperator(Tag.COMPL, Type::isIntegral) 661 .addUnaryOperator(LONG, LONG, lxor) 662 .addUnaryOperator(INT, INT, ixor), 663 new UnaryPrefixPostfixOperator(Tag.POSTINC) 664 .addUnaryOperator(DOUBLE, DOUBLE, dadd) 665 .addUnaryOperator(FLOAT, FLOAT, fadd) 666 .addUnaryOperator(LONG, LONG, ladd) 667 .addUnaryOperator(INT, INT, iadd) 668 .addUnaryOperator(CHAR, CHAR, iadd) 669 .addUnaryOperator(SHORT, SHORT, iadd) 670 .addUnaryOperator(BYTE, BYTE, iadd), 671 new UnaryPrefixPostfixOperator(Tag.POSTDEC) 672 .addUnaryOperator(DOUBLE, DOUBLE, dsub) 673 .addUnaryOperator(FLOAT, FLOAT, fsub) 674 .addUnaryOperator(LONG, LONG, lsub) 675 .addUnaryOperator(INT, INT, isub) 676 .addUnaryOperator(CHAR, CHAR, isub) 677 .addUnaryOperator(SHORT, SHORT, isub) 678 .addUnaryOperator(BYTE, BYTE, isub), 679 new UnaryBooleanOperator(Tag.NOT) 680 .addUnaryOperator(BOOLEAN, BOOLEAN, bool_not), 681 new UnaryReferenceOperator(Tag.NULLCHK) 682 .addUnaryOperator(OBJECT, OBJECT, nullchk)); 683 } 684 685 /** 686 * Initialize all binary operators. 687 */ 688 private void initBinaryOperators() { 689 initOperators(binaryOperators, 690 new BinaryStringOperator(Tag.PLUS) 691 .addBinaryOperator(STRING, OBJECT, STRING, string_add) 692 .addBinaryOperator(OBJECT, STRING, STRING, string_add) 693 .addBinaryOperator(STRING, STRING, STRING, string_add) 694 .addBinaryOperator(STRING, INT, STRING, string_add) 695 .addBinaryOperator(STRING, LONG, STRING, string_add) 696 .addBinaryOperator(STRING, FLOAT, STRING, string_add) 697 .addBinaryOperator(STRING, DOUBLE, STRING, string_add) 698 .addBinaryOperator(STRING, BOOLEAN, STRING, string_add) 699 .addBinaryOperator(STRING, BOT, STRING, string_add) 700 .addBinaryOperator(INT, STRING, STRING, string_add) 701 .addBinaryOperator(LONG, STRING, STRING, string_add) 702 .addBinaryOperator(FLOAT, STRING, STRING, string_add) 703 .addBinaryOperator(DOUBLE, STRING, STRING, string_add) 704 .addBinaryOperator(BOOLEAN, STRING, STRING, string_add) 705 .addBinaryOperator(BOT, STRING, STRING, string_add), 706 new BinaryNumericOperator(Tag.PLUS) 707 .addBinaryOperator(DOUBLE, DOUBLE, DOUBLE, dadd) 708 .addBinaryOperator(FLOAT, FLOAT, FLOAT, fadd) 709 .addBinaryOperator(LONG, LONG, LONG, ladd) 710 .addBinaryOperator(INT, INT, INT, iadd), 711 new BinaryNumericOperator(Tag.MINUS) 712 .addBinaryOperator(DOUBLE, DOUBLE, DOUBLE, dsub) 713 .addBinaryOperator(FLOAT, FLOAT, FLOAT, fsub) 714 .addBinaryOperator(LONG, LONG, LONG, lsub) 715 .addBinaryOperator(INT, INT, INT, isub), 716 new BinaryNumericOperator(Tag.MUL) 717 .addBinaryOperator(DOUBLE, DOUBLE, DOUBLE, dmul) 718 .addBinaryOperator(FLOAT, FLOAT, FLOAT, fmul) 719 .addBinaryOperator(LONG, LONG, LONG, lmul) 720 .addBinaryOperator(INT, INT, INT, imul), 721 new BinaryNumericOperator(Tag.DIV) 722 .addBinaryOperator(DOUBLE, DOUBLE, DOUBLE, ddiv) 723 .addBinaryOperator(FLOAT, FLOAT, FLOAT, fdiv) 724 .addBinaryOperator(LONG, LONG, LONG, ldiv) 725 .addBinaryOperator(INT, INT, INT, idiv), 726 new BinaryNumericOperator(Tag.MOD) 727 .addBinaryOperator(DOUBLE, DOUBLE, DOUBLE, dmod) 728 .addBinaryOperator(FLOAT, FLOAT, FLOAT, fmod) 729 .addBinaryOperator(LONG, LONG, LONG, lmod) 730 .addBinaryOperator(INT, INT, INT, imod), 731 new BinaryBooleanOperator(Tag.BITAND) 732 .addBinaryOperator(BOOLEAN, BOOLEAN, BOOLEAN, iand), 733 new BinaryNumericOperator(Tag.BITAND, Type::isIntegral) 734 .addBinaryOperator(LONG, LONG, LONG, land) 735 .addBinaryOperator(INT, INT, INT, iand), 736 new BinaryBooleanOperator(Tag.BITOR) 737 .addBinaryOperator(BOOLEAN, BOOLEAN, BOOLEAN, ior), 738 new BinaryNumericOperator(Tag.BITOR, Type::isIntegral) 739 .addBinaryOperator(LONG, LONG, LONG, lor) 740 .addBinaryOperator(INT, INT, INT, ior), 741 new BinaryBooleanOperator(Tag.BITXOR) 742 .addBinaryOperator(BOOLEAN, BOOLEAN, BOOLEAN, ixor), 743 new BinaryNumericOperator(Tag.BITXOR, Type::isIntegral) 744 .addBinaryOperator(LONG, LONG, LONG, lxor) 745 .addBinaryOperator(INT, INT, INT, ixor), 746 new BinaryShiftOperator(Tag.SL) 747 .addBinaryOperator(INT, INT, INT, ishl) 748 .addBinaryOperator(INT, LONG, INT, ishll) 749 .addBinaryOperator(LONG, INT, LONG, lshl) 750 .addBinaryOperator(LONG, LONG, LONG, lshll), 751 new BinaryShiftOperator(Tag.SR) 752 .addBinaryOperator(INT, INT, INT, ishr) 753 .addBinaryOperator(INT, LONG, INT, ishrl) 754 .addBinaryOperator(LONG, INT, LONG, lshr) 755 .addBinaryOperator(LONG, LONG, LONG, lshrl), 756 new BinaryShiftOperator(Tag.USR) 757 .addBinaryOperator(INT, INT, INT, iushr) 758 .addBinaryOperator(INT, LONG, INT, iushrl) 759 .addBinaryOperator(LONG, INT, LONG, lushr) 760 .addBinaryOperator(LONG, LONG, LONG, lushrl), 761 new BinaryNumericOperator(Tag.LT) 762 .addBinaryOperator(DOUBLE, DOUBLE, BOOLEAN, dcmpg, iflt) 763 .addBinaryOperator(FLOAT, FLOAT, BOOLEAN, fcmpg, iflt) 764 .addBinaryOperator(LONG, LONG, BOOLEAN, lcmp, iflt) 765 .addBinaryOperator(INT, INT, BOOLEAN, if_icmplt), 766 new BinaryNumericOperator(Tag.GT) 767 .addBinaryOperator(DOUBLE, DOUBLE, BOOLEAN, dcmpl, ifgt) 768 .addBinaryOperator(FLOAT, FLOAT, BOOLEAN, fcmpl, ifgt) 769 .addBinaryOperator(LONG, LONG, BOOLEAN, lcmp, ifgt) 770 .addBinaryOperator(INT, INT, BOOLEAN, if_icmpgt), 771 new BinaryNumericOperator(Tag.LE) 772 .addBinaryOperator(DOUBLE, DOUBLE, BOOLEAN, dcmpg, ifle) 773 .addBinaryOperator(FLOAT, FLOAT, BOOLEAN, fcmpg, ifle) 774 .addBinaryOperator(LONG, LONG, BOOLEAN, lcmp, ifle) 775 .addBinaryOperator(INT, INT, BOOLEAN, if_icmple), 776 new BinaryNumericOperator(Tag.GE) 777 .addBinaryOperator(DOUBLE, DOUBLE, BOOLEAN, dcmpl, ifge) 778 .addBinaryOperator(FLOAT, FLOAT, BOOLEAN, fcmpl, ifge) 779 .addBinaryOperator(LONG, LONG, BOOLEAN, lcmp, ifge) 780 .addBinaryOperator(INT, INT, BOOLEAN, if_icmpge), 781 new BinaryEqualityOperator(Tag.EQ) 782 .addBinaryOperator(OBJECT, OBJECT, BOOLEAN, if_acmpeq) 783 .addBinaryOperator(BOOLEAN, BOOLEAN, BOOLEAN, if_icmpeq) 784 .addBinaryOperator(DOUBLE, DOUBLE, BOOLEAN, dcmpl, ifeq) 785 .addBinaryOperator(FLOAT, FLOAT, BOOLEAN, fcmpl, ifeq) 786 .addBinaryOperator(LONG, LONG, BOOLEAN, lcmp, ifeq) 787 .addBinaryOperator(INT, INT, BOOLEAN, if_icmpeq), 788 new BinaryEqualityOperator(Tag.NE) 789 .addBinaryOperator(OBJECT, OBJECT, BOOLEAN, if_acmpne) 790 .addBinaryOperator(BOOLEAN, BOOLEAN, BOOLEAN, if_icmpne) 791 .addBinaryOperator(DOUBLE, DOUBLE, BOOLEAN, dcmpl, ifne) 792 .addBinaryOperator(FLOAT, FLOAT, BOOLEAN, fcmpl, ifne) 793 .addBinaryOperator(LONG, LONG, BOOLEAN, lcmp, ifne) 794 .addBinaryOperator(INT, INT, BOOLEAN, if_icmpne), 795 new BinaryBooleanOperator(Tag.AND) 796 .addBinaryOperator(BOOLEAN, BOOLEAN, BOOLEAN, bool_and), 797 new BinaryBooleanOperator(Tag.OR) 798 .addBinaryOperator(BOOLEAN, BOOLEAN, BOOLEAN, bool_or)); 799 } 800 801 /** 802 * Complete the initialization of an operator helper by storing it into the corresponding operator map. 803 */ 804 @SafeVarargs 805 private final <O extends OperatorHelper> void initOperators(Map<Name, List<O>> opsMap, O... ops) { 806 for (O o : ops) { 807 Name opName = o.name; 808 List<O> helpers = opsMap.getOrDefault(opName, List.nil()); 809 opsMap.put(opName, helpers.prepend(o)); 810 } 811 } 812 813 /** 814 * Initialize operator name array. 815 */ 816 private void initOperatorNames() { 817 setOperatorName(Tag.POS, "+"); 818 setOperatorName(Tag.NEG, "-"); 819 setOperatorName(Tag.NOT, "!"); 820 setOperatorName(Tag.COMPL, "~"); 821 setOperatorName(Tag.PREINC, "++"); 822 setOperatorName(Tag.PREDEC, "--"); 823 setOperatorName(Tag.POSTINC, "++"); 824 setOperatorName(Tag.POSTDEC, "--"); 825 setOperatorName(Tag.NULLCHK, "<*nullchk*>"); 826 setOperatorName(Tag.OR, "||"); 827 setOperatorName(Tag.AND, "&&"); 828 setOperatorName(Tag.EQ, "=="); 829 setOperatorName(Tag.NE, "!="); 830 setOperatorName(Tag.LT, "<"); 831 setOperatorName(Tag.GT, ">"); 832 setOperatorName(Tag.LE, "<="); 833 setOperatorName(Tag.GE, ">="); 834 setOperatorName(Tag.BITOR, "|"); 835 setOperatorName(Tag.BITXOR, "^"); 836 setOperatorName(Tag.BITAND, "&"); 837 setOperatorName(Tag.SL, "<<"); 838 setOperatorName(Tag.SR, ">>"); 839 setOperatorName(Tag.USR, ">>>"); 840 setOperatorName(Tag.PLUS, "+"); 841 setOperatorName(Tag.MINUS, names.hyphen); 842 setOperatorName(Tag.MUL, names.asterisk); 843 setOperatorName(Tag.DIV, names.slash); 844 setOperatorName(Tag.MOD, "%"); 845 } 846 //where 847 private void setOperatorName(Tag tag, String name) { 848 setOperatorName(tag, names.fromString(name)); 849 } 850 851 private void setOperatorName(Tag tag, Name name) { 852 opname[tag.operatorIndex()] = name; 853 } 854} 855