1 /*
2 * Copyright (c) 1999, 2024, 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
26 package com.sun.tools.javac.jvm;
27
28 import com.sun.tools.javac.code.*;
29 import com.sun.tools.javac.code.Symbol.*;
30 import com.sun.tools.javac.resources.CompilerProperties.Errors;
31 import com.sun.tools.javac.util.*;
32 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
33
34 import java.util.function.ToIntBiFunction;
35 import java.util.function.ToIntFunction;
36
37 import static com.sun.tools.javac.code.TypeTag.BOT;
38 import static com.sun.tools.javac.code.TypeTag.DOUBLE;
39 import static com.sun.tools.javac.code.TypeTag.INT;
40 import static com.sun.tools.javac.code.TypeTag.LONG;
41 import static com.sun.tools.javac.jvm.ByteCodes.*;
42 import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_Class;
43 import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_Double;
44 import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_Fieldref;
45 import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_Float;
46 import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_Integer;
47 import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_InterfaceMethodref;
48 import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_Long;
49 import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_MethodHandle;
50 import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_MethodType;
51 import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_Methodref;
52 import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_String;
53 import static com.sun.tools.javac.jvm.UninitializedType.*;
54 import static com.sun.tools.javac.jvm.ClassWriter.StackMapTableFrame;
55 import java.util.Arrays;
56
57 /** An internal structure that corresponds to the code attribute of
58 * methods in a classfile. The class also provides some utility operations to
59 * generate bytecode instructions.
60 *
61 * <p><b>This is NOT part of any supported API.
62 * If you write code that depends on this, you do so at your own risk.
63 * This code and its internal interfaces are subject to change or
64 * deletion without notice.</b>
65 */
66 public class Code {
67
68 public final boolean debugCode;
69 public final boolean needStackMap;
70
71 public enum StackMapFormat {
72 NONE,
73 CLDC {
74 Name getAttributeName(Names names) {
75 return names.StackMap;
180
181 /** The stack map format to be generated. */
182 StackMapFormat stackMap;
183
184 /** Switch: emit variable debug info.
185 */
186 boolean varDebugInfo;
187
188 /** Switch: emit line number info.
189 */
190 boolean lineDebugInfo;
191
192 /** Emit line number info if map supplied
193 */
194 Position.LineMap lineMap;
195
196 final MethodSymbol meth;
197
198 private int letExprStackPos = 0;
199
200 /** Construct a code object, given the settings of the fatcode,
201 * debugging info switches and the CharacterRangeTable.
202 */
203 public Code(MethodSymbol meth,
204 boolean fatcode,
205 Position.LineMap lineMap,
206 boolean varDebugInfo,
207 StackMapFormat stackMap,
208 boolean debugCode,
209 CRTable crt,
210 Symtab syms,
211 Types types,
212 PoolWriter poolWriter) {
213 this.meth = meth;
214 this.fatcode = fatcode;
215 this.lineMap = lineMap;
216 this.lineDebugInfo = lineMap != null;
217 this.varDebugInfo = varDebugInfo;
218 this.crt = crt;
219 this.syms = syms;
220 this.types = types;
221 this.poolWriter = poolWriter;
222 this.debugCode = debugCode;
223 this.stackMap = stackMap;
224 switch (stackMap) {
225 case CLDC:
226 case JSR202:
227 this.needStackMap = true;
228 break;
229 default:
230 this.needStackMap = false;
231 }
232 state = new State();
233 lvar = new LocalVar[20];
234 }
235
236
237 /* **************************************************************************
238 * Typecodes & related stuff
239 ****************************************************************************/
240
241 /** Given a type, return its type code (used implicitly in the
242 * JVM architecture).
243 */
244 public static int typecode(Type type) {
245 switch (type.getTag()) {
246 case BYTE: return BYTEcode;
247 case SHORT: return SHORTcode;
248 case CHAR: return CHARcode;
249 case INT: return INTcode;
250 case LONG: return LONGcode;
251 case FLOAT: return FLOATcode;
252 case DOUBLE: return DOUBLEcode;
253 case BOOLEAN: return BYTEcode;
1064 case checkcast: {
1065 state.pop(1); // object ref
1066 Type t = types.erasure((Type)data);
1067 state.push(t);
1068 break; }
1069 case ldc2:
1070 case ldc2w:
1071 state.push(types.constantType((LoadableConstant)data));
1072 break;
1073 case instanceof_:
1074 state.pop(1);
1075 state.push(syms.intType);
1076 break;
1077 case jsr:
1078 break;
1079 default:
1080 throw new AssertionError(mnem(op));
1081 }
1082 // postop();
1083 }
1084
1085 /** Emit an opcode with a four-byte operand field.
1086 */
1087 public void emitop4(int op, int od) {
1088 emitop(op);
1089 if (!alive) return;
1090 emit4(od);
1091 switch (op) {
1092 case goto_w:
1093 markDead();
1094 break;
1095 case jsr_w:
1096 break;
1097 default:
1098 throw new AssertionError(mnem(op));
1099 }
1100 // postop();
1101 }
1102
1103 /** Align code pointer to next `incr' boundary.
1104 */
1211
1212 public int setLetExprStackPos(int pos) {
1213 int res = letExprStackPos;
1214 letExprStackPos = pos;
1215 return res;
1216 }
1217
1218 public boolean isStatementStart() {
1219 return !alive || state.stacksize == letExprStackPos;
1220 }
1221
1222 /* ************************************************************************
1223 * Stack map generation
1224 *************************************************************************/
1225
1226 /** An entry in the stack map. */
1227 static class StackMapFrame {
1228 int pc;
1229 Type[] locals;
1230 Type[] stack;
1231 }
1232
1233 /** A buffer of cldc stack map entries. */
1234 StackMapFrame[] stackMapBuffer = null;
1235
1236 /** A buffer of compressed StackMapTable entries. */
1237 StackMapTableFrame[] stackMapTableBuffer = null;
1238 int stackMapBufferSize = 0;
1239
1240 /** The last PC at which we generated a stack map. */
1241 int lastStackMapPC = -1;
1242
1243 /** The last stack map frame in StackMapTable. */
1244 StackMapFrame lastFrame = null;
1245
1246 /** The stack map frame before the last one. */
1247 StackMapFrame frameBeforeLast = null;
1248
1249 /** Emit a stack map entry. */
1250 public void emitStackMap() {
1251 int pc = curCP();
1252 if (!needStackMap) return;
1253
1254
1255
1256 switch (stackMap) {
1257 case CLDC:
1338 for (int i=0, j=0; i<localsSize; i++, j++) {
1339 Assert.check(j < localCount);
1340 frame.locals[j] = locals[i];
1341 if (width(locals[i]) > 1) i++;
1342 }
1343
1344 int stackCount = 0;
1345 for (int i=0; i<state.stacksize; i++) {
1346 if (state.stack[i] != null) {
1347 stackCount++;
1348 }
1349 }
1350 frame.stack = new Type[stackCount];
1351 stackCount = 0;
1352 for (int i=0; i<state.stacksize; i++) {
1353 if (state.stack[i] != null) {
1354 frame.stack[stackCount++] = types.erasure(state.stack[i]);
1355 }
1356 }
1357
1358 if (stackMapTableBuffer == null) {
1359 stackMapTableBuffer = new StackMapTableFrame[20];
1360 } else {
1361 stackMapTableBuffer = ArrayUtils.ensureCapacity(
1362 stackMapTableBuffer,
1363 stackMapBufferSize);
1364 }
1365 stackMapTableBuffer[stackMapBufferSize++] =
1366 StackMapTableFrame.getInstance(frame, lastFrame.pc, lastFrame.locals, types);
1367
1368 frameBeforeLast = lastFrame;
1369 lastFrame = frame;
1370 }
1371
1372 StackMapFrame getInitialFrame() {
1373 StackMapFrame frame = new StackMapFrame();
1374 List<Type> arg_types = ((MethodType)meth.externalType(types)).argtypes;
1375 int len = arg_types.length();
1376 int count = 0;
1377 if (!meth.isStatic()) {
1378 Type thisType = meth.owner.type;
1379 frame.locals = new Type[len+1];
1380 if (meth.isConstructor() && thisType != syms.objectType) {
1381 frame.locals[count++] = UninitializedType.uninitializedThis(thisType);
1382 } else {
1383 frame.locals[count++] = types.erasure(thisType);
1384 }
1385 } else {
1386 frame.locals = new Type[len];
1387 }
1388 for (Type arg_type : arg_types) {
1389 frame.locals[count++] = types.erasure(arg_type);
1390 }
1391 frame.pc = -1;
1392 frame.stack = null;
1393 return frame;
1394 }
1395
1396
1397 /* ************************************************************************
1398 * Operations having to do with jumps
1399 *************************************************************************/
1400
1401 /** A chain represents a list of unresolved jumps. Jump locations
1402 * are sorted in decreasing order.
1403 */
1404 public static class Chain {
1405
1406 /** The position of the jump instruction.
1407 */
1408 public final int pc;
1409
1410 /** The machine state after the jump instruction.
1411 * Invariant: all elements of a chain list have the same stacksize
1412 * and compatible stack and register contents.
1451 return cp - 5;
1452 } else {
1453 emitop2(opcode, 0);
1454 return cp - 3;
1455 }
1456 }
1457
1458 /** Emit a branch with given opcode; return its chain.
1459 * branch differs from jump in that jsr is treated as no-op.
1460 */
1461 public Chain branch(int opcode) {
1462 Chain result = null;
1463 if (opcode == goto_) {
1464 result = pendingJumps;
1465 pendingJumps = null;
1466 }
1467 if (opcode != dontgoto && isAlive()) {
1468 result = new Chain(emitJump(opcode),
1469 result,
1470 state.dup());
1471 fixedPc = fatcode;
1472 if (opcode == goto_) alive = false;
1473 }
1474 return result;
1475 }
1476
1477 /** Resolve chain to point to given target.
1478 */
1479 public void resolve(Chain chain, int target) {
1480 boolean changed = false;
1481 State newState = state;
1482 for (; chain != null; chain = chain.next) {
1483 Assert.check(state != chain.state
1484 && (target > chain.pc || isStatementStart()));
1485 if (target >= cp) {
1486 target = cp;
1487 } else if (get1(target) == goto_) {
1488 if (fatcode) target = target + get4(target + 1);
1489 else target = target + get2(target + 1);
1490 }
1491 if (get1(chain.pc) == goto_ &&
1492 chain.pc + 3 == target && target == cp && !fixedPc) {
1493 // If goto the next instruction, the jump is not needed:
1494 // compact the code.
1495 if (varDebugInfo) {
1496 adjustAliveRanges(cp, -3);
1497 }
1498 cp = cp - 3;
1499 target = target - 3;
1500 if (chain.next == null) {
1501 // This is the only jump to the target. Exit the loop
1502 // without setting new state. The code is reachable
1503 // from the instruction before goto_.
1504 alive = true;
1505 break;
1506 }
1507 } else {
1508 if (fatcode)
1509 put4(chain.pc + 1, target - chain.pc);
1510 else if (target - chain.pc < Short.MIN_VALUE ||
1511 target - chain.pc > Short.MAX_VALUE)
1512 fatcode = true;
1513 else
1514 put2(chain.pc + 1, target - chain.pc);
1515 Assert.check(!alive ||
1516 chain.state.stacksize == newState.stacksize &&
1517 chain.state.nlocks == newState.nlocks);
1518 }
1519 fixedPc = true;
1520 if (cp == target) {
1521 changed = true;
1522 if (debugCode)
1523 System.err.println("resolving chain state=" + chain.state);
1524 if (alive) {
1525 newState = chain.state.join(newState);
1526 } else {
1527 newState = chain.state;
1528 alive = true;
1529 }
1530 }
1531 }
1532 Assert.check(!changed || state != newState);
1533 if (state != newState) {
1534 setDefined(newState.defined);
|
1 /*
2 * Copyright (c) 1999, 2025, 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
26 package com.sun.tools.javac.jvm;
27
28 import com.sun.tools.javac.code.*;
29 import com.sun.tools.javac.code.Symbol.*;
30 import com.sun.tools.javac.resources.CompilerProperties.Errors;
31 import com.sun.tools.javac.util.*;
32 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
33
34 import java.util.function.ToIntBiFunction;
35
36 import static com.sun.tools.javac.code.TypeTag.BOT;
37 import static com.sun.tools.javac.code.TypeTag.DOUBLE;
38 import static com.sun.tools.javac.code.TypeTag.INT;
39 import static com.sun.tools.javac.code.TypeTag.LONG;
40 import static com.sun.tools.javac.jvm.ByteCodes.*;
41 import static com.sun.tools.javac.jvm.UninitializedType.*;
42 import static com.sun.tools.javac.jvm.ClassWriter.StackMapTableEntry;
43 import java.util.Arrays;
44 import java.util.Map;
45 import java.util.HashMap;
46 import java.util.Set;
47
48 /** An internal structure that corresponds to the code attribute of
49 * methods in a classfile. The class also provides some utility operations to
50 * generate bytecode instructions.
51 *
52 * <p><b>This is NOT part of any supported API.
53 * If you write code that depends on this, you do so at your own risk.
54 * This code and its internal interfaces are subject to change or
55 * deletion without notice.</b>
56 */
57 public class Code {
58
59 public final boolean debugCode;
60 public final boolean needStackMap;
61
62 public enum StackMapFormat {
63 NONE,
64 CLDC {
65 Name getAttributeName(Names names) {
66 return names.StackMap;
171
172 /** The stack map format to be generated. */
173 StackMapFormat stackMap;
174
175 /** Switch: emit variable debug info.
176 */
177 boolean varDebugInfo;
178
179 /** Switch: emit line number info.
180 */
181 boolean lineDebugInfo;
182
183 /** Emit line number info if map supplied
184 */
185 Position.LineMap lineMap;
186
187 final MethodSymbol meth;
188
189 private int letExprStackPos = 0;
190
191 private Map<Integer, Set<VarSymbol>> cpToUnsetFieldsMap = new HashMap<>();
192
193 public Set<VarSymbol> initialUnsetFields;
194
195 public Set<VarSymbol> currentUnsetFields;
196
197 boolean generateAssertUnsetFieldsFrame;
198
199 /** Construct a code object, given the settings of the fatcode,
200 * debugging info switches and the CharacterRangeTable.
201 */
202 public Code(MethodSymbol meth,
203 boolean fatcode,
204 Position.LineMap lineMap,
205 boolean varDebugInfo,
206 StackMapFormat stackMap,
207 boolean debugCode,
208 CRTable crt,
209 Symtab syms,
210 Types types,
211 PoolWriter poolWriter,
212 boolean generateAssertUnsetFieldsFrame) {
213 this.meth = meth;
214 this.fatcode = fatcode;
215 this.lineMap = lineMap;
216 this.lineDebugInfo = lineMap != null;
217 this.varDebugInfo = varDebugInfo;
218 this.crt = crt;
219 this.syms = syms;
220 this.types = types;
221 this.poolWriter = poolWriter;
222 this.debugCode = debugCode;
223 this.stackMap = stackMap;
224 switch (stackMap) {
225 case CLDC:
226 case JSR202:
227 this.needStackMap = true;
228 break;
229 default:
230 this.needStackMap = false;
231 }
232 state = new State();
233 lvar = new LocalVar[20];
234 this.generateAssertUnsetFieldsFrame = generateAssertUnsetFieldsFrame;
235 }
236
237
238 /* **************************************************************************
239 * Typecodes & related stuff
240 ****************************************************************************/
241
242 /** Given a type, return its type code (used implicitly in the
243 * JVM architecture).
244 */
245 public static int typecode(Type type) {
246 switch (type.getTag()) {
247 case BYTE: return BYTEcode;
248 case SHORT: return SHORTcode;
249 case CHAR: return CHARcode;
250 case INT: return INTcode;
251 case LONG: return LONGcode;
252 case FLOAT: return FLOATcode;
253 case DOUBLE: return DOUBLEcode;
254 case BOOLEAN: return BYTEcode;
1065 case checkcast: {
1066 state.pop(1); // object ref
1067 Type t = types.erasure((Type)data);
1068 state.push(t);
1069 break; }
1070 case ldc2:
1071 case ldc2w:
1072 state.push(types.constantType((LoadableConstant)data));
1073 break;
1074 case instanceof_:
1075 state.pop(1);
1076 state.push(syms.intType);
1077 break;
1078 case jsr:
1079 break;
1080 default:
1081 throw new AssertionError(mnem(op));
1082 }
1083 // postop();
1084 }
1085 /** Emit an opcode with a four-byte operand field.
1086 */
1087 public void emitop4(int op, int od) {
1088 emitop(op);
1089 if (!alive) return;
1090 emit4(od);
1091 switch (op) {
1092 case goto_w:
1093 markDead();
1094 break;
1095 case jsr_w:
1096 break;
1097 default:
1098 throw new AssertionError(mnem(op));
1099 }
1100 // postop();
1101 }
1102
1103 /** Align code pointer to next `incr' boundary.
1104 */
1211
1212 public int setLetExprStackPos(int pos) {
1213 int res = letExprStackPos;
1214 letExprStackPos = pos;
1215 return res;
1216 }
1217
1218 public boolean isStatementStart() {
1219 return !alive || state.stacksize == letExprStackPos;
1220 }
1221
1222 /* ************************************************************************
1223 * Stack map generation
1224 *************************************************************************/
1225
1226 /** An entry in the stack map. */
1227 static class StackMapFrame {
1228 int pc;
1229 Type[] locals;
1230 Type[] stack;
1231 Set<VarSymbol> unsetFields;
1232 }
1233
1234 /** A buffer of cldc stack map entries. */
1235 StackMapFrame[] stackMapBuffer = null;
1236
1237 /** A buffer of compressed StackMapTable entries. */
1238 StackMapTableEntry[] stackMapTableBuffer = null;
1239 int stackMapBufferSize = 0;
1240
1241 /** The last PC at which we generated a stack map. */
1242 int lastStackMapPC = -1;
1243
1244 /** The last stack map frame in StackMapTable. */
1245 StackMapFrame lastFrame = null;
1246
1247 /** The stack map frame before the last one. */
1248 StackMapFrame frameBeforeLast = null;
1249
1250 /** Emit a stack map entry. */
1251 public void emitStackMap() {
1252 int pc = curCP();
1253 if (!needStackMap) return;
1254
1255
1256
1257 switch (stackMap) {
1258 case CLDC:
1339 for (int i=0, j=0; i<localsSize; i++, j++) {
1340 Assert.check(j < localCount);
1341 frame.locals[j] = locals[i];
1342 if (width(locals[i]) > 1) i++;
1343 }
1344
1345 int stackCount = 0;
1346 for (int i=0; i<state.stacksize; i++) {
1347 if (state.stack[i] != null) {
1348 stackCount++;
1349 }
1350 }
1351 frame.stack = new Type[stackCount];
1352 stackCount = 0;
1353 for (int i=0; i<state.stacksize; i++) {
1354 if (state.stack[i] != null) {
1355 frame.stack[stackCount++] = types.erasure(state.stack[i]);
1356 }
1357 }
1358
1359 Set<VarSymbol> unsetFieldsAtPC = cpToUnsetFieldsMap.get(pc);
1360 boolean generateAssertUnsetFieldsEntry = unsetFieldsAtPC != null && generateAssertUnsetFieldsFrame && !lastFrame.unsetFields.equals(unsetFieldsAtPC) ;
1361
1362 if (stackMapTableBuffer == null) {
1363 stackMapTableBuffer = new StackMapTableEntry[20];
1364 } else {
1365 stackMapTableBuffer = ArrayUtils.ensureCapacity(
1366 stackMapTableBuffer,
1367 stackMapBufferSize + (generateAssertUnsetFieldsEntry ? 1 : 0));
1368 }
1369
1370 if (generateAssertUnsetFieldsEntry) {
1371 stackMapTableBuffer[stackMapBufferSize++] = new StackMapTableEntry.AssertUnsetFields(pc, unsetFieldsAtPC);
1372 frame.unsetFields = unsetFieldsAtPC;
1373 } else {
1374 frame.unsetFields = lastFrame.unsetFields;
1375 }
1376 stackMapTableBuffer[stackMapBufferSize++] =
1377 StackMapTableEntry.getInstance(frame, lastFrame, types, pc);
1378
1379 frameBeforeLast = lastFrame;
1380 lastFrame = frame;
1381 }
1382
1383 public void addUnsetFieldsAtPC(int pc, Set<VarSymbol> unsetFields) {
1384 cpToUnsetFieldsMap.put(pc, unsetFields);
1385 }
1386
1387 StackMapFrame getInitialFrame() {
1388 StackMapFrame frame = new StackMapFrame();
1389 List<Type> arg_types = ((MethodType)meth.externalType(types)).argtypes;
1390 int len = arg_types.length();
1391 int count = 0;
1392 if (!meth.isStatic()) {
1393 Type thisType = meth.owner.type;
1394 frame.locals = new Type[len+1];
1395 if (meth.isConstructor() && thisType != syms.objectType) {
1396 frame.locals[count++] = UninitializedType.uninitializedThis(thisType);
1397 } else {
1398 frame.locals[count++] = types.erasure(thisType);
1399 }
1400 } else {
1401 frame.locals = new Type[len];
1402 }
1403 for (Type arg_type : arg_types) {
1404 frame.locals[count++] = types.erasure(arg_type);
1405 }
1406 frame.pc = -1;
1407 frame.stack = null;
1408 frame.unsetFields = initialUnsetFields;
1409 return frame;
1410 }
1411
1412
1413 /* ************************************************************************
1414 * Operations having to do with jumps
1415 *************************************************************************/
1416
1417 /** A chain represents a list of unresolved jumps. Jump locations
1418 * are sorted in decreasing order.
1419 */
1420 public static class Chain {
1421
1422 /** The position of the jump instruction.
1423 */
1424 public final int pc;
1425
1426 /** The machine state after the jump instruction.
1427 * Invariant: all elements of a chain list have the same stacksize
1428 * and compatible stack and register contents.
1467 return cp - 5;
1468 } else {
1469 emitop2(opcode, 0);
1470 return cp - 3;
1471 }
1472 }
1473
1474 /** Emit a branch with given opcode; return its chain.
1475 * branch differs from jump in that jsr is treated as no-op.
1476 */
1477 public Chain branch(int opcode) {
1478 Chain result = null;
1479 if (opcode == goto_) {
1480 result = pendingJumps;
1481 pendingJumps = null;
1482 }
1483 if (opcode != dontgoto && isAlive()) {
1484 result = new Chain(emitJump(opcode),
1485 result,
1486 state.dup());
1487 if (currentUnsetFields != null) {
1488 addUnsetFieldsAtPC(result.pc, currentUnsetFields);
1489 }
1490 fixedPc = fatcode;
1491 if (opcode == goto_) alive = false;
1492 }
1493 return result;
1494 }
1495
1496 /** Resolve chain to point to given target.
1497 */
1498 public void resolve(Chain chain, int target) {
1499 boolean changed = false;
1500 State newState = state;
1501 int originalTarget = target;
1502 for (; chain != null; chain = chain.next) {
1503 Assert.check(state != chain.state
1504 && (target > chain.pc || isStatementStart()));
1505 if (target >= cp) {
1506 target = cp;
1507 } else if (get1(target) == goto_) {
1508 if (fatcode) target = target + get4(target + 1);
1509 else target = target + get2(target + 1);
1510 }
1511 if (get1(chain.pc) == goto_ &&
1512 chain.pc + 3 == target && target == cp && !fixedPc) {
1513 // If goto the next instruction, the jump is not needed:
1514 // compact the code.
1515 if (varDebugInfo) {
1516 adjustAliveRanges(cp, -3);
1517 }
1518 cp = cp - 3;
1519 target = target - 3;
1520 if (chain.next == null) {
1521 // This is the only jump to the target. Exit the loop
1522 // without setting new state. The code is reachable
1523 // from the instruction before goto_.
1524 alive = true;
1525 break;
1526 }
1527 } else {
1528 if (fatcode) {
1529 put4(chain.pc + 1, target - chain.pc);
1530 if (cpToUnsetFieldsMap.get(chain.pc) != null) {
1531 addUnsetFieldsAtPC(originalTarget, cpToUnsetFieldsMap.get(chain.pc));
1532 }
1533 }
1534 else if (target - chain.pc < Short.MIN_VALUE ||
1535 target - chain.pc > Short.MAX_VALUE)
1536 fatcode = true;
1537 else {
1538 put2(chain.pc + 1, target - chain.pc);
1539 if (cpToUnsetFieldsMap.get(chain.pc) != null) {
1540 addUnsetFieldsAtPC(originalTarget, cpToUnsetFieldsMap.get(chain.pc));
1541 }
1542 }
1543 Assert.check(!alive ||
1544 chain.state.stacksize == newState.stacksize &&
1545 chain.state.nlocks == newState.nlocks);
1546 }
1547 fixedPc = true;
1548 if (cp == target) {
1549 changed = true;
1550 if (debugCode)
1551 System.err.println("resolving chain state=" + chain.state);
1552 if (alive) {
1553 newState = chain.state.join(newState);
1554 } else {
1555 newState = chain.state;
1556 alive = true;
1557 }
1558 }
1559 }
1560 Assert.check(!changed || state != newState);
1561 if (state != newState) {
1562 setDefined(newState.defined);
|