001/* 002 * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package jdk.internal.jvmci.code; 024 025import static java.util.Collections.*; 026import static jdk.internal.jvmci.meta.MetaUtil.*; 027 028import java.util.*; 029 030import jdk.internal.jvmci.meta.*; 031import jdk.internal.jvmci.meta.Assumptions.*; 032 033/** 034 * Represents the output from compiling a method, including the compiled machine code, associated 035 * data and references, relocation information, deoptimization information, etc. 036 */ 037public class CompilationResult { 038 039 /** 040 * Represents a code position with associated additional information. 041 */ 042 public abstract static class Site { 043 044 /** 045 * The position (or offset) of this site with respect to the start of the target method. 046 */ 047 public final int pcOffset; 048 049 public Site(int pos) { 050 this.pcOffset = pos; 051 } 052 053 @Override 054 public final int hashCode() { 055 throw new UnsupportedOperationException("hashCode"); 056 } 057 058 @Override 059 public String toString() { 060 return identityHashCodeString(this); 061 } 062 063 @Override 064 public abstract boolean equals(Object obj); 065 } 066 067 /** 068 * Represents an infopoint with associated debug info. Note that safepoints are also infopoints. 069 */ 070 public static class Infopoint extends Site implements Comparable<Infopoint> { 071 072 public final DebugInfo debugInfo; 073 074 public final InfopointReason reason; 075 076 public Infopoint(int pcOffset, DebugInfo debugInfo, InfopointReason reason) { 077 super(pcOffset); 078 this.debugInfo = debugInfo; 079 this.reason = reason; 080 } 081 082 @Override 083 public String toString() { 084 StringBuilder sb = new StringBuilder(); 085 sb.append(pcOffset); 086 sb.append("[<infopoint>]"); 087 appendDebugInfo(sb, debugInfo); 088 return sb.toString(); 089 } 090 091 @Override 092 public int compareTo(Infopoint o) { 093 if (pcOffset < o.pcOffset) { 094 return -1; 095 } else if (pcOffset > o.pcOffset) { 096 return 1; 097 } 098 return this.reason.compareTo(o.reason); 099 } 100 101 @Override 102 public boolean equals(Object obj) { 103 if (this == obj) { 104 return true; 105 } 106 if (obj != null && obj.getClass() == getClass()) { 107 Infopoint that = (Infopoint) obj; 108 if (this.pcOffset == that.pcOffset && Objects.equals(this.debugInfo, that.debugInfo) && Objects.equals(this.reason, that.reason)) { 109 return true; 110 } 111 } 112 return false; 113 } 114 } 115 116 /** 117 * Represents a call in the code. 118 */ 119 public static final class Call extends Infopoint { 120 121 /** 122 * The target of the call. 123 */ 124 public final InvokeTarget target; 125 126 /** 127 * The size of the call instruction. 128 */ 129 public final int size; 130 131 /** 132 * Specifies if this call is direct or indirect. A direct call has an immediate operand 133 * encoding the absolute or relative (to the call itself) address of the target. An indirect 134 * call has a register or memory operand specifying the target address of the call. 135 */ 136 public final boolean direct; 137 138 public Call(InvokeTarget target, int pcOffset, int size, boolean direct, DebugInfo debugInfo) { 139 super(pcOffset, debugInfo, InfopointReason.CALL); 140 this.size = size; 141 this.target = target; 142 this.direct = direct; 143 } 144 145 @Override 146 public boolean equals(Object obj) { 147 if (this == obj) { 148 return true; 149 } 150 if (obj instanceof Call && super.equals(obj)) { 151 Call that = (Call) obj; 152 if (this.size == that.size && this.direct == that.direct && Objects.equals(this.target, that.target)) { 153 return true; 154 } 155 } 156 return false; 157 } 158 159 @Override 160 public String toString() { 161 StringBuilder sb = new StringBuilder(); 162 sb.append(pcOffset); 163 sb.append('['); 164 sb.append(target); 165 sb.append(']'); 166 167 if (debugInfo != null) { 168 appendDebugInfo(sb, debugInfo); 169 } 170 171 return sb.toString(); 172 } 173 } 174 175 /** 176 * Represents some external data that is referenced by the code. 177 */ 178 public abstract static class Reference { 179 180 @Override 181 public abstract int hashCode(); 182 183 @Override 184 public abstract boolean equals(Object obj); 185 } 186 187 public static final class ConstantReference extends Reference { 188 189 private final VMConstant constant; 190 191 public ConstantReference(VMConstant constant) { 192 this.constant = constant; 193 } 194 195 public VMConstant getConstant() { 196 return constant; 197 } 198 199 @Override 200 public String toString() { 201 return constant.toString(); 202 } 203 204 @Override 205 public int hashCode() { 206 return constant.hashCode(); 207 } 208 209 @Override 210 public boolean equals(Object obj) { 211 if (this == obj) { 212 return true; 213 } 214 if (obj instanceof ConstantReference) { 215 ConstantReference that = (ConstantReference) obj; 216 return Objects.equals(this.constant, that.constant); 217 } 218 return false; 219 } 220 } 221 222 public static final class DataSectionReference extends Reference { 223 224 private boolean initialized; 225 private int offset; 226 227 public DataSectionReference() { 228 // will be set after the data section layout is fixed 229 offset = 0xDEADDEAD; 230 } 231 232 public int getOffset() { 233 assert initialized; 234 235 return offset; 236 } 237 238 public void setOffset(int offset) { 239 assert !initialized; 240 initialized = true; 241 242 this.offset = offset; 243 } 244 245 @Override 246 public int hashCode() { 247 return offset; 248 } 249 250 @Override 251 public boolean equals(Object obj) { 252 if (this == obj) { 253 return true; 254 } 255 if (obj instanceof DataSectionReference) { 256 DataSectionReference that = (DataSectionReference) obj; 257 return this.offset == that.offset; 258 } 259 return false; 260 } 261 } 262 263 /** 264 * Represents a code site that references some data. The associated data can be either a 265 * {@link DataSectionReference reference} to the data section, or it may be an inlined 266 * {@link JavaConstant} that needs to be patched. 267 */ 268 public static final class DataPatch extends Site { 269 270 public Reference reference; 271 272 public DataPatch(int pcOffset, Reference reference) { 273 super(pcOffset); 274 this.reference = reference; 275 } 276 277 @Override 278 public String toString() { 279 return String.format("%d[<data patch referring to %s>]", pcOffset, reference.toString()); 280 } 281 282 @Override 283 public boolean equals(Object obj) { 284 if (this == obj) { 285 return true; 286 } 287 if (obj instanceof DataPatch) { 288 DataPatch that = (DataPatch) obj; 289 if (this.pcOffset == that.pcOffset && Objects.equals(this.reference, that.reference)) { 290 return true; 291 } 292 } 293 return false; 294 } 295 } 296 297 /** 298 * Provides extra information about instructions or data at specific positions in 299 * {@link CompilationResult#getTargetCode()}. This is optional information that can be used to 300 * enhance a disassembly of the code. 301 */ 302 public abstract static class CodeAnnotation { 303 304 public final int position; 305 306 public CodeAnnotation(int position) { 307 this.position = position; 308 } 309 310 @Override 311 public final int hashCode() { 312 throw new UnsupportedOperationException("hashCode"); 313 } 314 315 @Override 316 public String toString() { 317 return identityHashCodeString(this); 318 } 319 320 @Override 321 public abstract boolean equals(Object obj); 322 } 323 324 /** 325 * A string comment about one or more instructions at a specific position in the code. 326 */ 327 public static final class CodeComment extends CodeAnnotation { 328 329 public final String value; 330 331 public CodeComment(int position, String comment) { 332 super(position); 333 this.value = comment; 334 } 335 336 @Override 337 public boolean equals(Object obj) { 338 if (this == obj) { 339 return true; 340 } 341 if (obj instanceof CodeComment) { 342 CodeComment that = (CodeComment) obj; 343 if (this.position == that.position && this.value.equals(that.value)) { 344 return true; 345 } 346 } 347 return false; 348 } 349 350 @Override 351 public String toString() { 352 return getClass().getSimpleName() + "@" + position + ": " + value; 353 } 354 } 355 356 /** 357 * Describes a table of signed offsets embedded in the code. The offsets are relative to the 358 * starting address of the table. This type of table maybe generated when translating a 359 * multi-way branch based on a key value from a dense value set (e.g. the {@code tableswitch} 360 * JVM instruction). 361 * 362 * The table is indexed by the contiguous range of integers from {@link #low} to {@link #high} 363 * inclusive. 364 */ 365 public static final class JumpTable extends CodeAnnotation { 366 367 /** 368 * The low value in the key range (inclusive). 369 */ 370 public final int low; 371 372 /** 373 * The high value in the key range (inclusive). 374 */ 375 public final int high; 376 377 /** 378 * The size (in bytes) of each table entry. 379 */ 380 public final int entrySize; 381 382 public JumpTable(int position, int low, int high, int entrySize) { 383 super(position); 384 this.low = low; 385 this.high = high; 386 this.entrySize = entrySize; 387 } 388 389 @Override 390 public boolean equals(Object obj) { 391 if (this == obj) { 392 return true; 393 } 394 if (obj instanceof JumpTable) { 395 JumpTable that = (JumpTable) obj; 396 if (this.position == that.position && this.entrySize == that.entrySize && this.low == that.low && this.high == that.high) { 397 return true; 398 } 399 } 400 return false; 401 } 402 403 @Override 404 public String toString() { 405 return getClass().getSimpleName() + "@" + position + ": [" + low + " .. " + high + "]"; 406 } 407 } 408 409 /** 410 * Represents exception handler information for a specific code position. It includes the catch 411 * code position as well as the caught exception type. 412 */ 413 public static final class ExceptionHandler extends Site { 414 415 public final int handlerPos; 416 417 ExceptionHandler(int pcOffset, int handlerPos) { 418 super(pcOffset); 419 this.handlerPos = handlerPos; 420 } 421 422 @Override 423 public String toString() { 424 return String.format("%d[<exception edge to %d>]", pcOffset, handlerPos); 425 } 426 427 @Override 428 public boolean equals(Object obj) { 429 if (this == obj) { 430 return true; 431 } 432 if (obj instanceof ExceptionHandler) { 433 ExceptionHandler that = (ExceptionHandler) obj; 434 if (this.pcOffset == that.pcOffset && this.handlerPos == that.handlerPos) { 435 return true; 436 } 437 } 438 return false; 439 } 440 } 441 442 /** 443 * Represents a mark in the machine code that can be used by the runtime for its own purposes. A 444 * mark can reference other marks. 445 */ 446 public static final class Mark extends Site { 447 448 public final Object id; 449 450 public Mark(int pcOffset, Object id) { 451 super(pcOffset); 452 this.id = id; 453 } 454 455 @Override 456 public String toString() { 457 if (id == null) { 458 return String.format("%d[<mar>]", pcOffset); 459 } else if (id instanceof Integer) { 460 return String.format("%d[<mark with id %s>]", pcOffset, Integer.toHexString((Integer) id)); 461 } else { 462 return String.format("%d[<mark with id %s>]", pcOffset, id.toString()); 463 } 464 } 465 466 @Override 467 public boolean equals(Object obj) { 468 if (this == obj) { 469 return true; 470 } 471 if (obj instanceof Mark) { 472 Mark that = (Mark) obj; 473 if (this.pcOffset == that.pcOffset && Objects.equals(this.id, that.id)) { 474 return true; 475 } 476 } 477 return false; 478 } 479 } 480 481 private int id = -1; 482 private int entryBCI = -1; 483 484 private final DataSection dataSection = new DataSection(); 485 486 private final List<Infopoint> infopoints = new ArrayList<>(); 487 private final List<DataPatch> dataPatches = new ArrayList<>(); 488 private final List<ExceptionHandler> exceptionHandlers = new ArrayList<>(); 489 private final List<Mark> marks = new ArrayList<>(); 490 491 private int totalFrameSize = -1; 492 private int customStackAreaOffset = -1; 493 494 private final String name; 495 496 /** 497 * The buffer containing the emitted machine code. 498 */ 499 private byte[] targetCode; 500 501 /** 502 * The leading number of bytes in {@link #targetCode} containing the emitted machine code. 503 */ 504 private int targetCodeSize; 505 506 private ArrayList<CodeAnnotation> annotations; 507 508 private Assumption[] assumptions; 509 510 /** 511 * The list of the methods whose bytecodes were used as input to the compilation. If 512 * {@code null}, then the compilation did not record method dependencies. Otherwise, the first 513 * element of this array is the root method of the compilation. 514 */ 515 private ResolvedJavaMethod[] methods; 516 517 private int bytecodeSize; 518 519 private boolean hasUnsafeAccess; 520 521 public CompilationResult() { 522 this(null); 523 } 524 525 public CompilationResult(String name) { 526 this.name = name; 527 } 528 529 @Override 530 public int hashCode() { 531 // CompilationResult instances should not be used as hash map keys 532 throw new UnsupportedOperationException("hashCode"); 533 } 534 535 @Override 536 public String toString() { 537 if (methods != null) { 538 return getClass().getName() + "[" + methods[0].format("%H.%n(%p)%r") + "]"; 539 } 540 return identityHashCodeString(this); 541 } 542 543 @Override 544 public boolean equals(Object obj) { 545 if (this == obj) { 546 return true; 547 } 548 if (obj != null && obj.getClass() == getClass()) { 549 CompilationResult that = (CompilationResult) obj; 550 // @formatter:off 551 if (this.entryBCI == that.entryBCI && 552 this.id == that.id && 553 this.customStackAreaOffset == that.customStackAreaOffset && 554 this.totalFrameSize == that.totalFrameSize && 555 this.targetCodeSize == that.targetCodeSize && 556 Objects.equals(this.name, that.name) && 557 Objects.equals(this.annotations, that.annotations) && 558 Objects.equals(this.dataSection, that.dataSection) && 559 Objects.equals(this.exceptionHandlers, that.exceptionHandlers) && 560 Objects.equals(this.dataPatches, that.dataPatches) && 561 Objects.equals(this.infopoints, that.infopoints) && 562 Objects.equals(this.marks, that.marks) && 563 Arrays.equals(this.assumptions, that.assumptions) && 564 Arrays.equals(targetCode, that.targetCode)) { 565 return true; 566 } 567 // @formatter:on 568 } 569 return false; 570 } 571 572 /** 573 * @return the compile id 574 */ 575 public int getId() { 576 return id; 577 } 578 579 /** 580 * @param id the compile id to set 581 */ 582 public void setId(int id) { 583 this.id = id; 584 } 585 586 /** 587 * @return the entryBCI 588 */ 589 public int getEntryBCI() { 590 return entryBCI; 591 } 592 593 /** 594 * @param entryBCI the entryBCI to set 595 */ 596 public void setEntryBCI(int entryBCI) { 597 this.entryBCI = entryBCI; 598 } 599 600 /** 601 * Sets the assumptions made during compilation. 602 */ 603 public void setAssumptions(Assumption[] assumptions) { 604 this.assumptions = assumptions; 605 } 606 607 /** 608 * Gets the assumptions made during compilation. 609 */ 610 public Assumption[] getAssumptions() { 611 return assumptions; 612 } 613 614 /** 615 * Sets the methods whose bytecodes were used as input to the compilation. 616 * 617 * @param rootMethod the root method of the compilation 618 * @param inlinedMethods the methods inlined during compilation 619 */ 620 public void setMethods(ResolvedJavaMethod rootMethod, Collection<ResolvedJavaMethod> inlinedMethods) { 621 assert rootMethod != null; 622 assert inlinedMethods != null; 623 if (inlinedMethods.contains(rootMethod)) { 624 methods = inlinedMethods.toArray(new ResolvedJavaMethod[inlinedMethods.size()]); 625 for (int i = 0; i < methods.length; i++) { 626 if (methods[i].equals(rootMethod)) { 627 if (i != 0) { 628 ResolvedJavaMethod tmp = methods[0]; 629 methods[0] = methods[i]; 630 methods[i] = tmp; 631 } 632 break; 633 } 634 } 635 } else { 636 methods = new ResolvedJavaMethod[1 + inlinedMethods.size()]; 637 methods[0] = rootMethod; 638 int i = 1; 639 for (ResolvedJavaMethod m : inlinedMethods) { 640 methods[i++] = m; 641 } 642 } 643 } 644 645 /** 646 * Gets the methods whose bytecodes were used as input to the compilation. 647 * 648 * @return {@code null} if the compilation did not record method dependencies otherwise the 649 * methods whose bytecodes were used as input to the compilation with the first element 650 * being the root method of the compilation 651 */ 652 public ResolvedJavaMethod[] getMethods() { 653 return methods; 654 } 655 656 public void setBytecodeSize(int bytecodeSize) { 657 this.bytecodeSize = bytecodeSize; 658 } 659 660 public int getBytecodeSize() { 661 return bytecodeSize; 662 } 663 664 public DataSection getDataSection() { 665 return dataSection; 666 } 667 668 /** 669 * The total frame size of the method in bytes. This includes the return address pushed onto the 670 * stack, if any. 671 * 672 * @return the frame size 673 */ 674 public int getTotalFrameSize() { 675 assert totalFrameSize != -1 : "frame size not yet initialized!"; 676 return totalFrameSize; 677 } 678 679 /** 680 * Sets the total frame size in bytes. This includes the return address pushed onto the stack, 681 * if any. 682 * 683 * @param size the size of the frame in bytes 684 */ 685 public void setTotalFrameSize(int size) { 686 totalFrameSize = size; 687 } 688 689 /** 690 * Sets the machine that has been generated by the compiler. 691 * 692 * @param code the machine code generated 693 * @param size the size of the machine code 694 */ 695 public void setTargetCode(byte[] code, int size) { 696 targetCode = code; 697 targetCodeSize = size; 698 } 699 700 /** 701 * Records a data patch in the code section. The data patch can refer to something in the 702 * {@link DataSectionReference data section} or directly to an {@link ConstantReference inlined 703 * constant}. 704 * 705 * @param codePos The position in the code that needs to be patched. 706 * @param ref The reference that should be inserted in the code. 707 */ 708 public void recordDataPatch(int codePos, Reference ref) { 709 assert codePos >= 0 && ref != null; 710 dataPatches.add(new DataPatch(codePos, ref)); 711 } 712 713 /** 714 * Records a call in the code array. 715 * 716 * @param codePos the position of the call in the code array 717 * @param size the size of the call instruction 718 * @param target the being called 719 * @param debugInfo the debug info for the call 720 * @param direct specifies if this is a {@linkplain Call#direct direct} call 721 */ 722 public void recordCall(int codePos, int size, InvokeTarget target, DebugInfo debugInfo, boolean direct) { 723 final Call call = new Call(target, codePos, size, direct, debugInfo); 724 addInfopoint(call); 725 } 726 727 /** 728 * Records an exception handler for this method. 729 * 730 * @param codePos the position in the code that is covered by the handler 731 * @param handlerPos the position of the handler 732 */ 733 public void recordExceptionHandler(int codePos, int handlerPos) { 734 assert validateExceptionHandlerAdd(codePos, handlerPos) : String.format("Duplicate exception handler for pc 0x%x handlerPos 0x%x", codePos, handlerPos); 735 exceptionHandlers.add(new ExceptionHandler(codePos, handlerPos)); 736 } 737 738 /** 739 * Validate if the exception handler for codePos already exists and handlerPos is different. 740 * 741 * @param codePos 742 * @param handlerPos 743 * @return true if the validation is successful 744 */ 745 private boolean validateExceptionHandlerAdd(int codePos, int handlerPos) { 746 ExceptionHandler exHandler = getExceptionHandlerForCodePos(codePos); 747 return exHandler == null || exHandler.handlerPos == handlerPos; 748 } 749 750 /** 751 * Returns the first ExceptionHandler which matches codePos. 752 * 753 * @param codePos position to search for 754 * @return first matching ExceptionHandler 755 */ 756 private ExceptionHandler getExceptionHandlerForCodePos(int codePos) { 757 for (ExceptionHandler h : exceptionHandlers) { 758 if (h.pcOffset == codePos) { 759 return h; 760 } 761 } 762 return null; 763 } 764 765 /** 766 * Records an infopoint in the code array. 767 * 768 * @param codePos the position of the infopoint in the code array 769 * @param debugInfo the debug info for the infopoint 770 */ 771 public void recordInfopoint(int codePos, DebugInfo debugInfo, InfopointReason reason) { 772 addInfopoint(new Infopoint(codePos, debugInfo, reason)); 773 } 774 775 /** 776 * Records a custom infopoint in the code section. 777 * 778 * Compiler implementations can use this method to record non-standard infopoints, which are not 779 * handled by the dedicated methods like {@link #recordCall}. 780 * 781 * @param infopoint the infopoint to record, usually a derived class from {@link Infopoint} 782 */ 783 public void addInfopoint(Infopoint infopoint) { 784 // The infopoints list must always be sorted 785 if (!infopoints.isEmpty()) { 786 Infopoint previousInfopoint = infopoints.get(infopoints.size() - 1); 787 if (previousInfopoint.pcOffset > infopoint.pcOffset) { 788 // This re-sorting should be very rare 789 Collections.sort(infopoints); 790 previousInfopoint = infopoints.get(infopoints.size() - 1); 791 } 792 if (previousInfopoint.pcOffset == infopoint.pcOffset) { 793 if (infopoint.reason.canBeOmitted()) { 794 return; 795 } 796 if (previousInfopoint.reason.canBeOmitted()) { 797 Infopoint removed = infopoints.remove(infopoints.size() - 1); 798 assert removed == previousInfopoint; 799 } else { 800 throw new RuntimeException("Infopoints that can not be omited should have distinct PCs"); 801 } 802 } 803 } 804 infopoints.add(infopoint); 805 } 806 807 /** 808 * Records an instruction mark within this method. 809 * 810 * @param codePos the position in the code that is covered by the handler 811 * @param markId the identifier for this mark 812 */ 813 public Mark recordMark(int codePos, Object markId) { 814 Mark mark = new Mark(codePos, markId); 815 marks.add(mark); 816 return mark; 817 } 818 819 /** 820 * Offset in bytes for the custom stack area (relative to sp). 821 * 822 * @return the offset in bytes 823 */ 824 public int getCustomStackAreaOffset() { 825 return customStackAreaOffset; 826 } 827 828 /** 829 * @see #getCustomStackAreaOffset() 830 * @param offset 831 */ 832 public void setCustomStackAreaOffset(int offset) { 833 customStackAreaOffset = offset; 834 } 835 836 /** 837 * @return the machine code generated for this method 838 */ 839 public byte[] getTargetCode() { 840 return targetCode; 841 } 842 843 /** 844 * @return the size of the machine code generated for this method 845 */ 846 public int getTargetCodeSize() { 847 return targetCodeSize; 848 } 849 850 /** 851 * @return the code annotations or {@code null} if there are none 852 */ 853 public List<CodeAnnotation> getAnnotations() { 854 if (annotations == null) { 855 return Collections.emptyList(); 856 } 857 return annotations; 858 } 859 860 public void addAnnotation(CodeAnnotation annotation) { 861 assert annotation != null; 862 if (annotations == null) { 863 annotations = new ArrayList<>(); 864 } 865 annotations.add(annotation); 866 } 867 868 private static void appendDebugInfo(StringBuilder sb, DebugInfo info) { 869 if (info != null) { 870 ReferenceMap refMap = info.getReferenceMap(); 871 if (refMap != null) { 872 sb.append(refMap.toString()); 873 sb.append(']'); 874 } 875 RegisterSaveLayout calleeSaveInfo = info.getCalleeSaveInfo(); 876 if (calleeSaveInfo != null) { 877 sb.append(" callee-save-info["); 878 String sep = ""; 879 for (Map.Entry<Register, Integer> e : calleeSaveInfo.registersToSlots(true).entrySet()) { 880 sb.append(sep).append(e.getKey()).append("->").append(e.getValue()); 881 sep = ", "; 882 } 883 sb.append(']'); 884 } 885 BytecodePosition codePos = info.getBytecodePosition(); 886 if (codePos != null) { 887 MetaUtil.appendLocation(sb.append(" "), codePos.getMethod(), codePos.getBCI()); 888 if (info.hasFrame()) { 889 sb.append(" #locals=").append(info.frame().numLocals).append(" #expr=").append(info.frame().numStack); 890 if (info.frame().numLocks > 0) { 891 sb.append(" #locks=").append(info.frame().numLocks); 892 } 893 } 894 } 895 } 896 } 897 898 /** 899 * @return the list of infopoints, sorted by {@link Site#pcOffset} 900 */ 901 public List<Infopoint> getInfopoints() { 902 if (infopoints.isEmpty()) { 903 return emptyList(); 904 } 905 return unmodifiableList(infopoints); 906 } 907 908 /** 909 * @return the list of data references 910 */ 911 public List<DataPatch> getDataPatches() { 912 if (dataPatches.isEmpty()) { 913 return emptyList(); 914 } 915 return unmodifiableList(dataPatches); 916 } 917 918 /** 919 * @return the list of exception handlers 920 */ 921 public List<ExceptionHandler> getExceptionHandlers() { 922 if (exceptionHandlers.isEmpty()) { 923 return emptyList(); 924 } 925 return unmodifiableList(exceptionHandlers); 926 } 927 928 /** 929 * @return the list of marks 930 */ 931 public List<Mark> getMarks() { 932 if (marks.isEmpty()) { 933 return emptyList(); 934 } 935 return unmodifiableList(marks); 936 } 937 938 public String getName() { 939 return name; 940 } 941 942 public void setHasUnsafeAccess(boolean hasUnsafeAccess) { 943 this.hasUnsafeAccess = hasUnsafeAccess; 944 } 945 946 public boolean hasUnsafeAccess() { 947 return hasUnsafeAccess; 948 } 949 950 public void reset() { 951 hasUnsafeAccess = false; 952 infopoints.clear(); 953 dataPatches.clear(); 954 exceptionHandlers.clear(); 955 marks.clear(); 956 dataSection.clear(); 957 if (annotations != null) { 958 annotations.clear(); 959 } 960 } 961}