22 */ |
22 */ |
23 |
23 |
24 |
24 |
25 package org.graalvm.compiler.java; |
25 package org.graalvm.compiler.java; |
26 |
26 |
27 public class JsrScope { |
27 import org.graalvm.compiler.bytecode.Bytecodes; |
28 |
28 |
|
29 /** |
|
30 * Represents a subroutine entered via {@link Bytecodes#JSR} and exited via {@link Bytecodes#RET}. |
|
31 */ |
|
32 public final class JsrScope { |
|
33 |
|
34 /** |
|
35 * The scope outside of any JSR/RET subroutine. |
|
36 */ |
29 public static final JsrScope EMPTY_SCOPE = new JsrScope(); |
37 public static final JsrScope EMPTY_SCOPE = new JsrScope(); |
30 |
38 |
31 private final long scope; |
39 private final char returnAddress; |
32 |
40 |
33 private JsrScope(long scope) { |
41 private final JsrScope parent; |
34 this.scope = scope; |
42 |
|
43 private JsrScope(int returnBci, JsrScope parent) { |
|
44 this.returnAddress = (char) returnBci; |
|
45 this.parent = parent; |
35 } |
46 } |
36 |
47 |
37 public JsrScope() { |
48 private JsrScope() { |
38 this.scope = 0; |
49 this.returnAddress = 0; |
|
50 this.parent = null; |
39 } |
51 } |
40 |
52 |
41 public int nextReturnAddress() { |
53 public int nextReturnAddress() { |
42 return (int) (scope & 0xffff); |
54 return returnAddress; |
43 } |
55 } |
44 |
56 |
45 public JsrScope push(int jsrReturnBci) { |
57 /** |
46 if ((scope & 0xffff000000000000L) != 0) { |
58 * Enters a new subroutine from the current scope represented by this object. |
47 throw new JsrNotSupportedBailout("only four jsr nesting levels are supported"); |
59 * |
|
60 * @param returnBci the bytecode address returned to when leaving the new scope |
|
61 * @return an object representing the newly entered scope |
|
62 */ |
|
63 public JsrScope push(int returnBci) { |
|
64 if (returnBci == 0) { |
|
65 throw new IllegalArgumentException("A bytecode subroutine cannot have a return address of 0"); |
48 } |
66 } |
49 return new JsrScope((scope << 16) | jsrReturnBci); |
67 if (returnBci < 1 || returnBci > 0xFFFF) { |
|
68 throw new IllegalArgumentException("Bytecode subroutine return address cannot be encoded as a char: " + returnBci); |
|
69 } |
|
70 return new JsrScope(returnBci, this); |
50 } |
71 } |
51 |
72 |
|
73 /** |
|
74 * Determines if this is the scope outside of any JSR/RET subroutine. |
|
75 */ |
52 public boolean isEmpty() { |
76 public boolean isEmpty() { |
53 return scope == 0; |
77 return returnAddress == 0; |
54 } |
78 } |
55 |
79 |
56 public boolean isPrefixOf(JsrScope other) { |
80 /** |
57 return (scope & other.scope) == scope; |
81 * Gets the ancestry of this scope starting with the {@link #returnAddress} of this scope's most |
|
82 * distant ancestor and ending with the {@link #returnAddress} of this object. |
|
83 * |
|
84 * @return a String where each character is a 16-bit BCI. This value can be converted to an |
|
85 * {@code int[]} with {@code value.chars().toArray()}. |
|
86 */ |
|
87 public String getAncestry() { |
|
88 StringBuilder sb = new StringBuilder(); |
|
89 for (JsrScope s = this; s != null; s = s.parent) { |
|
90 if (!s.isEmpty()) { |
|
91 sb.append(s.returnAddress); |
|
92 } |
|
93 } |
|
94 return sb.reverse().toString(); |
58 } |
95 } |
59 |
96 |
|
97 /** |
|
98 * Determines if the {@linkplain #getAncestry() ancestry} of this scope is a prefix of the |
|
99 * ancestry of {@code other}. |
|
100 */ |
|
101 public boolean isPrefixOf(JsrScope other) { |
|
102 if (isEmpty()) { |
|
103 return true; |
|
104 } |
|
105 String ancestry = getAncestry(); |
|
106 String otherAncestry = other.getAncestry(); |
|
107 return otherAncestry.startsWith(ancestry); |
|
108 } |
|
109 |
|
110 /** |
|
111 * Gets this scope's parent. |
|
112 * |
|
113 * @return this scope's parent or {@link #EMPTY_SCOPE} if this is the {@link #EMPTY_SCOPE} |
|
114 */ |
60 public JsrScope pop() { |
115 public JsrScope pop() { |
61 return new JsrScope(scope >>> 16); |
116 if (isEmpty()) { |
|
117 return this; |
|
118 } |
|
119 return parent; |
62 } |
120 } |
63 |
121 |
64 @Override |
122 @Override |
65 public int hashCode() { |
123 public int hashCode() { |
66 return (int) (scope ^ (scope >>> 32)); |
124 int hc = returnAddress; |
|
125 JsrScope ancestor = parent; |
|
126 while (ancestor != null) { |
|
127 hc = hc ^ ancestor.returnAddress; |
|
128 ancestor = ancestor.parent; |
|
129 } |
|
130 return hc; |
67 } |
131 } |
68 |
132 |
69 @Override |
133 @Override |
70 public boolean equals(Object obj) { |
134 public boolean equals(Object obj) { |
71 if (this == obj) { |
135 if (this == obj) { |
72 return true; |
136 return true; |
73 } |
137 } |
74 return obj != null && getClass() == obj.getClass() && scope == ((JsrScope) obj).scope; |
138 if (obj != null && getClass() == obj.getClass()) { |
|
139 JsrScope ancestor = this; |
|
140 JsrScope otherAncestor = (JsrScope) obj; |
|
141 while (ancestor != null) { |
|
142 if (otherAncestor == null) { |
|
143 return false; |
|
144 } |
|
145 if (otherAncestor.returnAddress != ancestor.returnAddress) { |
|
146 return false; |
|
147 } |
|
148 ancestor = ancestor.parent; |
|
149 otherAncestor = otherAncestor.parent; |
|
150 } |
|
151 if (otherAncestor == null) { |
|
152 return true; |
|
153 } |
|
154 } |
|
155 return false; |
75 } |
156 } |
76 |
157 |
77 @Override |
158 @Override |
78 public String toString() { |
159 public String toString() { |
79 StringBuilder sb = new StringBuilder(); |
160 StringBuilder sb = new StringBuilder(); |
80 long tmp = scope; |
161 |
81 sb.append(" ["); |
162 for (JsrScope ancestor = this; ancestor != null; ancestor = ancestor.parent) { |
82 while (tmp != 0) { |
163 if (!ancestor.isEmpty()) { |
83 sb.append(", ").append(tmp & 0xffff); |
164 if (sb.length() != 0) { |
84 tmp = tmp >>> 16; |
165 sb.append(", "); |
|
166 } |
|
167 sb.append((int) ancestor.returnAddress); |
|
168 } |
85 } |
169 } |
86 sb.append(']'); |
170 return "[" + sb + "]"; |
87 return sb.toString(); |
|
88 } |
171 } |
89 } |
172 } |