1 /*
  2  * Copyright (c) 1996, 2020, 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 package org.openjdk.asmtools.jasm;
 24 
 25 import org.openjdk.asmtools.jasm.OpcodeTables.Opcode;
 26 import org.openjdk.asmtools.jasm.Tables.AttrTag;
 27 
 28 import java.io.IOException;
 29 import java.util.ArrayList;
 30 import java.util.HashMap;
 31 
 32 import static org.openjdk.asmtools.jasm.RuntimeConstants.SPLIT_VERIFIER_CFV;
 33 
 34 class CodeAttr extends AttrData {
 35 
 36     protected ClassData cls;
 37 
 38     protected MethodData mtd;
 39     protected Environment env;
 40     protected Argument max_stack, max_locals;
 41     protected Instr zeroInstr, lastInstr;
 42     protected int cur_pc = 0;
 43     protected DataVector<TrapData> trap_table; // TrapData
 44     protected DataVectorAttr<LineNumData> lin_num_tb; // LineNumData
 45     protected int lastln = 0;
 46     protected DataVectorAttr<LocVarData> loc_var_tb;  // LocVarData
 47     protected DataVector<DataVectorAttr<? extends Data>> attrs;
 48     protected ArrayList<Integer> slots;
 49     protected HashMap<String, LocVarData> locvarsHash;
 50     protected HashMap<String, Label> labelsHash;
 51     protected HashMap<String, Trap> trapsHash;
 52     protected StackMapData curMapEntry = null;
 53     protected DataVectorAttr<StackMapData> stackMap;
 54     // type annotations
 55     protected DataVectorAttr<TypeAnnotationData> type_annotAttrVis = null;
 56     protected DataVectorAttr<TypeAnnotationData> type_annotAttrInv = null;
 57 
 58     public CodeAttr(MethodData mtd, int pos, int paramcnt, Argument max_stack, Argument max_locals) {
 59         super(mtd.cls, AttrTag.ATT_Code.parsekey());
 60         this.mtd = mtd;
 61         this.cls = mtd.cls;
 62         this.env = cls.env;
 63         this.max_stack = max_stack;
 64         this.max_locals = max_locals;
 65         lastInstr = zeroInstr = new Instr();
 66         trap_table = new DataVector<>(0); // TrapData
 67         attrs = new DataVector<>();
 68         if (env.debugInfoFlag) {
 69             lin_num_tb = new DataVectorAttr<>(cls, AttrTag.ATT_LineNumberTable.parsekey());
 70             attrs.add(lin_num_tb);
 71         }
 72         slots = new ArrayList<>(paramcnt);
 73         for (int k = 0; k < paramcnt; k++) {
 74             slots.add(k, 1);
 75         }
 76     }
 77 
 78     void endCode() {
 79         checkTraps();
 80         checkLocVars();
 81         checkLabels();
 82         //
 83         if (type_annotAttrVis != null) {
 84             attrs.add(type_annotAttrVis);
 85         }
 86         if (type_annotAttrInv != null) {
 87             attrs.add(type_annotAttrInv);
 88         }
 89     }
 90 
 91     public void addAnnotations(ArrayList<AnnotationData> list) {
 92         for (AnnotationData item : list) {
 93             boolean invisible = item.invisible;
 94             if (item instanceof TypeAnnotationData) {
 95                 // Type Annotations
 96                 TypeAnnotationData ta = (TypeAnnotationData) item;
 97                 if (invisible) {
 98                     if (type_annotAttrInv == null) {
 99                         type_annotAttrInv = new DataVectorAttr(cls,
100                                 AttrTag.ATT_RuntimeInvisibleTypeAnnotations.parsekey());
101                     }
102                     type_annotAttrInv.add(ta);
103                 } else {
104                     if (type_annotAttrVis == null) {
105                         type_annotAttrVis = new DataVectorAttr(cls,
106                                 AttrTag.ATT_RuntimeVisibleTypeAnnotations.parsekey());
107                     }
108                     type_annotAttrVis.add(ta);
109                 }
110             }
111         }
112     }
113 
114     /* -------------------------------------- Traps */
115     Trap trapDecl(int pos, String name) {
116         Trap local;
117         if (trapsHash == null) {
118             trapsHash = new HashMap<>(10);
119             local = null;
120         } else {
121             local = trapsHash.get(name);
122         }
123         if (local == null) {
124             local = new Trap(pos, name);
125             trapsHash.put(name, local);
126         }
127         return local;
128     }
129 
130     void beginTrap(int pos, String name) {
131         Trap trap = trapDecl(pos, name);
132         if (trap.start_pc != Argument.NotSet) {
133             env.error("trap.tryredecl", name);
134             return;
135         }
136         trap.start_pc = cur_pc;
137     }
138 
139     void endTrap(int pos, String name) {
140         Trap trap = trapDecl(pos, name);
141         if (trap.end_pc != Argument.NotSet) {
142             env.error("trap.endtryredecl", name);
143             return;
144         }
145         trap.end_pc = cur_pc;
146     }
147 
148     void trapHandler(int pos, String name, Argument type) {
149         Trap trap = trapDecl(pos, name);
150         trap.refd = true;
151         TrapData trapData = new TrapData(pos, trap, cur_pc, type);
152         trap_table.addElement(trapData);
153     }
154 
155     void checkTraps() {
156         if (trapsHash == null) {
157             return;
158         }
159         for (Trap trap : trapsHash.values()) {
160             if (!trap.refd) {
161                 env.error(trap.pos, "warn.trap.notref", trap.name);
162             }
163         }
164 
165         for (TrapData trapData : trap_table) {
166             Trap trapLabel = trapData.trap;
167             if (trapLabel.start_pc == Argument.NotSet) {
168                 env.error(trapData.pos, "trap.notry", trapLabel.name);
169             }
170             if (trapLabel.end_pc == Argument.NotSet) {
171                 env.error(trapData.pos, "trap.noendtry", trapLabel.name);
172             }
173         }
174     }
175 
176     /* -------------------------------------- Labels */
177     Label labelDecl(String name) {
178         Label local;
179         if (labelsHash == null) {
180             labelsHash = new HashMap<>(10);
181             local = null;
182         } else {
183             local = labelsHash.get(name);
184         }
185         if (local == null) {
186             local = new Label(name);
187             labelsHash.put(name, local);
188         }
189         return local;
190     }
191 
192     public Label LabelDef(int pos, String name) {
193         Label label = labelDecl(name);
194         if (label.defd) {
195             env.error(pos, "label.redecl", name);
196             return null;
197         }
198         label.defd = true;
199         label.arg = cur_pc;
200         return label;
201     }
202 
203     public Label LabelRef(String name) {
204         Label label = labelDecl(name);
205         label.refd = true;
206         return label;
207     }
208 
209     void checkLabels() {
210         if (labelsHash == null) {
211             return;
212         }
213 
214         for (Label local : labelsHash.values()) {
215             // check that every label is defined
216             if (!local.defd) {
217                 env.error("label.undecl", local.name);
218             }
219         }
220     }
221 
222     /* -------------------------------------- Variables */
223     LocVarData locvarDecl(String name) {
224         LocVarData local;
225         if (locvarsHash == null) {
226             locvarsHash = new HashMap<>(10);
227             local = null;
228         } else {
229             local = locvarsHash.get(name);
230         }
231         if (local == null) {
232             local = new LocVarData(name);
233             locvarsHash.put(name, local);
234         }
235         return local;
236     }
237 
238     public void LocVarDataDef(int slot) {
239         slots.set(slot, 1);
240         if ((max_locals != null) && (max_locals.arg < slots.size())) {
241             env.error("warn.illslot", Integer.toString(slot));
242         }
243     }
244 
245     public void LocVarDataDef(String name, ConstantPool.ConstCell type) {
246         LocVarData locvar = locvarDecl(name);
247         if (locvar.defd) {
248             env.error("locvar.redecl", name);
249             return;
250         }
251         locvar.defd = true;
252         locvar.start_pc = (short) cur_pc;
253         locvar.name_cpx = cls.pool.FindCellAsciz(name);
254         locvar.sig_cpx = type;
255         int k;
256         findSlot:
257         {
258             for (k = 0; k < slots.size(); k++) {
259                 if (slots.get(k) == 0) {
260                     break findSlot;
261                 }
262             }
263             k = slots.size();
264         }
265         LocVarDataDef(k);
266         locvar.arg = k;
267         if (loc_var_tb == null) {
268             loc_var_tb = new DataVectorAttr<>(cls, AttrTag.ATT_LocalVariableTable.parsekey());
269             attrs.add(loc_var_tb);
270         }
271         loc_var_tb.add(locvar);
272     }
273 
274     public Argument LocVarDataRef(String name) {
275         LocVarData locvar = locvarDecl(name);
276         if (!locvar.defd) {
277             env.error("locvar.undecl", name);
278             locvar.defd = true; // to avoid multiple error messages
279         }
280         locvar.refd = true;
281         return locvar;
282     }
283 
284     public void LocVarDataEnd(int slot) {
285         slots.set(slot, 0);
286     }
287 
288     public void LocVarDataEnd(String name) {
289         LocVarData locvar = locvarsHash.get(name);
290         if (locvar == null) {
291             env.error("locvar.undecl", name);
292             return;
293         } else if (!locvar.defd) {
294             env.error("locvar.undecl", name);
295             return;
296         }
297         locvar.length = (short) (cur_pc - locvar.start_pc);
298 
299         slots.set(locvar.arg, 0);
300         locvarsHash.put(name, new LocVarData(name));
301     }
302 
303     void checkLocVars() {
304         if (locvarsHash == null) {
305             return;
306         }
307         for (LocVarData locvar : locvarsHash.values()) {
308             if (!locvar.defd) {
309                 continue;
310             } // this is false locvar
311             // set end of scope, if not set
312             if (slots.get(locvar.arg) == 1) {
313                 locvar.length = (short) (cur_pc - locvar.start_pc);
314                 slots.set(locvar.arg, 0);
315             }
316         }
317     }
318 
319     /* -------------------------------------- StackMap */
320     public StackMapData getStackMap() {
321         if (curMapEntry == null) {
322             curMapEntry = new StackMapData(env);
323             if (cls.cfv.major_version() >= SPLIT_VERIFIER_CFV) {
324                 curMapEntry.setIsStackMapTable(true);
325             }
326         }
327         return curMapEntry;
328     }
329 
330     /*====================================================== Instr */
331     void addInstr(int mnenoc_pos, Opcode opcode, Argument arg, Object arg2) {
332         Instr newInstr = new Instr(cur_pc, cls.env.pos, opcode, arg, arg2);
333         lastInstr.next = newInstr;
334         lastInstr = newInstr;
335         int len = opcode.length();
336         switch (opcode) {
337             case opc_tableswitch:
338                 len = ((SwitchTable) arg2).recalcTableSwitch(cur_pc);
339                 break;
340             case opc_lookupswitch:
341                 len = ((SwitchTable) arg2).calcLookupSwitch(cur_pc);
342                 break;
343             case opc_ldc:
344                 ((ConstantPool.ConstCell) arg).setRank(ConstantPool.ReferenceRank.LDC);
345                 break;
346             default:
347                 if (arg instanceof ConstantPool.ConstCell) {
348                     ((ConstantPool.ConstCell) arg).setRank(ConstantPool.ReferenceRank.ANY);
349                 }
350         }
351         if (env.debugInfoFlag) {
352             int ln = env.lineNumber(mnenoc_pos);
353             if (ln != lastln) { // only one entry in lin_num_tb per line
354                 lin_num_tb.add(new LineNumData(cur_pc, ln));
355                 lastln = ln;
356             }
357         }
358         if (curMapEntry != null) {
359             curMapEntry.pc = cur_pc;
360             StackMapData prevStackFrame = null;
361             if (stackMap == null) {
362                 if (cls.cfv.major_version() >= SPLIT_VERIFIER_CFV) {
363                     stackMap = new DataVectorAttr<>(cls, AttrTag.ATT_StackMapTable.parsekey());
364                 } else {
365                     stackMap = new DataVectorAttr<>(cls, AttrTag.ATT_StackMap.parsekey());
366                 }
367                 attrs.add(stackMap);
368             } else if (stackMap.size() > 0) {
369                 prevStackFrame = stackMap.get(stackMap.size() - 1);
370             }
371             curMapEntry.setOffset(prevStackFrame);
372             stackMap.add(curMapEntry);
373             curMapEntry = null;
374         }
375 
376         cur_pc += len;
377     }
378 
379     /*====================================================== Attr interface */
380     // subclasses must redefine this
381     @Override
382     public int attrLength() {
383         return 2 + 2 + 4 // for max_stack, max_locals, and cur_pc
384                 + cur_pc //      + 2+trap_table.size()*8
385                 + trap_table.getLength() + attrs.getLength();
386     }
387 
388     @Override
389     public void write(CheckedDataOutputStream out)
390             throws IOException, Parser.CompilerError {
391         int mxstck = (max_stack != null) ? max_stack.arg : 0;
392         int mxloc = (max_locals != null) ? max_locals.arg : slots.size();
393         super.write(out);  // attr name, attr len
394         out.writeShort(mxstck);
395         out.writeShort(mxloc);
396         out.writeInt(cur_pc);
397         for (Instr instr = zeroInstr.next; instr != null; instr = instr.next) {
398             instr.write(out, env);
399         }
400 
401         trap_table.write(out);
402 
403         attrs.write(out);
404     }
405 
406     /*-------------------------------------------------------- */
407     /* CodeAttr inner classes */
408     static public class Local extends Argument {
409 
410         String name;
411         boolean defd = false, refd = false;
412 
413         public Local(String name) {
414             this.name = name;
415         }
416     }
417 
418     /**
419      *
420      */
421     static public class Label extends Local {
422 
423         public Label(String name) {
424             super(name);
425         }
426     }
427 
428     /**
429      *
430      */
431     class LocVarData extends Local implements Data {
432 
433         // arg means slot
434         short start_pc, length;
435         ConstantPool.ConstCell name_cpx, sig_cpx;
436 
437         public LocVarData(String name) {
438             super(name);
439         }
440 
441         @Override
442         public int getLength() {
443             return 10;
444         }
445 
446         @Override
447         public void write(CheckedDataOutputStream out) throws IOException {
448             out.writeShort(start_pc);
449             out.writeShort(length);
450             out.writeShort(name_cpx.arg);
451             out.writeShort(sig_cpx.arg);
452             out.writeShort(arg);
453         }
454     }
455 
456     /**
457      *
458      */
459     class LineNumData implements Data {
460 
461         int start_pc, line_number;
462 
463         public LineNumData(int start_pc, int line_number) {
464             this.start_pc = start_pc;
465             this.line_number = line_number;
466         }
467 
468         @Override
469         public int getLength() {
470             return 4;
471         }
472 
473         @Override
474         public void write(CheckedDataOutputStream out) throws IOException {
475             out.writeShort(start_pc);
476             out.writeShort(line_number);
477         }
478     }
479 
480     /**
481      *
482      */
483     class Trap extends Local {
484 
485         int start_pc = Argument.NotSet, end_pc = Argument.NotSet;
486         int pos;
487 
488         Trap(int pos, String name) {
489             super(name);
490             this.pos = pos;
491         }
492     }
493 
494     /**
495      *
496      */
497     class TrapData implements Data {
498 
499         int pos;
500         Trap trap;
501         int handler_pc;
502         Argument catchType;
503 
504         public TrapData(int pos, Trap trap, int handler_pc, Argument catchType) {
505             this.pos = pos;
506             this.trap = trap;
507             this.handler_pc = handler_pc;
508             this.catchType = catchType;
509         }
510 
511         @Override
512         public int getLength() {
513             return 8; // add the length of number of elements
514         }
515 
516         @Override
517         public void write(CheckedDataOutputStream out) throws IOException {
518             out.writeShort(trap.start_pc);
519             out.writeShort(trap.end_pc);
520             out.writeShort(handler_pc);
521             if (catchType.isSet()) {
522                 out.writeShort(catchType.arg);
523             } else {
524                 out.writeShort(0);
525             }
526         }
527     }  // end TrapData
528 } // end CodeAttr
529