Operators.java revision 3541:9f8da995da88
1/* 2 * Copyright (c) 2015, 2016, 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 26package com.sun.tools.javac.comp; 27 28import com.sun.tools.javac.code.Symbol; 29import com.sun.tools.javac.code.Symbol.OperatorSymbol; 30import com.sun.tools.javac.code.Symtab; 31import com.sun.tools.javac.code.Type; 32import com.sun.tools.javac.code.Type.MethodType; 33import com.sun.tools.javac.code.TypeTag; 34import com.sun.tools.javac.code.Types; 35import com.sun.tools.javac.jvm.ByteCodes; 36import com.sun.tools.javac.resources.CompilerProperties.Errors; 37import com.sun.tools.javac.tree.JCTree; 38import com.sun.tools.javac.tree.JCTree.Tag; 39import com.sun.tools.javac.util.Assert; 40import com.sun.tools.javac.util.Context; 41import com.sun.tools.javac.util.JCDiagnostic; 42import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; 43import com.sun.tools.javac.util.List; 44import com.sun.tools.javac.util.Log; 45import com.sun.tools.javac.util.Name; 46import com.sun.tools.javac.util.Names; 47 48import java.util.HashMap; 49import java.util.Map; 50import java.util.Optional; 51import java.util.function.BiPredicate; 52import java.util.function.Function; 53import java.util.function.Predicate; 54import java.util.function.Supplier; 55import java.util.stream.Stream; 56 57import static com.sun.tools.javac.jvm.ByteCodes.*; 58import 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 */ 68public 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 initOperatorNames(); 99 initUnaryOperators(); 100 initBinaryOperators(); 101 } 102 103 /** 104 * Perform unary promotion of a type; this routine implements JLS 5.6.1. 105 * If the input type is not supported by unary promotion, it is returned unaltered. 106 */ 107 Type unaryPromotion(Type t) { 108 Type unboxed = types.unboxedTypeOrType(t); 109 switch (unboxed.getTag()) { 110 case BYTE: 111 case SHORT: 112 case CHAR: 113 return syms.intType; 114 default: 115 return unboxed; 116 } 117 } 118 119 /** 120 * Perform binary promotion of a pair of types; this routine implements JLS 5.6.2. 121 * If the input types are not supported by unary promotion, if such types are identical to 122 * a type C, then C is returned, otherwise Object is returned. 123 */ 124 Type binaryPromotion(Type t1, Type t2) { 125 Type unboxedT1 = types.unboxedTypeOrType(t1); 126 Type unboxedT2 = types.unboxedTypeOrType(t2); 127 128 if (unboxedT1.isNumeric() && unboxedT2.isNumeric()) { 129 if (unboxedT1.hasTag(TypeTag.DOUBLE) || unboxedT2.hasTag(TypeTag.DOUBLE)) { 130 return syms.doubleType; 131 } else if (unboxedT1.hasTag(TypeTag.FLOAT) || unboxedT2.hasTag(TypeTag.FLOAT)) { 132 return syms.floatType; 133 } else if (unboxedT1.hasTag(TypeTag.LONG) || unboxedT2.hasTag(TypeTag.LONG)) { 134 return syms.longType; 135 } else { 136 return syms.intType; 137 } 138 } else if (types.isSameType(unboxedT1, unboxedT2)) { 139 return unboxedT1; 140 } else { 141 return syms.objectType; 142 } 143 } 144 145 /** 146 * Entry point for resolving a unary operator given an operator tag and an argument type. 147 */ 148 Symbol resolveUnary(DiagnosticPosition pos, JCTree.Tag tag, Type op) { 149 return resolve(tag, 150 unaryOperators, 151 unop -> unop.test(op), 152 unop -> unop.resolve(op), 153 () -> reportErrorIfNeeded(pos, tag, op)); 154 } 155 156 /** 157 * Entry point for resolving a binary operator given an operator tag and a pair of argument types. 158 */ 159 Symbol resolveBinary(DiagnosticPosition pos, JCTree.Tag tag, Type op1, Type op2) { 160 return resolve(tag, 161 binaryOperators, 162 binop -> binop.test(op1, op2), 163 binop -> binop.resolve(op1, op2), 164 () -> reportErrorIfNeeded(pos, tag, op1, op2)); 165 } 166 167 /** 168 * Main operator lookup routine; lookup an operator (either unary or binary) in its corresponding 169 * map. If there's a matching operator, its resolve routine is called and the result is returned; 170 * otherwise the result of a fallback function is returned. 171 */ 172 private <O> Symbol resolve(Tag tag, Map<Name, List<O>> opMap, Predicate<O> opTestFunc, 173 Function<O, Symbol> resolveFunc, Supplier<Symbol> noResultFunc) { 174 return opMap.get(operatorName(tag)).stream() 175 .filter(opTestFunc) 176 .map(resolveFunc) 177 .findFirst() 178 .orElseGet(noResultFunc); 179 } 180 181 /** 182 * Creates an operator symbol. 183 */ 184 private Symbol makeOperator(Name name, List<OperatorType> formals, OperatorType res, int... opcodes) { 185 MethodType opType = new MethodType( 186 formals.stream() 187 .map(o -> o.asType(syms)) 188 .collect(List.collector()), 189 res.asType(syms), List.nil(), syms.methodClass); 190 return new OperatorSymbol(name, opType, mergeOpcodes(opcodes), syms.noSymbol); 191 } 192 193 /** 194 * Fold two opcodes in a single int value (if required). 195 */ 196 private int mergeOpcodes(int... opcodes) { 197 int opcodesLen = opcodes.length; 198 Assert.check(opcodesLen == 1 || opcodesLen == 2); 199 return (opcodesLen == 1) ? 200 opcodes[0] : 201 ((opcodes[0] << ByteCodes.preShift) | opcodes[1]); 202 } 203 204 /** 205 * Report an operator lookup error. 206 */ 207 private Symbol reportErrorIfNeeded(DiagnosticPosition pos, Tag tag, Type... args) { 208 if (Stream.of(args).noneMatch(Type::isErroneous)) { 209 Name opName = operatorName(tag); 210 JCDiagnostic.Error opError = (args.length) == 1 ? 211 Errors.OperatorCantBeApplied(opName, args[0]) : 212 Errors.OperatorCantBeApplied1(opName, args[0], args[1]); 213 log.error(pos, opError); 214 } 215 return syms.noSymbol; 216 } 217 218 /** 219 * Return name of operator with given tree tag. 220 */ 221 public Name operatorName(JCTree.Tag tag) { 222 return opname[tag.operatorIndex()]; 223 } 224 225 /** 226 * The constants in this enum represent the types upon which all the operator helpers 227 * operate upon. This allows lazy and consise mapping between a type name and a type instance. 228 */ 229 enum OperatorType { 230 BYTE(syms -> syms.byteType), 231 SHORT(syms -> syms.shortType), 232 INT(syms -> syms.intType), 233 LONG(syms -> syms.longType), 234 FLOAT(syms -> syms.floatType), 235 DOUBLE(syms -> syms.doubleType), 236 CHAR(syms -> syms.charType), 237 BOOLEAN(syms -> syms.booleanType), 238 OBJECT(syms -> syms.objectType), 239 STRING(syms -> syms.stringType), 240 BOT(syms -> syms.botType); 241 242 final Function<Symtab, Type> asTypeFunc; 243 244 OperatorType(Function<Symtab, Type> asTypeFunc) { 245 this.asTypeFunc = asTypeFunc; 246 } 247 248 Type asType(Symtab syms) { 249 return asTypeFunc.apply(syms); 250 } 251 } 252 253 /** 254 * Common root for all operator helpers. An operator helper instance is associated with a 255 * given operator (i.e. '+'); it contains routines to perform operator lookup, i.e. find 256 * which version of the '+' operator is the best given an argument type list. Supported 257 * operator symbols are initialized lazily upon first lookup request - this is in order to avoid 258 * initialization circularities between this class and {@code Symtab}. 259 */ 260 abstract class OperatorHelper { 261 262 /** The operator name. */ 263 final Name name; 264 265 /** The list of symbols associated with this operator (lazily populated). */ 266 Optional<Symbol[]> alternatives = Optional.empty(); 267 268 /** An array of operator symbol suppliers (used to lazily populate the symbol list). */ 269 List<Supplier<Symbol>> operatorSuppliers = List.nil(); 270 271 @SuppressWarnings("varargs") 272 OperatorHelper(Tag tag) { 273 this.name = operatorName(tag); 274 } 275 276 /** 277 * This routine implements the main operator lookup process. Each operator is tested 278 * using an applicability predicate; if the test suceeds that same operator is returned, 279 * otherwise a dummy symbol is returned. 280 */ 281 final Symbol doLookup(Predicate<Symbol> applicabilityTest) { 282 return Stream.of(alternatives.orElseGet(this::initOperators)) 283 .filter(applicabilityTest) 284 .findFirst() 285 .orElse(syms.noSymbol); 286 } 287 288 /** 289 * This routine performs lazy instantiation of the operator symbols supported by this helper. 290 * After initialization is done, the suppliers are cleared, to free up memory. 291 */ 292 private Symbol[] initOperators() { 293 Symbol[] operators = operatorSuppliers.stream() 294 .map(op -> op.get()) 295 .toArray(Symbol[]::new); 296 alternatives = Optional.of(operators); 297 operatorSuppliers = null; //let GC do its work 298 return operators; 299 } 300 } 301 302 /** 303 * Common superclass for all unary operator helpers. 304 */ 305 abstract class UnaryOperatorHelper extends OperatorHelper implements Predicate<Type> { 306 307 UnaryOperatorHelper(Tag tag) { 308 super(tag); 309 } 310 311 /** 312 * This routine implements the unary operator lookup process. It customizes the behavior 313 * of the shared lookup routine in {@link OperatorHelper}, by using an unary applicability test 314 * (see {@link UnaryOperatorHelper#isUnaryOperatorApplicable(OperatorSymbol, Type)} 315 */ 316 final Symbol doLookup(Type t) { 317 return doLookup(op -> isUnaryOperatorApplicable((OperatorSymbol)op, t)); 318 } 319 320 /** 321 * Unary operator applicability test - is the input type the same as the expected operand type? 322 */ 323 boolean isUnaryOperatorApplicable(OperatorSymbol op, Type t) { 324 return types.isSameType(op.type.getParameterTypes().head, t); 325 } 326 327 /** 328 * Adds a unary operator symbol. 329 */ 330 final UnaryOperatorHelper addUnaryOperator(OperatorType arg, OperatorType res, int... opcode) { 331 operatorSuppliers = operatorSuppliers.prepend(() -> makeOperator(name, List.of(arg), res, opcode)); 332 return this; 333 } 334 335 /** 336 * This method will be overridden by unary operator helpers to provide custom resolution 337 * logic. 338 */ 339 abstract Symbol resolve(Type t); 340 } 341 342 abstract class BinaryOperatorHelper extends OperatorHelper implements BiPredicate<Type, Type> { 343 344 BinaryOperatorHelper(Tag tag) { 345 super(tag); 346 } 347 348 /** 349 * This routine implements the binary operator lookup process. It customizes the behavior 350 * of the shared lookup routine in {@link OperatorHelper}, by using an unary applicability test 351 * (see {@link BinaryOperatorHelper#isBinaryOperatorApplicable(OperatorSymbol, Type, Type)} 352 */ 353 final Symbol doLookup(Type t1, Type t2) { 354 return doLookup(op -> isBinaryOperatorApplicable((OperatorSymbol)op, t1, t2)); 355 } 356 357 /** 358 * Binary operator applicability test - are the input types the same as the expected operand types? 359 */ 360 boolean isBinaryOperatorApplicable(OperatorSymbol op, Type t1, Type t2) { 361 List<Type> formals = op.type.getParameterTypes(); 362 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 Symbol lookupBinaryOp(Predicate<Symbol> applicabilityTest) { 802 return binaryOperators.values().stream() 803 .flatMap(List::stream) 804 .map(helper -> helper.doLookup(applicabilityTest)) 805 .distinct() 806 .filter(sym -> sym != syms.noSymbol) 807 .findFirst().get(); 808 } 809 810 /** 811 * Complete the initialization of an operator helper by storing it into the corresponding operator map. 812 */ 813 @SafeVarargs 814 private final <O extends OperatorHelper> void initOperators(Map<Name, List<O>> opsMap, O... ops) { 815 for (O o : ops) { 816 Name opName = o.name; 817 List<O> helpers = opsMap.getOrDefault(opName, List.nil()); 818 opsMap.put(opName, helpers.prepend(o)); 819 } 820 } 821 822 /** 823 * Initialize operator name array. 824 */ 825 private void initOperatorNames() { 826 setOperatorName(Tag.POS, "+"); 827 setOperatorName(Tag.NEG, "-"); 828 setOperatorName(Tag.NOT, "!"); 829 setOperatorName(Tag.COMPL, "~"); 830 setOperatorName(Tag.PREINC, "++"); 831 setOperatorName(Tag.PREDEC, "--"); 832 setOperatorName(Tag.POSTINC, "++"); 833 setOperatorName(Tag.POSTDEC, "--"); 834 setOperatorName(Tag.NULLCHK, "<*nullchk*>"); 835 setOperatorName(Tag.OR, "||"); 836 setOperatorName(Tag.AND, "&&"); 837 setOperatorName(Tag.EQ, "=="); 838 setOperatorName(Tag.NE, "!="); 839 setOperatorName(Tag.LT, "<"); 840 setOperatorName(Tag.GT, ">"); 841 setOperatorName(Tag.LE, "<="); 842 setOperatorName(Tag.GE, ">="); 843 setOperatorName(Tag.BITOR, "|"); 844 setOperatorName(Tag.BITXOR, "^"); 845 setOperatorName(Tag.BITAND, "&"); 846 setOperatorName(Tag.SL, "<<"); 847 setOperatorName(Tag.SR, ">>"); 848 setOperatorName(Tag.USR, ">>>"); 849 setOperatorName(Tag.PLUS, "+"); 850 setOperatorName(Tag.MINUS, names.hyphen); 851 setOperatorName(Tag.MUL, names.asterisk); 852 setOperatorName(Tag.DIV, names.slash); 853 setOperatorName(Tag.MOD, "%"); 854 } 855 //where 856 private void setOperatorName(Tag tag, String name) { 857 setOperatorName(tag, names.fromString(name)); 858 } 859 860 private void setOperatorName(Tag tag, Name name) { 861 opname[tag.operatorIndex()] = name; 862 } 863} 864