/* * 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 trap_table; // TrapData protected DataVectorAttr lin_num_tb; // LineNumData protected int lastln = 0; protected DataVectorAttr loc_var_tb; // LocVarData protected DataVector> attrs; protected ArrayList slots; protected HashMap locvarsHash; protected HashMap labelsHash; protected HashMap trapsHash; protected StackMapData curMapEntry = null; protected DataVectorAttr stackMap; // type annotations protected DataVectorAttr type_annotAttrVis = null; protected DataVectorAttr 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 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