asmtools/src/org/openjdk/asmtools/jasm/CodeAttr.java

530 lines
16 KiB
Java

/*
* Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.asmtools.jasm;
import org.openjdk.asmtools.jasm.OpcodeTables.Opcode;
import org.openjdk.asmtools.jasm.Tables.AttrTag;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import static org.openjdk.asmtools.jasm.RuntimeConstants.SPLIT_VERIFIER_CFV;
class CodeAttr extends AttrData {
protected ClassData cls;
protected MethodData mtd;
protected Environment env;
protected Argument max_stack, max_locals;
protected Instr zeroInstr, lastInstr;
protected int cur_pc = 0;
protected DataVector<TrapData> trap_table; // TrapData
protected DataVectorAttr<LineNumData> lin_num_tb; // LineNumData
protected int lastln = 0;
protected DataVectorAttr<LocVarData> loc_var_tb; // LocVarData
protected DataVector<DataVectorAttr<? extends Data>> attrs;
protected ArrayList<Integer> slots;
protected HashMap<String, LocVarData> locvarsHash;
protected HashMap<String, Label> labelsHash;
protected HashMap<String, Trap> trapsHash;
protected StackMapData curMapEntry = null;
protected DataVectorAttr<StackMapData> stackMap;
// type annotations
protected DataVectorAttr<TypeAnnotationData> type_annotAttrVis = null;
protected DataVectorAttr<TypeAnnotationData> type_annotAttrInv = null;
public CodeAttr(MethodData mtd, int pos, int paramcnt, Argument max_stack, Argument max_locals) {
super(mtd.cls, AttrTag.ATT_Code.parsekey());
this.mtd = mtd;
this.cls = mtd.cls;
this.env = cls.env;
this.max_stack = max_stack;
this.max_locals = max_locals;
lastInstr = zeroInstr = new Instr();
trap_table = new DataVector<>(0); // TrapData
attrs = new DataVector<>();
if (env.debugInfoFlag) {
lin_num_tb = new DataVectorAttr<>(cls, AttrTag.ATT_LineNumberTable.parsekey());
attrs.add(lin_num_tb);
}
slots = new ArrayList<>(paramcnt);
for (int k = 0; k < paramcnt; k++) {
slots.add(k, 1);
}
}
void endCode() {
checkTraps();
checkLocVars();
checkLabels();
//
if (type_annotAttrVis != null) {
attrs.add(type_annotAttrVis);
}
if (type_annotAttrInv != null) {
attrs.add(type_annotAttrInv);
}
}
public void addAnnotations(ArrayList<AnnotationData> list) {
for (AnnotationData item : list) {
boolean invisible = item.invisible;
if (item instanceof TypeAnnotationData) {
// Type Annotations
TypeAnnotationData ta = (TypeAnnotationData) item;
if (invisible) {
if (type_annotAttrInv == null) {
type_annotAttrInv = new DataVectorAttr(cls,
AttrTag.ATT_RuntimeInvisibleTypeAnnotations.parsekey());
}
type_annotAttrInv.add(ta);
} else {
if (type_annotAttrVis == null) {
type_annotAttrVis = new DataVectorAttr(cls,
AttrTag.ATT_RuntimeVisibleTypeAnnotations.parsekey());
}
type_annotAttrVis.add(ta);
}
}
}
}
/* -------------------------------------- Traps */
Trap trapDecl(int pos, String name) {
Trap local;
if (trapsHash == null) {
trapsHash = new HashMap<>(10);
local = null;
} else {
local = trapsHash.get(name);
}
if (local == null) {
local = new Trap(pos, name);
trapsHash.put(name, local);
}
return local;
}
void beginTrap(int pos, String name) {
Trap trap = trapDecl(pos, name);
if (trap.start_pc != Argument.NotSet) {
env.error("trap.tryredecl", name);
return;
}
trap.start_pc = cur_pc;
}
void endTrap(int pos, String name) {
Trap trap = trapDecl(pos, name);
if (trap.end_pc != Argument.NotSet) {
env.error("trap.endtryredecl", name);
return;
}
trap.end_pc = cur_pc;
}
void trapHandler(int pos, String name, Argument type) {
Trap trap = trapDecl(pos, name);
trap.refd = true;
TrapData trapData = new TrapData(pos, trap, cur_pc, type);
trap_table.addElement(trapData);
}
void checkTraps() {
if (trapsHash == null) {
return;
}
for (Trap trap : trapsHash.values()) {
if (!trap.refd) {
env.error(trap.pos, "warn.trap.notref", trap.name);
}
}
for (TrapData trapData : trap_table) {
Trap trapLabel = trapData.trap;
if (trapLabel.start_pc == Argument.NotSet) {
env.error(trapData.pos, "trap.notry", trapLabel.name);
}
if (trapLabel.end_pc == Argument.NotSet) {
env.error(trapData.pos, "trap.noendtry", trapLabel.name);
}
}
}
/* -------------------------------------- Labels */
Label labelDecl(String name) {
Label local;
if (labelsHash == null) {
labelsHash = new HashMap<>(10);
local = null;
} else {
local = labelsHash.get(name);
}
if (local == null) {
local = new Label(name);
labelsHash.put(name, local);
}
return local;
}
public Label LabelDef(int pos, String name) {
Label label = labelDecl(name);
if (label.defd) {
env.error(pos, "label.redecl", name);
return null;
}
label.defd = true;
label.arg = cur_pc;
return label;
}
public Label LabelRef(String name) {
Label label = labelDecl(name);
label.refd = true;
return label;
}
void checkLabels() {
if (labelsHash == null) {
return;
}
for (Label local : labelsHash.values()) {
// check that every label is defined
if (!local.defd) {
env.error("label.undecl", local.name);
}
}
}
/* -------------------------------------- Variables */
LocVarData locvarDecl(String name) {
LocVarData local;
if (locvarsHash == null) {
locvarsHash = new HashMap<>(10);
local = null;
} else {
local = locvarsHash.get(name);
}
if (local == null) {
local = new LocVarData(name);
locvarsHash.put(name, local);
}
return local;
}
public void LocVarDataDef(int slot) {
slots.set(slot, 1);
if ((max_locals != null) && (max_locals.arg < slots.size())) {
env.error("warn.illslot", Integer.toString(slot));
}
}
public void LocVarDataDef(String name, ConstantPool.ConstCell type) {
LocVarData locvar = locvarDecl(name);
if (locvar.defd) {
env.error("locvar.redecl", name);
return;
}
locvar.defd = true;
locvar.start_pc = (short) cur_pc;
locvar.name_cpx = cls.pool.FindCellAsciz(name);
locvar.sig_cpx = type;
int k;
findSlot:
{
for (k = 0; k < slots.size(); k++) {
if (slots.get(k) == 0) {
break findSlot;
}
}
k = slots.size();
}
LocVarDataDef(k);
locvar.arg = k;
if (loc_var_tb == null) {
loc_var_tb = new DataVectorAttr<>(cls, AttrTag.ATT_LocalVariableTable.parsekey());
attrs.add(loc_var_tb);
}
loc_var_tb.add(locvar);
}
public Argument LocVarDataRef(String name) {
LocVarData locvar = locvarDecl(name);
if (!locvar.defd) {
env.error("locvar.undecl", name);
locvar.defd = true; // to avoid multiple error messages
}
locvar.refd = true;
return locvar;
}
public void LocVarDataEnd(int slot) {
slots.set(slot, 0);
}
public void LocVarDataEnd(String name) {
LocVarData locvar = locvarsHash.get(name);
if (locvar == null) {
env.error("locvar.undecl", name);
return;
} else if (!locvar.defd) {
env.error("locvar.undecl", name);
return;
}
locvar.length = (short) (cur_pc - locvar.start_pc);
slots.set(locvar.arg, 0);
locvarsHash.put(name, new LocVarData(name));
}
void checkLocVars() {
if (locvarsHash == null) {
return;
}
for (LocVarData locvar : locvarsHash.values()) {
if (!locvar.defd) {
continue;
} // this is false locvar
// set end of scope, if not set
if (slots.get(locvar.arg) == 1) {
locvar.length = (short) (cur_pc - locvar.start_pc);
slots.set(locvar.arg, 0);
}
}
}
/* -------------------------------------- StackMap */
public StackMapData getStackMap() {
if (curMapEntry == null) {
curMapEntry = new StackMapData(env);
if (cls.cfv.major_version() >= SPLIT_VERIFIER_CFV) {
curMapEntry.setIsStackMapTable(true);
}
}
return curMapEntry;
}
/*====================================================== Instr */
void addInstr(int mnenoc_pos, Opcode opcode, Argument arg, Object arg2) {
Instr newInstr = new Instr(cur_pc, cls.env.pos, opcode, arg, arg2);
lastInstr.next = newInstr;
lastInstr = newInstr;
int len = opcode.length();
switch (opcode) {
case opc_tableswitch:
len = ((SwitchTable) arg2).recalcTableSwitch(cur_pc);
break;
case opc_lookupswitch:
len = ((SwitchTable) arg2).calcLookupSwitch(cur_pc);
break;
case opc_ldc:
((ConstantPool.ConstCell) arg).setRank(0);
break;
default:
if (arg instanceof ConstantPool.ConstCell) {
((ConstantPool.ConstCell) arg).setRank(1);
}
}
if (env.debugInfoFlag) {
int ln = env.lineNumber(mnenoc_pos);
if (ln != lastln) { // only one entry in lin_num_tb per line
lin_num_tb.add(new LineNumData(cur_pc, ln));
lastln = ln;
}
}
if (curMapEntry != null) {
curMapEntry.pc = cur_pc;
StackMapData prevStackFrame = null;
if (stackMap == null) {
if (cls.cfv.major_version() >= SPLIT_VERIFIER_CFV) {
stackMap = new DataVectorAttr<>(cls, AttrTag.ATT_StackMapTable.parsekey());
} else {
stackMap = new DataVectorAttr<>(cls, AttrTag.ATT_StackMap.parsekey());
}
attrs.add(stackMap);
} else if (stackMap.size() > 0) {
prevStackFrame = stackMap.get(stackMap.size() - 1);
}
curMapEntry.setOffset(prevStackFrame);
stackMap.add(curMapEntry);
curMapEntry = null;
}
cur_pc += len;
}
/*====================================================== Attr interface */
// subclasses must redefine this
@Override
public int attrLength() {
return 2 + 2 + 4 // for max_stack, max_locals, and cur_pc
+ cur_pc // + 2+trap_table.size()*8
+ trap_table.getLength() + attrs.getLength();
}
@Override
public void write(CheckedDataOutputStream out)
throws IOException, Parser.CompilerError {
int mxstck = (max_stack != null) ? max_stack.arg : 0;
int mxloc = (max_locals != null) ? max_locals.arg : slots.size();
super.write(out); // attr name, attr len
out.writeShort(mxstck);
out.writeShort(mxloc);
out.writeInt(cur_pc);
for (Instr instr = zeroInstr.next; instr != null; instr = instr.next) {
instr.write(out, env);
}
trap_table.write(out);
attrs.write(out);
}
/*-------------------------------------------------------- */
/* CodeAttr inner classes */
static public class Local extends Argument {
String name;
boolean defd = false, refd = false;
public Local(String name) {
this.name = name;
}
}
/**
*
*/
static public class Label extends Local {
public Label(String name) {
super(name);
}
}
/**
*
*/
class LocVarData extends Local implements Data {
// arg means slot
short start_pc, length;
ConstantPool.ConstCell name_cpx, sig_cpx;
public LocVarData(String name) {
super(name);
}
@Override
public int getLength() {
return 10;
}
@Override
public void write(CheckedDataOutputStream out) throws IOException {
out.writeShort(start_pc);
out.writeShort(length);
out.writeShort(name_cpx.arg);
out.writeShort(sig_cpx.arg);
out.writeShort(arg);
}
}
/**
*
*/
class LineNumData implements Data {
int start_pc, line_number;
public LineNumData(int start_pc, int line_number) {
this.start_pc = start_pc;
this.line_number = line_number;
}
@Override
public int getLength() {
return 4;
}
@Override
public void write(CheckedDataOutputStream out) throws IOException {
out.writeShort(start_pc);
out.writeShort(line_number);
}
}
/**
*
*/
class Trap extends Local {
int start_pc = Argument.NotSet, end_pc = Argument.NotSet;
int pos;
Trap(int pos, String name) {
super(name);
this.pos = pos;
}
}
/**
*
*/
class TrapData implements Data {
int pos;
Trap trap;
int handler_pc;
Argument catchType;
public TrapData(int pos, Trap trap, int handler_pc, Argument catchType) {
this.pos = pos;
this.trap = trap;
this.handler_pc = handler_pc;
this.catchType = catchType;
}
@Override
public int getLength() {
return 8; // add the length of number of elements
}
@Override
public void write(CheckedDataOutputStream out) throws IOException {
out.writeShort(trap.start_pc);
out.writeShort(trap.end_pc);
out.writeShort(handler_pc);
if (catchType.isSet()) {
out.writeShort(catchType.arg);
} else {
out.writeShort(0);
}
}
} // end TrapData
} // end CodeAttr