IKVM11  11
Java SE 11 Virtual Machine for .NET
Loading...
Searching...
No Matches
ClassFile.Method.Code.cs
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2015 Jeroen Frijters
3
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any damages
6 arising from the use of this software.
7
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it
10 freely, subject to the following restrictions:
11
12 1. The origin of this software must not be misrepresented; you must not
13 claim that you wrote the original software. If you use this software
14 in a product, an acknowledgment in the product documentation would be
15 appreciated but is not required.
16 2. Altered source versions must be plainly marked as such, and must not be
17 misrepresented as being the original software.
18 3. This notice may not be removed or altered from any source distribution.
19
20 Jeroen Frijters
21 jeroen@frijters.net
22
23*/
24using System;
25using System.Buffers;
26using System.Collections.Generic;
27
28using IKVM.ByteCode;
29using IKVM.ByteCode.Decoding;
30
31namespace IKVM.Runtime
32{
33
34 sealed partial class ClassFile
35 {
36
37 internal sealed partial class Method
38 {
39
40 struct Code
41 {
42
43 internal bool hasJsr;
44 internal string verifyError;
45 internal ushort max_stack;
46 internal ushort max_locals;
47 internal Instruction[] instructions;
48 internal ExceptionTableEntry[] exception_table;
49 internal int[] argmap;
50 internal LineNumberTableEntry[] lineNumberTable;
51 internal LocalVariableTableEntry[] localVariableTable;
52
62 internal void Read(ClassFile classFile, string[] utf8_cp, Method method, CodeAttribute attribute, ClassFileParseOptions options)
63 {
64 max_stack = attribute.MaxStack;
65 max_locals = attribute.MaxLocals;
66 if (attribute.Code.Length == 0 || attribute.Code.Length > 65535)
67 throw new ClassFormatError("Invalid method Code length {1} in class file {0}", classFile.Name, attribute.Code.Length);
68
69 // we don't know how many instructions we will read until we read them, so first parse them into a temporary array
70 var _instructions = ArrayPool<Instruction>.Shared.Rent((int)attribute.Code.Length + 1);
71
72 try
73 {
74 int instructionCount = 0;
75
76 try
77 {
78 var decoder = new CodeDecoder(attribute.Code);
79 var lastIns = default(IKVM.ByteCode.Decoding.Instruction);
80
81 // read instructions and parse them into local structure
82 foreach (var instruction in decoder)
83 {
84 lastIns = instruction;
85
86 _instructions[instructionCount].Read(instruction, classFile);
87 hasJsr |= _instructions[instructionCount].NormalizedOpCode == NormalizedByteCode.__jsr;
88 instructionCount++;
89 }
90
91 // we add an additional nop instruction to make it easier for consumers of the code array
92 _instructions[instructionCount++].SetTermNop((ushort)(lastIns.IsNotNil ? lastIns.Offset + lastIns.Data.Length : 0));
93 }
94 catch (ArgumentOutOfRangeException e)
95 {
96 verifyError = e.Message;
97 }
98 catch (ClassFormatError e)
99 {
100 verifyError = e.Message;
101 }
102 catch (ByteCodeException e)
103 {
104 verifyError = e.Message;
105 }
106
107 // copy from temporary array into properly sized array
108 instructions = new Instruction[instructionCount];
109 Array.Copy(_instructions, 0, instructions, 0, instructionCount);
110
111 // build the pcIndexMap
112 var pcIndexMap = new int[instructions[instructionCount - 1].PC + 1];
113 pcIndexMap.AsSpan().Fill(-1);
114 for (int i = 0; i < instructionCount - 1; i++)
115 pcIndexMap[instructions[i].PC] = i;
116
117 // convert branch offsets to indexes
118 for (int i = 0; i < instructionCount - 1; i++)
119 {
120 switch (instructions[i].NormalizedOpCode)
121 {
122 case NormalizedByteCode.__ifeq:
123 case NormalizedByteCode.__ifne:
124 case NormalizedByteCode.__iflt:
125 case NormalizedByteCode.__ifge:
126 case NormalizedByteCode.__ifgt:
127 case NormalizedByteCode.__ifle:
128 case NormalizedByteCode.__if_icmpeq:
129 case NormalizedByteCode.__if_icmpne:
130 case NormalizedByteCode.__if_icmplt:
131 case NormalizedByteCode.__if_icmpge:
132 case NormalizedByteCode.__if_icmpgt:
133 case NormalizedByteCode.__if_icmple:
134 case NormalizedByteCode.__if_acmpeq:
135 case NormalizedByteCode.__if_acmpne:
136 case NormalizedByteCode.__ifnull:
137 case NormalizedByteCode.__ifnonnull:
138 case NormalizedByteCode.__goto:
139 case NormalizedByteCode.__jsr:
140 this.instructions[i].SetTargetIndex(pcIndexMap[this.instructions[i].Arg1 + this.instructions[i].PC]);
141 break;
142 case NormalizedByteCode.__tableswitch:
143 case NormalizedByteCode.__lookupswitch:
144 this.instructions[i].MapSwitchTargets(pcIndexMap);
145 break;
146 }
147 }
148
149 // read exception table
150 exception_table = new ExceptionTableEntry[attribute.ExceptionTable.Count];
151 for (int i = 0; i < attribute.ExceptionTable.Count; i++)
152 {
153 var handler = attribute.ExceptionTable[i];
154 var start_pc = handler.StartOffset;
155 var end_pc = handler.EndOffset;
156 var handler_pc = handler.HandlerOffset;
157 var catch_type = handler.CatchType;
158
159 if (start_pc >= end_pc || end_pc > attribute.Code.Length || handler_pc >= attribute.Code.Length || (catch_type.IsNotNil && !classFile.SafeIsConstantPoolClass(catch_type)))
160 throw new ClassFormatError("Illegal exception table: {0}.{1}{2}", classFile.Name, method.Name, method.Signature);
161
162 classFile.MarkLinkRequiredConstantPoolItem(catch_type);
163
164 // if start_pc, end_pc or handler_pc is invalid (i.e. doesn't point to the start of an instruction),
165 // the index will be -1 and this will be handled by the verifier
166 var startIndex = pcIndexMap[start_pc];
167 var endIndex = 0;
168 if (end_pc == attribute.Code.Length)
169 {
170 // it is legal for end_pc to point to just after the last instruction,
171 // but since there isn't an entry in our pcIndexMap for that, we have
172 // a special case for this
173 endIndex = instructionCount - 1;
174 }
175 else
176 {
177 endIndex = pcIndexMap[end_pc];
178 }
179
180 var handlerIndex = pcIndexMap[handler_pc];
181 exception_table[i] = new ExceptionTableEntry(startIndex, endIndex, handlerIndex, catch_type, i);
182 }
183
184 foreach (var _attribute in attribute.Attributes)
185 {
186 switch (classFile.GetConstantPoolUtf8String(utf8_cp, _attribute.Name))
187 {
188 case AttributeName.LineNumberTable:
189 var lnt = (IKVM.ByteCode.Decoding.LineNumberTableAttribute)_attribute;
190 if ((options & ClassFileParseOptions.LineNumberTable) != 0)
191 {
192 lineNumberTable = new LineNumberTableEntry[lnt.LineNumbers.Count];
193 for (int j = 0; j < lnt.LineNumbers.Count; j++)
194 {
195 var item = lnt.LineNumbers[j];
196 lineNumberTable[j].start_pc = item.StartPc;
197 lineNumberTable[j].line_number = item.LineNumber;
198 if (lineNumberTable[j].start_pc >= attribute.Code.Length)
199 throw new ClassFormatError("{0} (LineNumberTable has invalid pc)", classFile.Name);
200 }
201 }
202 break;
203 case AttributeName.LocalVariableTable:
204 var lvt = (IKVM.ByteCode.Decoding.LocalVariableTableAttribute)_attribute;
205 if ((options & ClassFileParseOptions.LocalVariableTable) != 0)
206 {
207 localVariableTable = new LocalVariableTableEntry[lvt.LocalVariables.Count];
208 for (int j = 0; j < lvt.LocalVariables.Count; j++)
209 {
210 var item = lvt.LocalVariables[j];
211 localVariableTable[j].start_pc = item.StartPc;
212 localVariableTable[j].length = item.Length;
213 localVariableTable[j].name = classFile.GetConstantPoolUtf8String(utf8_cp, item.Name);
214 localVariableTable[j].descriptor = classFile.GetConstantPoolUtf8String(utf8_cp, item.Descriptor).Replace('/', '.');
215 localVariableTable[j].index = item.Slot;
216 }
217 }
218 break;
219 default:
220 break;
221 }
222 }
223
224 // build the argmap
225 var sig = method.Signature;
226 var args = new List<int>();
227 int pos = 0;
228 if (!method.IsStatic)
229 args.Add(pos++);
230
231 for (int i = 1; sig[i] != ')'; i++)
232 {
233 args.Add(pos++);
234 switch (sig[i])
235 {
236 case 'L':
237 i = sig.IndexOf(';', i);
238 break;
239 case 'D':
240 case 'J':
241 args.Add(-1);
242 break;
243 case '[':
244 {
245 while (sig[i] == '[')
246 {
247 i++;
248 }
249 if (sig[i] == 'L')
250 {
251 i = sig.IndexOf(';', i);
252 }
253 break;
254 }
255 }
256 }
257 argmap = args.ToArray();
258
259 if (args.Count > max_locals)
260 throw new ClassFormatError("{0} (Arguments can't fit into locals)", classFile.Name);
261 }
262 catch (InvalidCodeException e)
263 {
264 throw new ClassFormatError(e.Message);
265 }
266 catch (ByteCodeException e)
267 {
268 throw new ClassFormatError(e.Message);
269 }
270 finally
271 {
272 ArrayPool<Instruction>.Shared.Return(_instructions);
273 }
274 }
275
276 internal bool IsEmpty => instructions == null;
277
278 }
279
280 }
281
282 }
283
284}
IKVM.Runtime.ClassFile.Method.Instruction Instruction
Definition compiler.cs:44
IKVM.Runtime.ClassFile.Method.LocalVariableTableEntry LocalVariableTableEntry
Definition compiler.cs:43
IKVM.Runtime.ClassFile.Method.ExceptionTableEntry ExceptionTableEntry
Definition compiler.cs:42