1 /* 2 * Copyright (c) 2011, 2019, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 25 package org.graalvm.compiler.java; 26 27 import org.graalvm.compiler.bytecode.Bytecodes; 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 */ 37 public static final JsrScope EMPTY_SCOPE = new JsrScope(); 38 39 private final char returnAddress; 40 41 private final JsrScope parent; 42 43 private JsrScope(int returnBci, JsrScope parent) { 44 this.returnAddress = (char) returnBci; 45 this.parent = parent; 46 } 47 48 private JsrScope() { 49 this.returnAddress = 0; 50 this.parent = null; 51 } 52 53 public int nextReturnAddress() { 54 return returnAddress; 55 } 56 57 /** 58 * Enters a new subroutine from the current scope represented by this object. 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"); 66 } 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); 71 } 72 73 /** 74 * Determines if this is the scope outside of any JSR/RET subroutine. 75 */ 76 public boolean isEmpty() { 77 return returnAddress == 0; 78 } 79 80 /** 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(); 95 } 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 */ 115 public JsrScope pop() { 116 if (isEmpty()) { 117 return this; 118 } 119 return parent; 120 } 121 122 @Override 123 public int hashCode() { 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; 131 } 132 133 @Override 134 public boolean equals(Object obj) { 135 if (this == obj) { 136 return true; 137 } 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; 156 } 157 158 @Override 159 public String toString() { 160 StringBuilder sb = new StringBuilder(); 161 162 for (JsrScope ancestor = this; ancestor != null; ancestor = ancestor.parent) { 163 if (!ancestor.isEmpty()) { 164 if (sb.length() != 0) { 165 sb.append(", "); 166 } 167 sb.append((int) ancestor.returnAddress); 168 } 169 } 170 return "[" + sb + "]"; 171 } 172 }