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}