Operators.java revision 3555:3665ebc22a42
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 noOpSymbol = new OperatorSymbol(names.empty, Type.noType, -1, syms.noSymbol); 99 initOperatorNames(); 100 initUnaryOperators(); 101 initBinaryOperators(); 102 } 103 104 /** 105 * Perform unary promotion of a type; this routine implements JLS 5.6.1. 106 * If the input type is not supported by unary promotion, it is returned unaltered. 107 */ 108 Type unaryPromotion(Type t) { 109 Type unboxed = types.unboxedTypeOrType(t); 110 switch (unboxed.getTag()) { 111 case BYTE: 112 case SHORT: 113 case CHAR: 114 return syms.intType; 115 default: 116 return unboxed; 117 } 118 } 119 120 /** 121 * Perform binary promotion of a pair of types; this routine implements JLS 5.6.2. 122 * If the input types are not supported by unary promotion, if such types are identical to 123 * a type C, then C is returned, otherwise Object is returned. 124 */ 125 Type binaryPromotion(Type t1, Type t2) { 126 Type unboxedT1 = types.unboxedTypeOrType(t1); 127 Type unboxedT2 = types.unboxedTypeOrType(t2); 128 129 if (unboxedT1.isNumeric() && unboxedT2.isNumeric()) { 130 if (unboxedT1.hasTag(TypeTag.DOUBLE) || unboxedT2.hasTag(TypeTag.DOUBLE)) { 131 return syms.doubleType; 132 } else if (unboxedT1.hasTag(TypeTag.FLOAT) || unboxedT2.hasTag(TypeTag.FLOAT)) { 133 return syms.floatType; 134 } else if (unboxedT1.hasTag(TypeTag.LONG) || unboxedT2.hasTag(TypeTag.LONG)) { 135 return syms.longType; 136 } else { 137 return syms.intType; 138 } 139 } else if (types.isSameType(unboxedT1, unboxedT2)) { 140 return unboxedT1; 141 } else { 142 return syms.objectType; 143 } 144 } 145 146 /** 147 * Entry point for resolving a unary operator given an operator tag and an argument type. 148 */ 149 OperatorSymbol resolveUnary(DiagnosticPosition pos, JCTree.Tag tag, Type op) { 150 return resolve(tag, 151 unaryOperators, 152 unop -> unop.test(op), 153 unop -> unop.resolve(op), 154 () -> reportErrorIfNeeded(pos, tag, op)); 155 } 156 157 /** 158 * Entry point for resolving a binary operator given an operator tag and a pair of argument types. 159 */ 160 OperatorSymbol resolveBinary(DiagnosticPosition pos, JCTree.Tag tag, Type op1, Type op2) { 161 return resolve(tag, 162 binaryOperators, 163 binop -> binop.test(op1, op2), 164 binop -> binop.resolve(op1, op2), 165 () -> reportErrorIfNeeded(pos, tag, op1, op2)); 166 } 167 168 /** 169 * Main operator lookup routine; lookup an operator (either unary or binary) in its corresponding 170 * map. If there's a matching operator, its resolve routine is called and the result is returned; 171 * otherwise the result of a fallback function is returned. 172 */ 173 private <O> OperatorSymbol resolve(Tag tag, Map<Name, List<O>> opMap, Predicate<O> opTestFunc, 174 Function<O, OperatorSymbol> resolveFunc, Supplier<OperatorSymbol> noResultFunc) { 175 return opMap.get(operatorName(tag)).stream() 176 .filter(opTestFunc) 177 .map(resolveFunc) 178 .findFirst() 179 .orElseGet(noResultFunc); 180 } 181 182 /** 183 * Creates an operator symbol. 184 */ 185 private OperatorSymbol makeOperator(Name name, List<OperatorType> formals, OperatorType res, int... opcodes) { 186 MethodType opType = new MethodType( 187 formals.stream() 188 .map(o -> o.asType(syms)) 189 .collect(List.collector()), 190 res.asType(syms), List.nil(), syms.methodClass); 191 return new OperatorSymbol(name, opType, mergeOpcodes(opcodes), syms.noSymbol); 192 } 193 194 /** 195 * Fold two opcodes in a single int value (if required). 196 */ 197 private int mergeOpcodes(int... opcodes) { 198 int opcodesLen = opcodes.length; 199 Assert.check(opcodesLen == 1 || opcodesLen == 2); 200 return (opcodesLen == 1) ? 201 opcodes[0] : 202 ((opcodes[0] << ByteCodes.preShift) | opcodes[1]); 203 } 204 205 /** A symbol that stands for a missing operator. 206 */ 207 public final OperatorSymbol noOpSymbol; 208 209 /** 210 * Report an operator lookup error. 211 */ 212 private OperatorSymbol reportErrorIfNeeded(DiagnosticPosition pos, Tag tag, Type... args) { 213 if (Stream.of(args).noneMatch(Type::isErroneous)) { 214 Name opName = operatorName(tag); 215 JCDiagnostic.Error opError = (args.length) == 1 ? 216 Errors.OperatorCantBeApplied(opName, args[0]) : 217 Errors.OperatorCantBeApplied1(opName, args[0], args[1]); 218 log.error(pos, opError); 219 } 220 return noOpSymbol; 221 } 222 223 /** 224 * Return name of operator with given tree tag. 225 */ 226 public Name operatorName(JCTree.Tag tag) { 227 return opname[tag.operatorIndex()]; 228 } 229 230 /** 231 * The constants in this enum represent the types upon which all the operator helpers 232 * operate upon. This allows lazy and consise mapping between a type name and a type instance. 233 */ 234 enum OperatorType { 235 BYTE(syms -> syms.byteType), 236 SHORT(syms -> syms.shortType), 237 INT(syms -> syms.intType), 238 LONG(syms -> syms.longType), 239 FLOAT(syms -> syms.floatType), 240 DOUBLE(syms -> syms.doubleType), 241 CHAR(syms -> syms.charType), 242 BOOLEAN(syms -> syms.booleanType), 243 OBJECT(syms -> syms.objectType), 244 STRING(syms -> syms.stringType), 245 BOT(syms -> syms.botType); 246 247 final Function<Symtab, Type> asTypeFunc; 248 249 OperatorType(Function<Symtab, Type> asTypeFunc) { 250 this.asTypeFunc = asTypeFunc; 251 } 252 253 Type asType(Symtab syms) { 254 return asTypeFunc.apply(syms); 255 } 256 } 257 258 /** 259 * Common root for all operator helpers. An operator helper instance is associated with a 260 * given operator (i.e. '+'); it contains routines to perform operator lookup, i.e. find 261 * which version of the '+' operator is the best given an argument type list. Supported 262 * operator symbols are initialized lazily upon first lookup request - this is in order to avoid 263 * initialization circularities between this class and {@code Symtab}. 264 */ 265 abstract class OperatorHelper { 266 267 /** The operator name. */ 268 final Name name; 269 270 /** The list of symbols associated with this operator (lazily populated). */ 271 Optional<OperatorSymbol[]> alternatives = Optional.empty(); 272 273 /** An array of operator symbol suppliers (used to lazily populate the symbol list). */ 274 List<Supplier<OperatorSymbol>> operatorSuppliers = List.nil(); 275 276 @SuppressWarnings("varargs") 277 OperatorHelper(Tag tag) { 278 this.name = operatorName(tag); 279 } 280 281 /** 282 * This routine implements the main operator lookup process. Each operator is tested 283 * using an applicability predicate; if the test suceeds that same operator is returned, 284 * otherwise a dummy symbol is returned. 285 */ 286 final OperatorSymbol doLookup(Predicate<OperatorSymbol> applicabilityTest) { 287 return Stream.of(alternatives.orElseGet(this::initOperators)) 288 .filter(applicabilityTest) 289 .findFirst() 290 .orElse(noOpSymbol); 291 } 292 293 /** 294 * This routine performs lazy instantiation of the operator symbols supported by this helper. 295 * After initialization is done, the suppliers are cleared, to free up memory. 296 */ 297 private OperatorSymbol[] initOperators() { 298 OperatorSymbol[] operators = operatorSuppliers.stream() 299 .map(op -> op.get()) 300 .toArray(OperatorSymbol[]::new); 301 alternatives = Optional.of(operators); 302 operatorSuppliers = null; //let GC do its work 303 return operators; 304 } 305 } 306 307 /** 308 * Common superclass for all unary operator helpers. 309 */ 310 abstract class UnaryOperatorHelper extends OperatorHelper implements Predicate<Type> { 311 312 UnaryOperatorHelper(Tag tag) { 313 super(tag); 314 } 315 316 /** 317 * This routine implements the unary operator lookup process. It customizes the behavior 318 * of the shared lookup routine in {@link OperatorHelper}, by using an unary applicability test 319 * (see {@link UnaryOperatorHelper#isUnaryOperatorApplicable(OperatorOperatorSymbol, Type)} 320 */ 321 final OperatorSymbol doLookup(Type t) { 322 return doLookup(op -> isUnaryOperatorApplicable(op, t)); 323 } 324 325 /** 326 * Unary operator applicability test - is the input type the same as the expected operand type? 327 */ 328 boolean isUnaryOperatorApplicable(OperatorSymbol op, Type t) { 329 return types.isSameType(op.type.getParameterTypes().head, t); 330 } 331 332 /** 333 * Adds a unary operator symbol. 334 */ 335 final UnaryOperatorHelper addUnaryOperator(OperatorType arg, OperatorType res, int... opcode) { 336 operatorSuppliers = operatorSuppliers.prepend(() -> makeOperator(name, List.of(arg), res, opcode)); 337 return this; 338 } 339 340 /** 341 * This method will be overridden by unary operator helpers to provide custom resolution 342 * logic. 343 */ 344 abstract OperatorSymbol resolve(Type t); 345 } 346 347 abstract class BinaryOperatorHelper extends OperatorHelper implements BiPredicate<Type, Type> { 348 349 BinaryOperatorHelper(Tag tag) { 350 super(tag); 351 } 352 353 /** 354 * This routine implements the binary operator lookup process. It customizes the behavior 355 * of the shared lookup routine in {@link OperatorHelper}, by using an unary applicability test 356 * (see {@link BinaryOperatorHelper#isBinaryOperatorApplicable(OperatorSymbol, Type, Type)} 357 */ 358 final OperatorSymbol doLookup(Type t1, Type t2) { 359 return doLookup(op -> isBinaryOperatorApplicable(op, t1, t2)); 360 } 361 362 /** 363 * Binary operator applicability test - are the input types the same as the expected operand types? 364 */ 365 boolean isBinaryOperatorApplicable(OperatorSymbol op, Type t1, Type t2) { 366 List<Type> formals = op.type.getParameterTypes(); 367 return types.isSameType(formals.head, t1) && 368 types.isSameType(formals.tail.head, t2); 369 } 370 371 /** 372 * Adds a binary operator symbol. 373 */ 374 final BinaryOperatorHelper addBinaryOperator(OperatorType arg1, OperatorType arg2, OperatorType res, int... opcode) { 375 operatorSuppliers = operatorSuppliers.prepend(() -> makeOperator(name, List.of(arg1, arg2), res, opcode)); 376 return this; 377 } 378 379 /** 380 * This method will be overridden by binary operator helpers to provide custom resolution 381 * logic. 382 */ 383 abstract OperatorSymbol resolve(Type t1, Type t2); 384 } 385 386 /** 387 * Class representing unary operator helpers that operate on reference types. 388 */ 389 class UnaryReferenceOperator extends UnaryOperatorHelper { 390 391 UnaryReferenceOperator(Tag tag) { 392 super(tag); 393 } 394 395 @Override 396 public boolean test(Type type) { 397 return type.isNullOrReference(); 398 } 399 400 @Override 401 public OperatorSymbol resolve(Type arg) { 402 return doLookup(syms.objectType); 403 } 404 } 405 406 /** 407 * Class representing unary operator helpers that operate on numeric types (either boxed or unboxed). 408 * Operator lookup is performed after applying numeric promotion of the input type. 409 */ 410 class UnaryNumericOperator extends UnaryOperatorHelper { 411 412 Predicate<Type> numericTest; 413 414 UnaryNumericOperator(Tag tag) { 415 this(tag, Type::isNumeric); 416 } 417 418 UnaryNumericOperator(Tag tag, Predicate<Type> numericTest) { 419 super(tag); 420 this.numericTest = numericTest; 421 } 422 423 @Override 424 public boolean test(Type type) { 425 return numericTest.test(unaryPromotion(type)); 426 } 427 428 @Override 429 public OperatorSymbol resolve(Type arg) { 430 return doLookup(unaryPromotion(arg)); 431 } 432 } 433 434 /** 435 * Class representing unary operator helpers that operate on boolean types (either boxed or unboxed). 436 * Operator lookup is performed assuming the input type is a boolean type. 437 */ 438 class UnaryBooleanOperator extends UnaryOperatorHelper { 439 440 UnaryBooleanOperator(Tag tag) { 441 super(tag); 442 } 443 444 @Override 445 public boolean test(Type type) { 446 return types.unboxedTypeOrType(type).hasTag(TypeTag.BOOLEAN); 447 } 448 449 @Override 450 public OperatorSymbol resolve(Type arg) { 451 return doLookup(syms.booleanType); 452 } 453 } 454 455 /** 456 * Class representing prefix/postfix unary operator helpers. Operates on numeric types (either 457 * boxed or unboxed). Operator lookup is performed on the unboxed version of the input type. 458 */ 459 class UnaryPrefixPostfixOperator extends UnaryNumericOperator { 460 461 UnaryPrefixPostfixOperator(Tag tag) { 462 super(tag); 463 } 464 465 @Override 466 public OperatorSymbol resolve(Type arg) { 467 return doLookup(types.unboxedTypeOrType(arg)); 468 } 469 } 470 471 /** 472 * Class representing binary operator helpers that operate on numeric types (either boxed or unboxed). 473 * Operator lookup is performed after applying binary numeric promotion of the input types. 474 */ 475 class BinaryNumericOperator extends BinaryOperatorHelper { 476 477 Predicate<Type> numericTest; 478 479 BinaryNumericOperator(Tag tag) { 480 this(tag, Type::isNumeric); 481 } 482 483 BinaryNumericOperator(Tag tag, Predicate<Type> numericTest) { 484 super(tag); 485 this.numericTest = numericTest; 486 } 487 488 @Override 489 public OperatorSymbol resolve(Type arg1, Type arg2) { 490 Type t = binaryPromotion(arg1, arg2); 491 return doLookup(t, t); 492 } 493 494 @Override 495 public boolean test(Type arg1, Type arg2) { 496 return numericTest.test(unaryPromotion(arg1)) && 497 numericTest.test(unaryPromotion(arg2)); 498 } 499 } 500 501 /** 502 * Class representing bitwise operator helpers that operate on boolean types (either boxed or unboxed). 503 * Operator lookup is performed assuming both input types are boolean types. 504 */ 505 class BinaryBooleanOperator extends BinaryOperatorHelper { 506 507 BinaryBooleanOperator(Tag tag) { 508 super(tag); 509 } 510 511 @Override 512 public OperatorSymbol resolve(Type arg1, Type arg2) { 513 return doLookup(syms.booleanType, syms.booleanType); 514 } 515 516 @Override 517 public boolean test(Type arg1, Type arg2) { 518 return types.unboxedTypeOrType(arg1).hasTag(TypeTag.BOOLEAN) && 519 types.unboxedTypeOrType(arg2).hasTag(TypeTag.BOOLEAN); 520 } 521 } 522 523 /** 524 * Class representing string concatenation operator helper that operates on at least an 525 * string operand. Input types subject to an operator lookup undergoes a special string promotion 526 * (see {@link BinaryStringOperator#stringPromotion(Type)}. 527 */ 528 class BinaryStringOperator extends BinaryOperatorHelper { 529 530 BinaryStringOperator(Tag tag) { 531 super(tag); 532 } 533 534 @Override 535 public OperatorSymbol resolve(Type arg1, Type arg2) { 536 return doLookup(stringPromotion(arg1), stringPromotion(arg2)); 537 } 538 539 @Override 540 public boolean test(Type arg1, Type arg2) { 541 boolean hasStringOp = types.isSameType(arg1, syms.stringType) || 542 types.isSameType(arg2, syms.stringType); 543 boolean hasVoidOp = arg1.hasTag(TypeTag.VOID) || arg2.hasTag(TypeTag.VOID); 544 return hasStringOp && !hasVoidOp; 545 } 546 547 /** 548 * This routine applies following mappings: 549 * - if input type is primitive, apply numeric promotion 550 * - if input type is either 'void', 'null' or 'String' leave it untouched 551 * - otherwise return 'Object' 552 */ 553 private Type stringPromotion(Type t) { 554 if (t.isPrimitive()) { 555 return unaryPromotion(t); 556 } else if (t.hasTag(TypeTag.VOID) || t.hasTag(TypeTag.BOT) || 557 types.isSameType(t, syms.stringType)) { 558 return t; 559 } else if (t.hasTag(TypeTag.TYPEVAR)) { 560 return stringPromotion(t.getUpperBound()); 561 } else { 562 return syms.objectType; 563 } 564 } 565 } 566 567 /** 568 * Class representing shift operator helper that operates on integral operand types (either boxed 569 * or unboxed). Operator lookup is performed after applying unary numeric promotion to each input type. 570 */ 571 class BinaryShiftOperator extends BinaryOperatorHelper { 572 573 BinaryShiftOperator(Tag tag) { 574 super(tag); 575 } 576 577 @Override 578 public OperatorSymbol resolve(Type arg1, Type arg2) { 579 return doLookup(unaryPromotion(arg1), unaryPromotion(arg2)); 580 } 581 582 @Override 583 public boolean test(Type arg1, Type arg2) { 584 TypeTag op1 = unaryPromotion(arg1).getTag(); 585 TypeTag op2 = unaryPromotion(arg2).getTag(); 586 return (op1 == TypeTag.LONG || op1 == TypeTag.INT) && 587 (op2 == TypeTag.LONG || op2 == TypeTag.INT); 588 } 589 } 590 591 /** 592 * This enum represent the possible kinds of an comparison test ('==' and '!='). 593 */ 594 enum ComparisonKind { 595 /** equality between numeric or boolean operands. */ 596 NUMERIC_OR_BOOLEAN, 597 /** equality between reference operands. */ 598 REFERENCE, 599 /** erroneous equality */ 600 INVALID 601 } 602 603 /** 604 * Class representing equality operator helper that operates on either numeric, boolean or reference 605 * types. Operator lookup for numeric/boolean equality test is performed after binary numeric 606 * promotion to the input types. Operator lookup for reference equality test is performed assuming 607 * the input type is 'Object'. 608 */ 609 class BinaryEqualityOperator extends BinaryOperatorHelper { 610 611 BinaryEqualityOperator(Tag tag) { 612 super(tag); 613 } 614 615 @Override 616 public boolean test(Type arg1, Type arg2) { 617 return getKind(arg1, arg2) != ComparisonKind.INVALID; 618 } 619 620 @Override 621 public OperatorSymbol resolve(Type t1, Type t2) { 622 ComparisonKind kind = getKind(t1, t2); 623 Type t = (kind == ComparisonKind.NUMERIC_OR_BOOLEAN) ? 624 binaryPromotion(t1, t2) : 625 syms.objectType; 626 return doLookup(t, t); 627 } 628 629 /** 630 * Retrieve the comparison kind associated with the given argument type pair. 631 */ 632 private ComparisonKind getKind(Type arg1, Type arg2) { 633 boolean arg1Primitive = arg1.isPrimitive(); 634 boolean arg2Primitive = arg2.isPrimitive(); 635 if (arg1Primitive && arg2Primitive) { 636 return ComparisonKind.NUMERIC_OR_BOOLEAN; 637 } else if (arg1Primitive) { 638 return unaryPromotion(arg2).isPrimitive() ? 639 ComparisonKind.NUMERIC_OR_BOOLEAN : ComparisonKind.INVALID; 640 } else if (arg2Primitive) { 641 return unaryPromotion(arg1).isPrimitive() ? 642 ComparisonKind.NUMERIC_OR_BOOLEAN : ComparisonKind.INVALID; 643 } else { 644 return arg1.isNullOrReference() && arg2.isNullOrReference() ? 645 ComparisonKind.REFERENCE : ComparisonKind.INVALID; 646 } 647 } 648 } 649 650 /** 651 * Initialize all unary operators. 652 */ 653 private void initUnaryOperators() { 654 initOperators(unaryOperators, 655 new UnaryNumericOperator(Tag.POS) 656 .addUnaryOperator(DOUBLE, DOUBLE, nop) 657 .addUnaryOperator(FLOAT, FLOAT, nop) 658 .addUnaryOperator(LONG, LONG, nop) 659 .addUnaryOperator(INT, INT, nop), 660 new UnaryNumericOperator(Tag.NEG) 661 .addUnaryOperator(DOUBLE, DOUBLE, dneg) 662 .addUnaryOperator(FLOAT, FLOAT, fneg) 663 .addUnaryOperator(LONG, LONG, lneg) 664 .addUnaryOperator(INT, INT, ineg), 665 new UnaryNumericOperator(Tag.COMPL, Type::isIntegral) 666 .addUnaryOperator(LONG, LONG, lxor) 667 .addUnaryOperator(INT, INT, ixor), 668 new UnaryPrefixPostfixOperator(Tag.POSTINC) 669 .addUnaryOperator(DOUBLE, DOUBLE, dadd) 670 .addUnaryOperator(FLOAT, FLOAT, fadd) 671 .addUnaryOperator(LONG, LONG, ladd) 672 .addUnaryOperator(INT, INT, iadd) 673 .addUnaryOperator(CHAR, CHAR, iadd) 674 .addUnaryOperator(SHORT, SHORT, iadd) 675 .addUnaryOperator(BYTE, BYTE, iadd), 676 new UnaryPrefixPostfixOperator(Tag.POSTDEC) 677 .addUnaryOperator(DOUBLE, DOUBLE, dsub) 678 .addUnaryOperator(FLOAT, FLOAT, fsub) 679 .addUnaryOperator(LONG, LONG, lsub) 680 .addUnaryOperator(INT, INT, isub) 681 .addUnaryOperator(CHAR, CHAR, isub) 682 .addUnaryOperator(SHORT, SHORT, isub) 683 .addUnaryOperator(BYTE, BYTE, isub), 684 new UnaryBooleanOperator(Tag.NOT) 685 .addUnaryOperator(BOOLEAN, BOOLEAN, bool_not), 686 new UnaryReferenceOperator(Tag.NULLCHK) 687 .addUnaryOperator(OBJECT, OBJECT, nullchk)); 688 } 689 690 /** 691 * Initialize all binary operators. 692 */ 693 private void initBinaryOperators() { 694 initOperators(binaryOperators, 695 new BinaryStringOperator(Tag.PLUS) 696 .addBinaryOperator(STRING, OBJECT, STRING, string_add) 697 .addBinaryOperator(OBJECT, STRING, STRING, string_add) 698 .addBinaryOperator(STRING, STRING, STRING, string_add) 699 .addBinaryOperator(STRING, INT, STRING, string_add) 700 .addBinaryOperator(STRING, LONG, STRING, string_add) 701 .addBinaryOperator(STRING, FLOAT, STRING, string_add) 702 .addBinaryOperator(STRING, DOUBLE, STRING, string_add) 703 .addBinaryOperator(STRING, BOOLEAN, STRING, string_add) 704 .addBinaryOperator(STRING, BOT, STRING, string_add) 705 .addBinaryOperator(INT, STRING, STRING, string_add) 706 .addBinaryOperator(LONG, STRING, STRING, string_add) 707 .addBinaryOperator(FLOAT, STRING, STRING, string_add) 708 .addBinaryOperator(DOUBLE, STRING, STRING, string_add) 709 .addBinaryOperator(BOOLEAN, STRING, STRING, string_add) 710 .addBinaryOperator(BOT, STRING, STRING, string_add), 711 new BinaryNumericOperator(Tag.PLUS) 712 .addBinaryOperator(DOUBLE, DOUBLE, DOUBLE, dadd) 713 .addBinaryOperator(FLOAT, FLOAT, FLOAT, fadd) 714 .addBinaryOperator(LONG, LONG, LONG, ladd) 715 .addBinaryOperator(INT, INT, INT, iadd), 716 new BinaryNumericOperator(Tag.MINUS) 717 .addBinaryOperator(DOUBLE, DOUBLE, DOUBLE, dsub) 718 .addBinaryOperator(FLOAT, FLOAT, FLOAT, fsub) 719 .addBinaryOperator(LONG, LONG, LONG, lsub) 720 .addBinaryOperator(INT, INT, INT, isub), 721 new BinaryNumericOperator(Tag.MUL) 722 .addBinaryOperator(DOUBLE, DOUBLE, DOUBLE, dmul) 723 .addBinaryOperator(FLOAT, FLOAT, FLOAT, fmul) 724 .addBinaryOperator(LONG, LONG, LONG, lmul) 725 .addBinaryOperator(INT, INT, INT, imul), 726 new BinaryNumericOperator(Tag.DIV) 727 .addBinaryOperator(DOUBLE, DOUBLE, DOUBLE, ddiv) 728 .addBinaryOperator(FLOAT, FLOAT, FLOAT, fdiv) 729 .addBinaryOperator(LONG, LONG, LONG, ldiv) 730 .addBinaryOperator(INT, INT, INT, idiv), 731 new BinaryNumericOperator(Tag.MOD) 732 .addBinaryOperator(DOUBLE, DOUBLE, DOUBLE, dmod) 733 .addBinaryOperator(FLOAT, FLOAT, FLOAT, fmod) 734 .addBinaryOperator(LONG, LONG, LONG, lmod) 735 .addBinaryOperator(INT, INT, INT, imod), 736 new BinaryBooleanOperator(Tag.BITAND) 737 .addBinaryOperator(BOOLEAN, BOOLEAN, BOOLEAN, iand), 738 new BinaryNumericOperator(Tag.BITAND, Type::isIntegral) 739 .addBinaryOperator(LONG, LONG, LONG, land) 740 .addBinaryOperator(INT, INT, INT, iand), 741 new BinaryBooleanOperator(Tag.BITOR) 742 .addBinaryOperator(BOOLEAN, BOOLEAN, BOOLEAN, ior), 743 new BinaryNumericOperator(Tag.BITOR, Type::isIntegral) 744 .addBinaryOperator(LONG, LONG, LONG, lor) 745 .addBinaryOperator(INT, INT, INT, ior), 746 new BinaryBooleanOperator(Tag.BITXOR) 747 .addBinaryOperator(BOOLEAN, BOOLEAN, BOOLEAN, ixor), 748 new BinaryNumericOperator(Tag.BITXOR, Type::isIntegral) 749 .addBinaryOperator(LONG, LONG, LONG, lxor) 750 .addBinaryOperator(INT, INT, INT, ixor), 751 new BinaryShiftOperator(Tag.SL) 752 .addBinaryOperator(INT, INT, INT, ishl) 753 .addBinaryOperator(INT, LONG, INT, ishll) 754 .addBinaryOperator(LONG, INT, LONG, lshl) 755 .addBinaryOperator(LONG, LONG, LONG, lshll), 756 new BinaryShiftOperator(Tag.SR) 757 .addBinaryOperator(INT, INT, INT, ishr) 758 .addBinaryOperator(INT, LONG, INT, ishrl) 759 .addBinaryOperator(LONG, INT, LONG, lshr) 760 .addBinaryOperator(LONG, LONG, LONG, lshrl), 761 new BinaryShiftOperator(Tag.USR) 762 .addBinaryOperator(INT, INT, INT, iushr) 763 .addBinaryOperator(INT, LONG, INT, iushrl) 764 .addBinaryOperator(LONG, INT, LONG, lushr) 765 .addBinaryOperator(LONG, LONG, LONG, lushrl), 766 new BinaryNumericOperator(Tag.LT) 767 .addBinaryOperator(DOUBLE, DOUBLE, BOOLEAN, dcmpg, iflt) 768 .addBinaryOperator(FLOAT, FLOAT, BOOLEAN, fcmpg, iflt) 769 .addBinaryOperator(LONG, LONG, BOOLEAN, lcmp, iflt) 770 .addBinaryOperator(INT, INT, BOOLEAN, if_icmplt), 771 new BinaryNumericOperator(Tag.GT) 772 .addBinaryOperator(DOUBLE, DOUBLE, BOOLEAN, dcmpl, ifgt) 773 .addBinaryOperator(FLOAT, FLOAT, BOOLEAN, fcmpl, ifgt) 774 .addBinaryOperator(LONG, LONG, BOOLEAN, lcmp, ifgt) 775 .addBinaryOperator(INT, INT, BOOLEAN, if_icmpgt), 776 new BinaryNumericOperator(Tag.LE) 777 .addBinaryOperator(DOUBLE, DOUBLE, BOOLEAN, dcmpg, ifle) 778 .addBinaryOperator(FLOAT, FLOAT, BOOLEAN, fcmpg, ifle) 779 .addBinaryOperator(LONG, LONG, BOOLEAN, lcmp, ifle) 780 .addBinaryOperator(INT, INT, BOOLEAN, if_icmple), 781 new BinaryNumericOperator(Tag.GE) 782 .addBinaryOperator(DOUBLE, DOUBLE, BOOLEAN, dcmpl, ifge) 783 .addBinaryOperator(FLOAT, FLOAT, BOOLEAN, fcmpl, ifge) 784 .addBinaryOperator(LONG, LONG, BOOLEAN, lcmp, ifge) 785 .addBinaryOperator(INT, INT, BOOLEAN, if_icmpge), 786 new BinaryEqualityOperator(Tag.EQ) 787 .addBinaryOperator(OBJECT, OBJECT, BOOLEAN, if_acmpeq) 788 .addBinaryOperator(BOOLEAN, BOOLEAN, BOOLEAN, if_icmpeq) 789 .addBinaryOperator(DOUBLE, DOUBLE, BOOLEAN, dcmpl, ifeq) 790 .addBinaryOperator(FLOAT, FLOAT, BOOLEAN, fcmpl, ifeq) 791 .addBinaryOperator(LONG, LONG, BOOLEAN, lcmp, ifeq) 792 .addBinaryOperator(INT, INT, BOOLEAN, if_icmpeq), 793 new BinaryEqualityOperator(Tag.NE) 794 .addBinaryOperator(OBJECT, OBJECT, BOOLEAN, if_acmpne) 795 .addBinaryOperator(BOOLEAN, BOOLEAN, BOOLEAN, if_icmpne) 796 .addBinaryOperator(DOUBLE, DOUBLE, BOOLEAN, dcmpl, ifne) 797 .addBinaryOperator(FLOAT, FLOAT, BOOLEAN, fcmpl, ifne) 798 .addBinaryOperator(LONG, LONG, BOOLEAN, lcmp, ifne) 799 .addBinaryOperator(INT, INT, BOOLEAN, if_icmpne), 800 new BinaryBooleanOperator(Tag.AND) 801 .addBinaryOperator(BOOLEAN, BOOLEAN, BOOLEAN, bool_and), 802 new BinaryBooleanOperator(Tag.OR) 803 .addBinaryOperator(BOOLEAN, BOOLEAN, BOOLEAN, bool_or)); 804 } 805 806 OperatorSymbol lookupBinaryOp(Predicate<OperatorSymbol> applicabilityTest) { 807 return binaryOperators.values().stream() 808 .flatMap(List::stream) 809 .map(helper -> helper.doLookup(applicabilityTest)) 810 .distinct() 811 .filter(sym -> sym != noOpSymbol) 812 .findFirst().get(); 813 } 814 815 /** 816 * Complete the initialization of an operator helper by storing it into the corresponding operator map. 817 */ 818 @SafeVarargs 819 private final <O extends OperatorHelper> void initOperators(Map<Name, List<O>> opsMap, O... ops) { 820 for (O o : ops) { 821 Name opName = o.name; 822 List<O> helpers = opsMap.getOrDefault(opName, List.nil()); 823 opsMap.put(opName, helpers.prepend(o)); 824 } 825 } 826 827 /** 828 * Initialize operator name array. 829 */ 830 private void initOperatorNames() { 831 setOperatorName(Tag.POS, "+"); 832 setOperatorName(Tag.NEG, "-"); 833 setOperatorName(Tag.NOT, "!"); 834 setOperatorName(Tag.COMPL, "~"); 835 setOperatorName(Tag.PREINC, "++"); 836 setOperatorName(Tag.PREDEC, "--"); 837 setOperatorName(Tag.POSTINC, "++"); 838 setOperatorName(Tag.POSTDEC, "--"); 839 setOperatorName(Tag.NULLCHK, "<*nullchk*>"); 840 setOperatorName(Tag.OR, "||"); 841 setOperatorName(Tag.AND, "&&"); 842 setOperatorName(Tag.EQ, "=="); 843 setOperatorName(Tag.NE, "!="); 844 setOperatorName(Tag.LT, "<"); 845 setOperatorName(Tag.GT, ">"); 846 setOperatorName(Tag.LE, "<="); 847 setOperatorName(Tag.GE, ">="); 848 setOperatorName(Tag.BITOR, "|"); 849 setOperatorName(Tag.BITXOR, "^"); 850 setOperatorName(Tag.BITAND, "&"); 851 setOperatorName(Tag.SL, "<<"); 852 setOperatorName(Tag.SR, ">>"); 853 setOperatorName(Tag.USR, ">>>"); 854 setOperatorName(Tag.PLUS, "+"); 855 setOperatorName(Tag.MINUS, names.hyphen); 856 setOperatorName(Tag.MUL, names.asterisk); 857 setOperatorName(Tag.DIV, names.slash); 858 setOperatorName(Tag.MOD, "%"); 859 } 860 //where 861 private void setOperatorName(Tag tag, String name) { 862 setOperatorName(tag, names.fromString(name)); 863 } 864 865 private void setOperatorName(Tag tag, Name name) { 866 opname[tag.operatorIndex()] = name; 867 } 868} 869