IKVM11  11
Java SE 11 Virtual Machine for .NET
Loading...
Searching...
No Matches
MethodAnalyzer.cs
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2014 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.Collections.Generic;
26using System.Diagnostics;
27using System.Text;
28
29using IKVM.ByteCode;
30
31#if IMPORTER
33#endif
34
36using InstructionFlags = IKVM.Runtime.ClassFile.Method.InstructionFlags;
37
38namespace IKVM.Runtime
39{
40
41 sealed class MethodAnalyzer
42 {
43
44 readonly RuntimeContext _context;
45 readonly RuntimeJavaType _host; // used to by Unsafe.defineAnonymousClass() to provide access to private members of the host
46 readonly RuntimeJavaType _type;
47 readonly RuntimeJavaMethod _method;
48 readonly ClassFile _classFile;
49 readonly ClassFile.Method _classFileMethod;
50 readonly RuntimeClassLoader _classLoader;
51 readonly RuntimeJavaType _thisType;
52 readonly InstructionState[] _state;
53 List<string> _errorMessages;
54 readonly Dictionary<int, RuntimeJavaType> _newTypes = new Dictionary<int, RuntimeJavaType>();
55 readonly Dictionary<int, RuntimeJavaType> _faultTypes = new Dictionary<int, RuntimeJavaType>();
56
62 {
63 _context = context ?? throw new ArgumentNullException(nameof(context));
64 }
65
69 public RuntimeContext Context => _context;
70
83 internal MethodAnalyzer(RuntimeContext context, RuntimeJavaType host, RuntimeJavaType type, RuntimeJavaMethod method, ClassFile classFile, ClassFile.Method classFileMethod, RuntimeClassLoader classLoader) :
84 this(context)
85 {
86 if (classFileMethod.VerifyError != null)
87 throw new VerifyError(classFileMethod.VerifyError);
88
89 _host = host;
90 _type = type;
91 _method = method;
92 _classFile = classFile;
93 _classFileMethod = classFileMethod;
94 _classLoader = classLoader;
95 _state = new InstructionState[classFileMethod.Instructions.Length];
96
97 try
98 {
99 // ensure that exception blocks and handlers start and end at instruction boundaries
100 for (int i = 0; i < classFileMethod.ExceptionTable.Length; i++)
101 {
102 int start = classFileMethod.ExceptionTable[i].startIndex;
103 int end = classFileMethod.ExceptionTable[i].endIndex;
104 int handler = classFileMethod.ExceptionTable[i].handlerIndex;
105 if (start >= end || start == -1 || end == -1 || handler <= 0)
106 throw new IndexOutOfRangeException();
107 }
108 }
109 catch (IndexOutOfRangeException)
110 {
111 // TODO figure out if we should throw this during class loading
112 throw new ClassFormatError($"Illegal exception table (class: {classFile.Name}, method: {classFileMethod.Name}, signature: {classFileMethod.Signature}");
113 }
114
115 // start by computing the initial state, the stack is empty and the locals contain the arguments
116 _state[0] = new InstructionState(context, classFileMethod.MaxLocals, classFileMethod.MaxStack);
117 int firstNonArgLocalIndex = 0;
118
119 if (classFileMethod.IsStatic == false)
120 {
121 _thisType = RuntimeVerifierJavaType.MakeThis(type);
122
123 // this reference. If we're a constructor, the this reference is uninitialized.
124 if (classFileMethod.IsConstructor)
125 {
126 _state[0].SetLocalType(firstNonArgLocalIndex++, context.VerifierJavaTypeFactory.UninitializedThis, -1);
127 _state[0].SetUnitializedThis(true);
128 }
129 else
130 {
131 _state[0].SetLocalType(firstNonArgLocalIndex++, _thisType, -1);
132 }
133 }
134 else
135 {
136 _thisType = null;
137 }
138
139 // mw can be null when we're invoked from IsSideEffectFreeStaticInitializer
140 var argTypes = method != null ? method.GetParameters() : [];
141 for (int i = 0; i < argTypes.Length; i++)
142 {
143 var argType = argTypes[i];
144 if (argType.IsIntOnStackPrimitive)
145 argType = context.PrimitiveJavaTypeFactory.INT;
146
147 _state[0].SetLocalType(firstNonArgLocalIndex++, argType, -1);
148 if (argType.IsWidePrimitive)
149 firstNonArgLocalIndex++;
150 }
151
152 AnalyzeTypeFlow();
153 VerifyPassTwo();
154 PatchLoadConstants();
155 }
156
160 void PatchLoadConstants()
161 {
162 var code = _classFileMethod.Instructions;
163 for (int i = 0; i < code.Length; i++)
164 {
165 if (_state[i]._initialized)
166 {
167 switch (code[i].NormalizedOpCode)
168 {
169 case NormalizedByteCode.__ldc:
170 switch (GetConstantPoolConstantType(code[i].Arg1))
171 {
172 case ClassFile.ConstantType.Double:
173 case ClassFile.ConstantType.Float:
174 case ClassFile.ConstantType.Integer:
175 case ClassFile.ConstantType.Long:
176 case ClassFile.ConstantType.String:
177 case ClassFile.ConstantType.LiveObject:
178 code[i].PatchOpCode(NormalizedByteCode.__ldc_nothrow);
179 break;
180 }
181
182 break;
183 }
184 }
185 }
186 }
187
188 internal CodeInfo GetCodeInfoAndErrors(UntangledExceptionTable exceptions, out List<string> errors)
189 {
190 var codeInfo = new CodeInfo(_context, _state);
191
192 OptimizationPass(codeInfo, _classFile, _classFileMethod, exceptions, _type, _classLoader);
193 PatchHardErrorsAndDynamicMemberAccess(_type, _method);
194 errors = _errorMessages;
195
196 if (AnalyzePotentialFaultBlocks(codeInfo, _classFileMethod, exceptions))
197 AnalyzeTypeFlow();
198
199 ConvertFinallyBlocks(codeInfo, _classFileMethod, exceptions);
200 return codeInfo;
201 }
202
203 void AnalyzeTypeFlow()
204 {
205 var s = new InstructionState(_context, _classFileMethod.MaxLocals, _classFileMethod.MaxStack);
206 var done = false;
207 var instructions = _classFileMethod.Instructions;
208
209 while (done == false)
210 {
211 done = true;
212
213 for (int i = 0; i < instructions.Length; i++)
214 {
215 if (_state[i]._initialized && _state[i]._changed)
216 {
217 try
218 {
219 // we encountered a state that is marked as changed, so we will need a next loop
220 done = false;
221 _state[i]._changed = false;
222
223 // mark the exception handlers reachable from this instruction
224 for (int j = 0; j < _classFileMethod.ExceptionTable.Length; j++)
225 if (_classFileMethod.ExceptionTable[j].startIndex <= i && i < _classFileMethod.ExceptionTable[j].endIndex)
226 MergeExceptionHandler(j, ref _state[i]);
227
228 // copy current frame to this frame
229 _state[i].CopyTo(ref s);
230
231 var inst = instructions[i];
232 switch (inst.NormalizedOpCode)
233 {
234 case NormalizedByteCode.__aload:
235 {
236 var type = s.GetLocalType(inst.NormalizedArg1);
237 if (type == _context.VerifierJavaTypeFactory.Invalid || type.IsPrimitive)
238 throw new VerifyError("Object reference expected");
239
240 s.PushType(type);
241 break;
242 }
243 case NormalizedByteCode.__astore:
244 {
245 if (RuntimeVerifierJavaType.IsFaultBlockException(s.PeekType()))
246 {
247 s.SetLocalType(inst.NormalizedArg1, s.PopFaultBlockException(), i);
248 break;
249 }
250
251 // NOTE since the reference can be uninitialized, we cannot use PopObjectType
252 var type = s.PopType();
253 if (type.IsPrimitive)
254 throw new VerifyError("Object reference expected");
255
256 s.SetLocalType(inst.NormalizedArg1, type, i);
257 break;
258 }
259 case NormalizedByteCode.__aconst_null:
260 s.PushType(_context.VerifierJavaTypeFactory.Null);
261 break;
262 case NormalizedByteCode.__aaload:
263 {
264 s.PopInt();
265 var type = s.PopArrayType();
266 if (type == _context.VerifierJavaTypeFactory.Null)
267 {
268 // if the array is null, we have use null as the element type, because
269 // otherwise the rest of the code will not verify correctly
270 s.PushType(_context.VerifierJavaTypeFactory.Null);
271 }
272 else if (type.IsUnloadable)
273 {
274 s.PushType(_context.VerifierJavaTypeFactory.Unloadable);
275 }
276 else
277 {
278 type = type.ElementTypeWrapper;
279 if (type.IsPrimitive)
280 throw new VerifyError("Object array expected");
281
282 s.PushType(type);
283 }
284 break;
285 }
286 case NormalizedByteCode.__aastore:
287 s.PopObjectType();
288 s.PopInt();
289 s.PopArrayType();
290 // TODO check that elem is assignable to the array
291 break;
292 case NormalizedByteCode.__baload:
293 {
294 s.PopInt();
295
296 var type = s.PopArrayType();
297 if (!RuntimeVerifierJavaType.IsNullOrUnloadable(type) && type != _context.MethodAnalyzerFactory.ByteArrayType && type != _context.MethodAnalyzerFactory.BooleanArrayType)
298 throw new VerifyError();
299
300 s.PushInt();
301 break;
302 }
303 case NormalizedByteCode.__bastore:
304 {
305 s.PopInt();
306 s.PopInt();
307
308 var type = s.PopArrayType();
309 if (!RuntimeVerifierJavaType.IsNullOrUnloadable(type) && type != _context.MethodAnalyzerFactory.ByteArrayType && type != _context.MethodAnalyzerFactory.BooleanArrayType)
310 throw new VerifyError();
311
312 break;
313 }
314 case NormalizedByteCode.__caload:
315 s.PopInt();
316 s.PopObjectType(_context.MethodAnalyzerFactory.CharArrayType);
317 s.PushInt();
318 break;
319 case NormalizedByteCode.__castore:
320 s.PopInt();
321 s.PopInt();
322 s.PopObjectType(_context.MethodAnalyzerFactory.CharArrayType);
323 break;
324 case NormalizedByteCode.__saload:
325 s.PopInt();
326 s.PopObjectType(_context.MethodAnalyzerFactory.ShortArrayType);
327 s.PushInt();
328 break;
329 case NormalizedByteCode.__sastore:
330 s.PopInt();
331 s.PopInt();
332 s.PopObjectType(_context.MethodAnalyzerFactory.ShortArrayType);
333 break;
334 case NormalizedByteCode.__iaload:
335 s.PopInt();
336 s.PopObjectType(_context.MethodAnalyzerFactory.IntArrayType);
337 s.PushInt();
338 break;
339 case NormalizedByteCode.__iastore:
340 s.PopInt();
341 s.PopInt();
342 s.PopObjectType(_context.MethodAnalyzerFactory.IntArrayType);
343 break;
344 case NormalizedByteCode.__laload:
345 s.PopInt();
346 s.PopObjectType(_context.MethodAnalyzerFactory.LongArrayType);
347 s.PushLong();
348 break;
349 case NormalizedByteCode.__lastore:
350 s.PopLong();
351 s.PopInt();
352 s.PopObjectType(_context.MethodAnalyzerFactory.LongArrayType);
353 break;
354 case NormalizedByteCode.__daload:
355 s.PopInt();
356 s.PopObjectType(_context.MethodAnalyzerFactory.DoubleArrayType);
357 s.PushDouble();
358 break;
359 case NormalizedByteCode.__dastore:
360 s.PopDouble();
361 s.PopInt();
362 s.PopObjectType(_context.MethodAnalyzerFactory.DoubleArrayType);
363 break;
364 case NormalizedByteCode.__faload:
365 s.PopInt();
366 s.PopObjectType(_context.MethodAnalyzerFactory.FloatArrayType);
367 s.PushFloat();
368 break;
369 case NormalizedByteCode.__fastore:
370 s.PopFloat();
371 s.PopInt();
372 s.PopObjectType(_context.MethodAnalyzerFactory.FloatArrayType);
373 break;
374 case NormalizedByteCode.__arraylength:
375 s.PopArrayType();
376 s.PushInt();
377 break;
378 case NormalizedByteCode.__iconst:
379 s.PushInt();
380 break;
381 case NormalizedByteCode.__if_icmpeq:
382 case NormalizedByteCode.__if_icmpne:
383 case NormalizedByteCode.__if_icmplt:
384 case NormalizedByteCode.__if_icmpge:
385 case NormalizedByteCode.__if_icmpgt:
386 case NormalizedByteCode.__if_icmple:
387 s.PopInt();
388 s.PopInt();
389 break;
390 case NormalizedByteCode.__ifeq:
391 case NormalizedByteCode.__ifge:
392 case NormalizedByteCode.__ifgt:
393 case NormalizedByteCode.__ifle:
394 case NormalizedByteCode.__iflt:
395 case NormalizedByteCode.__ifne:
396 s.PopInt();
397 break;
398 case NormalizedByteCode.__ifnonnull:
399 case NormalizedByteCode.__ifnull:
400 // TODO it might be legal to use an unitialized ref here
401 s.PopObjectType();
402 break;
403 case NormalizedByteCode.__if_acmpeq:
404 case NormalizedByteCode.__if_acmpne:
405 // TODO it might be legal to use an unitialized ref here
406 s.PopObjectType();
407 s.PopObjectType();
408 break;
409 case NormalizedByteCode.__getstatic:
410 case NormalizedByteCode.__dynamic_getstatic:
411 // special support for when we're being called from IsSideEffectFreeStaticInitializer
412 if (_method == null)
413 {
414 switch (GetFieldref(inst.Arg1).Signature[0])
415 {
416 case 'B':
417 case 'Z':
418 case 'C':
419 case 'S':
420 case 'I':
421 s.PushInt();
422 break;
423 case 'F':
424 s.PushFloat();
425 break;
426 case 'D':
427 s.PushDouble();
428 break;
429 case 'J':
430 s.PushLong();
431 break;
432 case 'L':
433 case '[':
434 throw new VerifyError();
435 default:
436 throw new InvalidOperationException();
437 }
438 }
439 else
440 {
441 var cpi = GetFieldref(inst.Arg1);
442 if (cpi.GetField() != null && cpi.GetField().FieldTypeWrapper.IsUnloadable)
443 s.PushType(cpi.GetField().FieldTypeWrapper);
444 else
445 s.PushType(cpi.GetFieldType());
446 }
447 break;
448 case NormalizedByteCode.__putstatic:
449 case NormalizedByteCode.__dynamic_putstatic:
450 // special support for when we're being called from IsSideEffectFreeStaticInitializer
451 if (_method == null)
452 {
453 switch (GetFieldref(inst.Arg1).Signature[0])
454 {
455 case 'B':
456 case 'Z':
457 case 'C':
458 case 'S':
459 case 'I':
460 s.PopInt();
461 break;
462 case 'F':
463 s.PopFloat();
464 break;
465 case 'D':
466 s.PopDouble();
467 break;
468 case 'J':
469 s.PopLong();
470 break;
471 case 'L':
472 case '[':
473 if (s.PopAnyType() != _context.VerifierJavaTypeFactory.Null)
474 throw new VerifyError();
475 break;
476 default:
477 throw new InvalidOperationException();
478 }
479 }
480 else
481 {
482 s.PopType(GetFieldref(inst.Arg1).GetFieldType());
483 }
484 break;
485 case NormalizedByteCode.__getfield:
486 case NormalizedByteCode.__dynamic_getfield:
487 {
488 s.PopObjectType(GetFieldref(inst.Arg1).GetClassType());
489
490 var cpi = GetFieldref(inst.Arg1);
491 if (cpi.GetField() != null && cpi.GetField().FieldTypeWrapper.IsUnloadable)
492 s.PushType(cpi.GetField().FieldTypeWrapper);
493 else
494 s.PushType(cpi.GetFieldType());
495
496 break;
497 }
498 case NormalizedByteCode.__putfield:
499 case NormalizedByteCode.__dynamic_putfield:
500 s.PopType(GetFieldref(inst.Arg1).GetFieldType());
501
502 // putfield is allowed to access the uninitialized this
503 if (s.PeekType() == _context.VerifierJavaTypeFactory.UninitializedThis && _type.IsAssignableTo(GetFieldref(inst.Arg1).GetClassType()))
504 s.PopType();
505 else
506 s.PopObjectType(GetFieldref(inst.Arg1).GetClassType());
507
508 break;
509 case NormalizedByteCode.__ldc_nothrow:
510 case NormalizedByteCode.__ldc:
511 {
512 switch (GetConstantPoolConstantType(inst.Arg1))
513 {
514 case ClassFile.ConstantType.Double:
515 s.PushDouble();
516 break;
517 case ClassFile.ConstantType.Float:
518 s.PushFloat();
519 break;
520 case ClassFile.ConstantType.Integer:
521 s.PushInt();
522 break;
523 case ClassFile.ConstantType.Long:
524 s.PushLong();
525 break;
526 case ClassFile.ConstantType.String:
527 s.PushType(_context.JavaBase.TypeOfJavaLangString);
528 break;
529 case ClassFile.ConstantType.LiveObject:
530 s.PushType(_context.JavaBase.TypeOfJavaLangObject);
531 break;
532 case ClassFile.ConstantType.Class:
533 if (_classFile.MajorVersion < 49)
534 throw new VerifyError("Illegal type in constant pool");
535
536 s.PushType(_context.JavaBase.TypeOfJavaLangClass);
537 break;
538 case ClassFile.ConstantType.MethodHandle:
539 s.PushType(_context.JavaBase.TypeOfJavaLangInvokeMethodHandle);
540 break;
541 case ClassFile.ConstantType.MethodType:
542 s.PushType(_context.JavaBase.TypeOfJavaLangInvokeMethodType);
543 break;
544 default:
545 // NOTE this is not a VerifyError, because it cannot happen (unless we have
546 // a bug in ClassFile.GetConstantPoolConstantType)
547 throw new InvalidOperationException();
548 }
549
550 break;
551 }
552 case NormalizedByteCode.__clone_array:
553 case NormalizedByteCode.__invokevirtual:
554 case NormalizedByteCode.__invokespecial:
555 case NormalizedByteCode.__invokeinterface:
556 case NormalizedByteCode.__invokestatic:
557 case NormalizedByteCode.__dynamic_invokevirtual:
558 case NormalizedByteCode.__dynamic_invokespecial:
559 case NormalizedByteCode.__dynamic_invokeinterface:
560 case NormalizedByteCode.__dynamic_invokestatic:
561 case NormalizedByteCode.__privileged_invokevirtual:
562 case NormalizedByteCode.__privileged_invokespecial:
563 case NormalizedByteCode.__privileged_invokestatic:
564 case NormalizedByteCode.__methodhandle_invoke:
565 case NormalizedByteCode.__methodhandle_link:
566 {
567 var cpi = GetMethodref(inst.Arg1);
568 var retType = cpi.GetRetType();
569
570 // HACK to allow the result of Unsafe.getObjectVolatile() (on an array)
571 // to be used with Unsafe.putObject() we need to propagate the
572 // element type here as the return type (instead of object)
573 if (cpi.GetMethod() != null && cpi.GetMethod().IsIntrinsic && cpi.Class == "sun.misc.Unsafe" && cpi.Name == "getObjectVolatile" && Intrinsics.IsSupportedArrayTypeForUnsafeOperation(s.GetStackSlot(1)))
574 retType = s.GetStackSlot(1).ElementTypeWrapper;
575
576 s.MultiPopAnyType(cpi.GetArgTypes().Length);
577
578 if (inst.NormalizedOpCode != NormalizedByteCode.__invokestatic && inst.NormalizedOpCode != NormalizedByteCode.__dynamic_invokestatic)
579 {
580 var type = s.PopType();
581 if (ReferenceEquals(cpi.Name, StringConstants.INIT))
582 {
583 // after we've invoked the constructor, the uninitialized references
584 // are now initialized
585 if (type == _context.VerifierJavaTypeFactory.UninitializedThis)
586 {
587 if (s.GetLocalTypeEx(0) == type)
588 s.SetLocalType(0, _thisType, i);
589
590 s.MarkInitialized(type, _type, i);
591 s.SetUnitializedThis(false);
592 }
593 else if (RuntimeVerifierJavaType.IsNew(type))
594 {
595 s.MarkInitialized(type, ((RuntimeVerifierJavaType)type).UnderlyingType, i);
596 }
597 else
598 {
599 // This is a VerifyError, but it will be caught by our second pass
600 }
601 }
602 }
603
604 if (retType != _context.PrimitiveJavaTypeFactory.VOID)
605 {
606 if (cpi.GetMethod() != null && cpi.GetMethod().ReturnType.IsUnloadable)
607 {
608 s.PushType(cpi.GetMethod().ReturnType);
609 }
610 else if (retType == _context.PrimitiveJavaTypeFactory.DOUBLE)
611 {
612 s.PushExtendedDouble();
613 }
614 else if (retType == _context.PrimitiveJavaTypeFactory.FLOAT)
615 {
616 s.PushExtendedFloat();
617 }
618 else
619 {
620 s.PushType(retType);
621 }
622 }
623
624 break;
625 }
626 case NormalizedByteCode.__invokedynamic:
627 {
628 var cpi = GetInvokeDynamic(inst.Arg1);
629 s.MultiPopAnyType(cpi.GetArgTypes().Length);
630
631 var retType = cpi.GetRetType();
632 if (retType != _context.PrimitiveJavaTypeFactory.VOID)
633 {
634 if (retType == _context.PrimitiveJavaTypeFactory.DOUBLE)
635 {
636 s.PushExtendedDouble();
637 }
638 else if (retType == _context.PrimitiveJavaTypeFactory.FLOAT)
639 {
640 s.PushExtendedFloat();
641 }
642 else
643 {
644 s.PushType(retType);
645 }
646 }
647
648 break;
649 }
650 case NormalizedByteCode.__goto:
651 break;
652 case NormalizedByteCode.__istore:
653 s.PopInt();
654 s.SetLocalInt(inst.NormalizedArg1, i);
655 break;
656 case NormalizedByteCode.__iload:
657 s.GetLocalInt(inst.NormalizedArg1);
658 s.PushInt();
659 break;
660 case NormalizedByteCode.__ineg:
661 s.PopInt();
662 s.PushInt();
663 break;
664 case NormalizedByteCode.__iadd:
665 case NormalizedByteCode.__isub:
666 case NormalizedByteCode.__imul:
667 case NormalizedByteCode.__idiv:
668 case NormalizedByteCode.__irem:
669 case NormalizedByteCode.__iand:
670 case NormalizedByteCode.__ior:
671 case NormalizedByteCode.__ixor:
672 case NormalizedByteCode.__ishl:
673 case NormalizedByteCode.__ishr:
674 case NormalizedByteCode.__iushr:
675 s.PopInt();
676 s.PopInt();
677 s.PushInt();
678 break;
679 case NormalizedByteCode.__lneg:
680 s.PopLong();
681 s.PushLong();
682 break;
683 case NormalizedByteCode.__ladd:
684 case NormalizedByteCode.__lsub:
685 case NormalizedByteCode.__lmul:
686 case NormalizedByteCode.__ldiv:
687 case NormalizedByteCode.__lrem:
688 case NormalizedByteCode.__land:
689 case NormalizedByteCode.__lor:
690 case NormalizedByteCode.__lxor:
691 s.PopLong();
692 s.PopLong();
693 s.PushLong();
694 break;
695 case NormalizedByteCode.__lshl:
696 case NormalizedByteCode.__lshr:
697 case NormalizedByteCode.__lushr:
698 s.PopInt();
699 s.PopLong();
700 s.PushLong();
701 break;
702 case NormalizedByteCode.__fneg:
703 if (s.PopFloat())
704 s.PushExtendedFloat();
705 else
706 s.PushFloat();
707 break;
708 case NormalizedByteCode.__fadd:
709 case NormalizedByteCode.__fsub:
710 case NormalizedByteCode.__fmul:
711 case NormalizedByteCode.__fdiv:
712 case NormalizedByteCode.__frem:
713 s.PopFloat();
714 s.PopFloat();
715 s.PushExtendedFloat();
716 break;
717 case NormalizedByteCode.__dneg:
718 if (s.PopDouble())
719 s.PushExtendedDouble();
720 else
721 s.PushDouble();
722
723 break;
724 case NormalizedByteCode.__dadd:
725 case NormalizedByteCode.__dsub:
726 case NormalizedByteCode.__dmul:
727 case NormalizedByteCode.__ddiv:
728 case NormalizedByteCode.__drem:
729 s.PopDouble();
730 s.PopDouble();
731 s.PushExtendedDouble();
732 break;
733 case NormalizedByteCode.__new:
734 {
735 // mark the type, so that we can ascertain that it is a "new object"
736 if (!_newTypes.TryGetValue(i, out var type))
737 {
738 type = GetConstantPoolClassType(inst.Arg1);
739 if (type.IsArray)
740 throw new VerifyError("Illegal use of array type");
741
742 type = RuntimeVerifierJavaType.MakeNew(type, i);
743 _newTypes[i] = type;
744 }
745
746 s.PushType(type);
747 break;
748 }
749 case NormalizedByteCode.__multianewarray:
750 {
751 if (inst.Arg2 < 1)
752 throw new VerifyError("Illegal dimension argument");
753
754 for (int j = 0; j < inst.Arg2; j++)
755 s.PopInt();
756
757 var type = GetConstantPoolClassType(inst.Arg1);
758 if (type.ArrayRank < inst.Arg2)
759 throw new VerifyError("Illegal dimension argument");
760
761 s.PushType(type);
762 break;
763 }
764 case NormalizedByteCode.__anewarray:
765 {
766 s.PopInt();
767 var type = GetConstantPoolClassType(inst.Arg1);
768 if (type.IsUnloadable)
769 s.PushType(new RuntimeUnloadableJavaType(_context, "[" + type.SigName));
770 else
771 s.PushType(type.MakeArrayType(1));
772
773 break;
774 }
775 case NormalizedByteCode.__newarray:
776 s.PopInt();
777 switch (inst.Arg1)
778 {
779 case 4:
780 s.PushType(_context.MethodAnalyzerFactory.BooleanArrayType);
781 break;
782 case 5:
783 s.PushType(_context.MethodAnalyzerFactory.CharArrayType);
784 break;
785 case 6:
786 s.PushType(_context.MethodAnalyzerFactory.FloatArrayType);
787 break;
788 case 7:
789 s.PushType(_context.MethodAnalyzerFactory.DoubleArrayType);
790 break;
791 case 8:
792 s.PushType(_context.MethodAnalyzerFactory.ByteArrayType);
793 break;
794 case 9:
795 s.PushType(_context.MethodAnalyzerFactory.ShortArrayType);
796 break;
797 case 10:
798 s.PushType(_context.MethodAnalyzerFactory.IntArrayType);
799 break;
800 case 11:
801 s.PushType(_context.MethodAnalyzerFactory.LongArrayType);
802 break;
803 default:
804 throw new VerifyError("Bad type");
805 }
806 break;
807 case NormalizedByteCode.__swap:
808 {
809 var t1 = s.PopType();
810 var t2 = s.PopType();
811 s.PushType(t1);
812 s.PushType(t2);
813 break;
814 }
815 case NormalizedByteCode.__dup:
816 {
817 var t = s.PopType();
818 s.PushType(t);
819 s.PushType(t);
820 break;
821 }
822 case NormalizedByteCode.__dup2:
823 {
824 var t = s.PopAnyType();
825 if (t.IsWidePrimitive || t == _context.VerifierJavaTypeFactory.ExtendedDouble)
826 {
827 s.PushType(t);
828 s.PushType(t);
829 }
830 else
831 {
832 var t2 = s.PopType();
833 s.PushType(t2);
834 s.PushType(t);
835 s.PushType(t2);
836 s.PushType(t);
837 }
838 break;
839 }
840 case NormalizedByteCode.__dup_x1:
841 {
842 var value1 = s.PopType();
843 var value2 = s.PopType();
844 s.PushType(value1);
845 s.PushType(value2);
846 s.PushType(value1);
847 break;
848 }
849 case NormalizedByteCode.__dup2_x1:
850 {
851 var value1 = s.PopAnyType();
852 if (value1.IsWidePrimitive || value1 == _context.VerifierJavaTypeFactory.ExtendedDouble)
853 {
854 var value2 = s.PopType();
855 s.PushType(value1);
856 s.PushType(value2);
857 s.PushType(value1);
858 }
859 else
860 {
861 var value2 = s.PopType();
862 var value3 = s.PopType();
863 s.PushType(value2);
864 s.PushType(value1);
865 s.PushType(value3);
866 s.PushType(value2);
867 s.PushType(value1);
868 }
869 break;
870 }
871 case NormalizedByteCode.__dup_x2:
872 {
873 var value1 = s.PopType();
874 var value2 = s.PopAnyType();
875 if (value2.IsWidePrimitive || value2 == _context.VerifierJavaTypeFactory.ExtendedDouble)
876 {
877 s.PushType(value1);
878 s.PushType(value2);
879 s.PushType(value1);
880 }
881 else
882 {
883 var value3 = s.PopType();
884 s.PushType(value1);
885 s.PushType(value3);
886 s.PushType(value2);
887 s.PushType(value1);
888 }
889 break;
890 }
891 case NormalizedByteCode.__dup2_x2:
892 {
893 var value1 = s.PopAnyType();
894 if (value1.IsWidePrimitive || value1 == _context.VerifierJavaTypeFactory.ExtendedDouble)
895 {
896 var value2 = s.PopAnyType();
897 if (value2.IsWidePrimitive || value2 == _context.VerifierJavaTypeFactory.ExtendedDouble)
898 {
899 // Form 4
900 s.PushType(value1);
901 s.PushType(value2);
902 s.PushType(value1);
903 }
904 else
905 {
906 // Form 2
907 var value3 = s.PopType();
908 s.PushType(value1);
909 s.PushType(value3);
910 s.PushType(value2);
911 s.PushType(value1);
912 }
913 }
914 else
915 {
916 var value2 = s.PopType();
917 var value3 = s.PopAnyType();
918 if (value3.IsWidePrimitive || value3 == _context.VerifierJavaTypeFactory.ExtendedDouble)
919 {
920 // Form 3
921 s.PushType(value2);
922 s.PushType(value1);
923 s.PushType(value3);
924 s.PushType(value2);
925 s.PushType(value1);
926 }
927 else
928 {
929 // Form 4
930 var value4 = s.PopType();
931 s.PushType(value2);
932 s.PushType(value1);
933 s.PushType(value4);
934 s.PushType(value3);
935 s.PushType(value2);
936 s.PushType(value1);
937 }
938 }
939 break;
940 }
941 case NormalizedByteCode.__pop:
942 s.PopType();
943 break;
944 case NormalizedByteCode.__pop2:
945 {
946 var type = s.PopAnyType();
947 if (!type.IsWidePrimitive && type != _context.VerifierJavaTypeFactory.ExtendedDouble)
948 s.PopType();
949
950 break;
951 }
952 case NormalizedByteCode.__monitorenter:
953 case NormalizedByteCode.__monitorexit:
954 // TODO these bytecodes are allowed on an uninitialized object, but
955 // we don't support that at the moment...
956 s.PopObjectType();
957 break;
958 case NormalizedByteCode.__return:
959 // mw is null if we're called from IsSideEffectFreeStaticInitializer
960 if (_method != null)
961 {
962 if (_method.ReturnType != _context.PrimitiveJavaTypeFactory.VOID)
963 throw new VerifyError("Wrong return type in function");
964
965 // if we're a constructor, make sure we called the base class constructor
966 s.CheckUninitializedThis();
967 }
968 break;
969 case NormalizedByteCode.__areturn:
970 s.PopObjectType(_method.ReturnType);
971 break;
972 case NormalizedByteCode.__ireturn:
973 {
974 s.PopInt();
975 if (!_method.ReturnType.IsIntOnStackPrimitive)
976 throw new VerifyError("Wrong return type in function");
977
978 break;
979 }
980 case NormalizedByteCode.__lreturn:
981 s.PopLong();
982 if (_method.ReturnType != _context.PrimitiveJavaTypeFactory.LONG)
983 throw new VerifyError("Wrong return type in function");
984
985 break;
986 case NormalizedByteCode.__freturn:
987 s.PopFloat();
988 if (_method.ReturnType != _context.PrimitiveJavaTypeFactory.FLOAT)
989 throw new VerifyError("Wrong return type in function");
990
991 break;
992 case NormalizedByteCode.__dreturn:
993 s.PopDouble();
994 if (_method.ReturnType != _context.PrimitiveJavaTypeFactory.DOUBLE)
995 throw new VerifyError("Wrong return type in function");
996
997 break;
998 case NormalizedByteCode.__fload:
999 s.GetLocalFloat(inst.NormalizedArg1);
1000 s.PushFloat();
1001 break;
1002 case NormalizedByteCode.__fstore:
1003 s.PopFloat();
1004 s.SetLocalFloat(inst.NormalizedArg1, i);
1005 break;
1006 case NormalizedByteCode.__dload:
1007 s.GetLocalDouble(inst.NormalizedArg1);
1008 s.PushDouble();
1009 break;
1010 case NormalizedByteCode.__dstore:
1011 s.PopDouble();
1012 s.SetLocalDouble(inst.NormalizedArg1, i);
1013 break;
1014 case NormalizedByteCode.__lload:
1015 s.GetLocalLong(inst.NormalizedArg1);
1016 s.PushLong();
1017 break;
1018 case NormalizedByteCode.__lstore:
1019 s.PopLong();
1020 s.SetLocalLong(inst.NormalizedArg1, i);
1021 break;
1022 case NormalizedByteCode.__lconst_0:
1023 case NormalizedByteCode.__lconst_1:
1024 s.PushLong();
1025 break;
1026 case NormalizedByteCode.__fconst_0:
1027 case NormalizedByteCode.__fconst_1:
1028 case NormalizedByteCode.__fconst_2:
1029 s.PushFloat();
1030 break;
1031 case NormalizedByteCode.__dconst_0:
1032 case NormalizedByteCode.__dconst_1:
1033 s.PushDouble();
1034 break;
1035 case NormalizedByteCode.__lcmp:
1036 s.PopLong();
1037 s.PopLong();
1038 s.PushInt();
1039 break;
1040 case NormalizedByteCode.__fcmpl:
1041 case NormalizedByteCode.__fcmpg:
1042 s.PopFloat();
1043 s.PopFloat();
1044 s.PushInt();
1045 break;
1046 case NormalizedByteCode.__dcmpl:
1047 case NormalizedByteCode.__dcmpg:
1048 s.PopDouble();
1049 s.PopDouble();
1050 s.PushInt();
1051 break;
1052 case NormalizedByteCode.__checkcast:
1053 s.PopObjectType();
1054 s.PushType(GetConstantPoolClassType(inst.Arg1));
1055 break;
1056 case NormalizedByteCode.__instanceof:
1057 s.PopObjectType();
1058 s.PushInt();
1059 break;
1060 case NormalizedByteCode.__iinc:
1061 s.GetLocalInt(inst.Arg1);
1062 break;
1063 case NormalizedByteCode.__athrow:
1064 if (RuntimeVerifierJavaType.IsFaultBlockException(s.PeekType()))
1065 s.PopFaultBlockException();
1066 else
1067 s.PopObjectType(_context.JavaBase.TypeOfjavaLangThrowable);
1068 break;
1069 case NormalizedByteCode.__tableswitch:
1070 case NormalizedByteCode.__lookupswitch:
1071 s.PopInt();
1072 break;
1073 case NormalizedByteCode.__i2b:
1074 s.PopInt();
1075 s.PushInt();
1076 break;
1077 case NormalizedByteCode.__i2c:
1078 s.PopInt();
1079 s.PushInt();
1080 break;
1081 case NormalizedByteCode.__i2s:
1082 s.PopInt();
1083 s.PushInt();
1084 break;
1085 case NormalizedByteCode.__i2l:
1086 s.PopInt();
1087 s.PushLong();
1088 break;
1089 case NormalizedByteCode.__i2f:
1090 s.PopInt();
1091 s.PushFloat();
1092 break;
1093 case NormalizedByteCode.__i2d:
1094 s.PopInt();
1095 s.PushDouble();
1096 break;
1097 case NormalizedByteCode.__l2i:
1098 s.PopLong();
1099 s.PushInt();
1100 break;
1101 case NormalizedByteCode.__l2f:
1102 s.PopLong();
1103 s.PushFloat();
1104 break;
1105 case NormalizedByteCode.__l2d:
1106 s.PopLong();
1107 s.PushDouble();
1108 break;
1109 case NormalizedByteCode.__f2i:
1110 s.PopFloat();
1111 s.PushInt();
1112 break;
1113 case NormalizedByteCode.__f2l:
1114 s.PopFloat();
1115 s.PushLong();
1116 break;
1117 case NormalizedByteCode.__f2d:
1118 s.PopFloat();
1119 s.PushDouble();
1120 break;
1121 case NormalizedByteCode.__d2i:
1122 s.PopDouble();
1123 s.PushInt();
1124 break;
1125 case NormalizedByteCode.__d2f:
1126 s.PopDouble();
1127 s.PushFloat();
1128 break;
1129 case NormalizedByteCode.__d2l:
1130 s.PopDouble();
1131 s.PushLong();
1132 break;
1133 case NormalizedByteCode.__nop:
1134 if (i + 1 == instructions.Length)
1135 throw new VerifyError("Falling off the end of the code");
1136 break;
1137 case NormalizedByteCode.__static_error:
1138 break;
1139 case NormalizedByteCode.__jsr:
1140 case NormalizedByteCode.__ret:
1141 throw new VerifyError("Bad instruction");
1142 default:
1143 throw new NotImplementedException(inst.NormalizedOpCode.ToString());
1144 }
1145
1146 if (s.GetStackHeight() > _classFileMethod.MaxStack)
1147 throw new VerifyError("Stack size too large");
1148
1149 for (int j = 0; j < _classFileMethod.ExceptionTable.Length; j++)
1150 if (_classFileMethod.ExceptionTable[j].endIndex == i + 1)
1151 MergeExceptionHandler(j, ref s);
1152
1153 try
1154 {
1155 switch (ByteCodeMetaData.GetFlowControl(inst.NormalizedOpCode))
1156 {
1157 case ByteCodeFlowControl.Switch:
1158 for (int j = 0; j < inst.SwitchEntryCount; j++)
1159 _state[inst.GetSwitchTargetIndex(j)] += s;
1160
1161 _state[inst.DefaultTarget] += s;
1162 break;
1163 case ByteCodeFlowControl.CondBranch:
1164 _state[i + 1] += s;
1165 _state[inst.TargetIndex] += s;
1166 break;
1167 case ByteCodeFlowControl.Branch:
1168 _state[inst.TargetIndex] += s;
1169 break;
1170 case ByteCodeFlowControl.Return:
1171 case ByteCodeFlowControl.Throw:
1172 break;
1173 case ByteCodeFlowControl.Next:
1174 _state[i + 1] += s;
1175 break;
1176 default:
1177 throw new InvalidOperationException();
1178 }
1179 }
1180 catch (IndexOutOfRangeException)
1181 {
1182 // we're going to assume that this always means that we have an invalid branch target
1183 // NOTE because PcIndexMap returns -1 for illegal PCs (in the middle of an instruction) and
1184 // we always use that value as an index into the state array, any invalid PC will result
1185 // in an IndexOutOfRangeException
1186 throw new VerifyError("Illegal target of jump or branch");
1187 }
1188 }
1189
1190 catch (VerifyError x)
1191 {
1192 var opcode = instructions[i].NormalizedOpCode.ToString();
1193 if (opcode.StartsWith("__"))
1194 opcode = opcode.Substring(2);
1195
1196 throw new VerifyError($"{x.Message} (class: {_classFile.Name}, method: {_classFileMethod.Name}, signature: {_classFileMethod.Signature}, offset: {instructions[i].PC}, instruction: {opcode})", x);
1197 }
1198 }
1199 }
1200 }
1201 }
1202
1203 void MergeExceptionHandler(int exceptionIndex, ref InstructionState curr)
1204 {
1205 var idx = _classFileMethod.ExceptionTable[exceptionIndex].handlerIndex;
1206 var exp = curr.CopyLocals();
1207
1208 var catchType = _classFileMethod.ExceptionTable[exceptionIndex].catchType;
1209 if (catchType.IsNil)
1210 {
1211 if (_faultTypes.TryGetValue(idx, out var faultType) == false)
1212 {
1213 faultType = RuntimeVerifierJavaType.MakeFaultBlockException(this, idx);
1214 _faultTypes.Add(idx, faultType);
1215 }
1216
1217 exp.PushType(faultType);
1218 }
1219 else
1220 {
1221 // TODO if the exception type is unloadable we should consider pushing
1222 // Throwable as the type and recording a loader constraint
1223 exp.PushType(GetConstantPoolClassType(catchType));
1224 }
1225
1226 _state[idx] += exp;
1227 }
1228
1229 // this verification pass must run on the unmodified bytecode stream
1230 void VerifyPassTwo()
1231 {
1232 var instructions = _classFileMethod.Instructions;
1233 for (int i = 0; i < instructions.Length; i++)
1234 {
1235 if (_state[i]._initialized)
1236 {
1237 try
1238 {
1239 switch (instructions[i].NormalizedOpCode)
1240 {
1241 case NormalizedByteCode.__invokeinterface:
1242 case NormalizedByteCode.__invokespecial:
1243 case NormalizedByteCode.__invokestatic:
1244 case NormalizedByteCode.__invokevirtual:
1245 VerifyInvokePassTwo(i);
1246 break;
1247 case NormalizedByteCode.__invokedynamic:
1248 VerifyInvokeDynamic(i);
1249 break;
1250 }
1251 }
1252 catch (VerifyError x)
1253 {
1254 var opcode = instructions[i].NormalizedOpCode.ToString();
1255 if (opcode.StartsWith("__"))
1256 opcode = opcode.Substring(2);
1257
1258 throw new VerifyError($"{x.Message} (class: {_classFile.Name}, method: {_classFileMethod.Name}, signature: {_classFileMethod.Signature}, offset: {instructions[i].PC}, instruction: {opcode})", x);
1259 }
1260 }
1261 }
1262 }
1263
1264 void VerifyInvokePassTwo(int index)
1265 {
1266 var stack = new StackState(_state, index);
1267 var invoke = _classFileMethod.Instructions[index].NormalizedOpCode;
1268 var cpi = GetMethodref(_classFileMethod.Instructions[index].Arg1);
1269 if ((invoke == NormalizedByteCode.__invokestatic || invoke == NormalizedByteCode.__invokespecial) && _classFile.MajorVersion >= 52)
1270 {
1271 // invokestatic and invokespecial may be used to invoke interface methods in Java 8
1272 // but invokespecial can only invoke methods in the current interface or a directly implemented interface
1273 if (invoke == NormalizedByteCode.__invokespecial && cpi is ClassFile.ConstantPoolItemInterfaceMethodref)
1274 {
1275 if (cpi.GetClassType() == _host)
1276 {
1277 // ok
1278 }
1279 else if (cpi.GetClassType() != _type && Array.IndexOf(_type.Interfaces, cpi.GetClassType()) == -1)
1280 {
1281 throw new VerifyError("Bad invokespecial instruction: interface method reference is in an indirect superinterface.");
1282 }
1283 }
1284 }
1285 else if ((cpi is ClassFile.ConstantPoolItemInterfaceMethodref) != (invoke == NormalizedByteCode.__invokeinterface))
1286 {
1287 throw new VerifyError("Illegal constant pool index");
1288 }
1289
1290 if (invoke != NormalizedByteCode.__invokespecial && ReferenceEquals(cpi.Name, StringConstants.INIT))
1291 throw new VerifyError("Must call initializers using invokespecial");
1292
1293 if (ReferenceEquals(cpi.Name, StringConstants.CLINIT))
1294 throw new VerifyError("Illegal call to internal method");
1295
1296 var args = cpi.GetArgTypes();
1297 for (int j = args.Length - 1; j >= 0; j--)
1298 stack.PopType(args[j]);
1299
1300 if (invoke == NormalizedByteCode.__invokeinterface)
1301 {
1302 int argcount = args.Length + 1;
1303 for (int j = 0; j < args.Length; j++)
1304 if (args[j].IsWidePrimitive)
1305 argcount++;
1306
1307 if (_classFileMethod.Instructions[index].Arg2 != argcount)
1308 throw new VerifyError("Inconsistent args size");
1309 }
1310
1311 if (invoke != NormalizedByteCode.__invokestatic)
1312 {
1313 if (ReferenceEquals(cpi.Name, StringConstants.INIT))
1314 {
1315 var type = stack.PopType();
1316 var isnew = RuntimeVerifierJavaType.IsNew(type);
1317 if ((isnew && ((RuntimeVerifierJavaType)type).UnderlyingType != cpi.GetClassType()) || (type == _context.VerifierJavaTypeFactory.UninitializedThis && cpi.GetClassType() != _type.BaseTypeWrapper && cpi.GetClassType() != _type) || (!isnew && type != _context.VerifierJavaTypeFactory.UninitializedThis))
1318 {
1319 // TODO oddly enough, Java fails verification for the class without
1320 // even running the constructor, so maybe constructors are always
1321 // verified...
1322 // NOTE when a constructor isn't verifiable, the static initializer
1323 // doesn't run either
1324 throw new VerifyError("Call to wrong initialization method");
1325 }
1326 }
1327 else
1328 {
1329 if (invoke != NormalizedByteCode.__invokeinterface)
1330 {
1331 var refType = stack.PopObjectType();
1332 var targetType = cpi.GetClassType();
1333
1334 if (!RuntimeVerifierJavaType.IsNullOrUnloadable(refType) && !targetType.IsUnloadable && !refType.IsAssignableTo(targetType))
1335 throw new VerifyError("Incompatible object argument for function call");
1336
1337 // for invokespecial we also need to make sure we're calling ourself or a base class
1338 if (invoke == NormalizedByteCode.__invokespecial)
1339 {
1340 if (RuntimeVerifierJavaType.IsNullOrUnloadable(refType))
1341 {
1342 // ok
1343 }
1344 else if (refType.IsSubTypeOf(_type))
1345 {
1346 // ok
1347 }
1348 else if (_host != null && refType.IsSubTypeOf(_host))
1349 {
1350 // ok
1351 }
1352 else
1353 {
1354 throw new VerifyError("Incompatible target object for invokespecial");
1355 }
1356 if (targetType.IsUnloadable)
1357 {
1358 // ok
1359 }
1360 else if (_type.IsSubTypeOf(targetType))
1361 {
1362 // ok
1363 }
1364 else if (_host != null && _host.IsSubTypeOf(targetType))
1365 {
1366 // ok
1367 }
1368 else
1369 {
1370 throw new VerifyError("Invokespecial cannot call subclass methods");
1371 }
1372 }
1373 }
1374 else /* __invokeinterface */
1375 {
1376 // NOTE unlike in the above case, we also allow *any* interface target type
1377 // regardless of whether it is compatible or not, because if it is not compatible
1378 // we want an IncompatibleClassChangeError at runtime
1379 var refType = stack.PopObjectType();
1380 var targetType = cpi.GetClassType();
1381 if (!RuntimeVerifierJavaType.IsNullOrUnloadable(refType) && !targetType.IsUnloadable && !refType.IsAssignableTo(targetType) && !targetType.IsInterface)
1382 throw new VerifyError("Incompatible object argument for function call");
1383 }
1384 }
1385 }
1386 }
1387
1388 void VerifyInvokeDynamic(int index)
1389 {
1390 var stack = new StackState(_state, index);
1391 var cpi = GetInvokeDynamic(_classFileMethod.Instructions[index].Arg1);
1392 var args = cpi.GetArgTypes();
1393 for (int j = args.Length - 1; j >= 0; j--)
1394 stack.PopType(args[j]);
1395 }
1396
1397 static void OptimizationPass(CodeInfo codeInfo, ClassFile classFile, ClassFile.Method method, UntangledExceptionTable exceptions, RuntimeJavaType wrapper, RuntimeClassLoader classLoader)
1398 {
1399 // optimization pass
1400 if (classLoader.RemoveAsserts)
1401 {
1402 // while the optimization is general, in practice it never happens that a getstatic is used on a final field,
1403 // so we only look for this if assert initialization has been optimized out
1404 if (classFile.HasAssertions)
1405 {
1406 // compute branch targets
1407 var flags = ComputePartialReachability(codeInfo, method.Instructions, exceptions, 0, false);
1408 var instructions = method.Instructions;
1409 for (int i = 0; i < instructions.Length; i++)
1410 {
1411 if (instructions[i].NormalizedOpCode == NormalizedByteCode.__getstatic &&
1412 instructions[i + 1].NormalizedOpCode == NormalizedByteCode.__ifne &&
1413 instructions[i + 1].TargetIndex > i &&
1414 (flags[i + 1] & InstructionFlags.BranchTarget) == 0)
1415 {
1416 if (classFile.GetFieldref(instructions[i].Arg1).GetField() is RuntimeConstantJavaField field &&
1417 field.FieldTypeWrapper == classLoader.Context.PrimitiveJavaTypeFactory.BOOLEAN &&
1418 (bool)field.GetConstantValue())
1419 {
1420 // we know the branch will always be taken, so we replace the getstatic/ifne by a goto.
1421 instructions[i].PatchOpCode(NormalizedByteCode.__goto, instructions[i + 1].TargetIndex);
1422 }
1423 }
1424 }
1425 }
1426 }
1427 }
1428
1429 void PatchHardErrorsAndDynamicMemberAccess(RuntimeJavaType wrapper, RuntimeJavaMethod mw)
1430 {
1431 // Now we do another pass to find "hard error" instructions
1432 if (true)
1433 {
1434 var instructions = _classFileMethod.Instructions;
1435 for (int i = 0; i < instructions.Length; i++)
1436 {
1437 if (_state[i]._initialized)
1438 {
1439 var stack = new StackState(_state, i);
1440
1441 switch (instructions[i].NormalizedOpCode)
1442 {
1443 case NormalizedByteCode.__invokeinterface:
1444 case NormalizedByteCode.__invokespecial:
1445 case NormalizedByteCode.__invokestatic:
1446 case NormalizedByteCode.__invokevirtual:
1447 PatchInvoke(wrapper, ref instructions[i], stack);
1448 break;
1449 case NormalizedByteCode.__getfield:
1450 case NormalizedByteCode.__putfield:
1451 case NormalizedByteCode.__getstatic:
1452 case NormalizedByteCode.__putstatic:
1453 PatchFieldAccess(wrapper, mw, ref instructions[i], stack);
1454 break;
1455 case NormalizedByteCode.__ldc:
1456 switch (_classFile.GetConstantPoolConstantType(instructions[i].Arg1))
1457 {
1458 case ClassFile.ConstantType.Class:
1459 {
1460 var tw = _classFile.GetConstantPoolClassType(instructions[i].Arg1);
1461 if (tw.IsUnloadable)
1462 ConditionalPatchNoClassDefFoundError(ref instructions[i], tw);
1463
1464 break;
1465 }
1466 case ClassFile.ConstantType.MethodType:
1467 {
1468 var cpi = _classFile.GetConstantPoolConstantMethodType(instructions[i].Arg1);
1469 var args = cpi.GetArgTypes();
1470 var tw = cpi.GetRetType();
1471 for (int j = 0; !tw.IsUnloadable && j < args.Length; j++)
1472 tw = args[j];
1473
1474 if (tw.IsUnloadable)
1475 ConditionalPatchNoClassDefFoundError(ref instructions[i], tw);
1476
1477 break;
1478 }
1479 case ClassFile.ConstantType.MethodHandle:
1480 PatchLdcMethodHandle(ref instructions[i]);
1481 break;
1482 }
1483 break;
1484 case NormalizedByteCode.__new:
1485 {
1486 var tw = _classFile.GetConstantPoolClassType(instructions[i].Arg1);
1487 if (tw.IsUnloadable)
1488 {
1489 ConditionalPatchNoClassDefFoundError(ref instructions[i], tw);
1490 }
1491 else if (!tw.IsAccessibleFrom(wrapper))
1492 {
1493 SetHardError(wrapper.ClassLoader, ref instructions[i], HardError.IllegalAccessError, "Try to access class {0} from class {1}", tw.Name, wrapper.Name);
1494 }
1495 else if (tw.IsAbstract)
1496 {
1497 SetHardError(wrapper.ClassLoader, ref instructions[i], HardError.InstantiationError, "{0}", tw.Name);
1498 }
1499
1500 break;
1501 }
1502 case NormalizedByteCode.__multianewarray:
1503 case NormalizedByteCode.__anewarray:
1504 {
1505 var tw = _classFile.GetConstantPoolClassType(instructions[i].Arg1);
1506 if (tw.IsUnloadable)
1507 {
1508 ConditionalPatchNoClassDefFoundError(ref instructions[i], tw);
1509 }
1510 else if (!tw.IsAccessibleFrom(wrapper))
1511 {
1512 SetHardError(wrapper.ClassLoader, ref instructions[i], HardError.IllegalAccessError, "Try to access class {0} from class {1}", tw.Name, wrapper.Name);
1513 }
1514
1515 break;
1516 }
1517 case NormalizedByteCode.__checkcast:
1518 case NormalizedByteCode.__instanceof:
1519 {
1520 var tw = _classFile.GetConstantPoolClassType(instructions[i].Arg1);
1521 if (tw.IsUnloadable)
1522 {
1523 // If the type is unloadable, we always generate the dynamic code
1524 // (regardless of ClassLoaderWrapper.DisableDynamicBinding), because at runtime,
1525 // null references should always pass thru without attempting
1526 // to load the type (for Sun compatibility).
1527 }
1528 else if (!tw.IsAccessibleFrom(wrapper))
1529 {
1530 SetHardError(wrapper.ClassLoader, ref instructions[i], HardError.IllegalAccessError, "Try to access class {0} from class {1}", tw.Name, wrapper.Name);
1531 }
1532
1533 break;
1534 }
1535 case NormalizedByteCode.__aaload:
1536 {
1537 stack.PopInt();
1538 var tw = stack.PopArrayType();
1539 if (tw.IsUnloadable)
1540 ConditionalPatchNoClassDefFoundError(ref instructions[i], tw);
1541
1542 break;
1543 }
1544 case NormalizedByteCode.__aastore:
1545 {
1546 stack.PopObjectType();
1547 stack.PopInt();
1548 RuntimeJavaType tw = stack.PopArrayType();
1549 if (tw.IsUnloadable)
1550 {
1551 ConditionalPatchNoClassDefFoundError(ref instructions[i], tw);
1552 }
1553 break;
1554 }
1555 default:
1556 break;
1557 }
1558 }
1559 }
1560 }
1561 }
1562
1563 void PatchLdcMethodHandle(ref ClassFile.Method.Instruction instr)
1564 {
1565 var cpi = _classFile.GetConstantPoolConstantMethodHandle(instr.Arg1);
1566 if (cpi.GetClassType().IsUnloadable)
1567 {
1568 ConditionalPatchNoClassDefFoundError(ref instr, cpi.GetClassType());
1569 }
1570 else if (!cpi.GetClassType().IsAccessibleFrom(_type))
1571 {
1572 SetHardError(_type.ClassLoader, ref instr, HardError.IllegalAccessError, "tried to access class {0} from class {1}", cpi.Class, _type.Name);
1573 }
1574 else if (cpi.Kind == MethodHandleKind.InvokeVirtual && cpi.GetClassType() == _context.JavaBase.TypeOfJavaLangInvokeMethodHandle && (cpi.Name == "invoke" || cpi.Name == "invokeExact"))
1575 {
1576 // it's allowed to use ldc to create a MethodHandle invoker
1577 }
1578 else if (cpi.Member == null || cpi.Member.IsStatic != (cpi.Kind == MethodHandleKind.GetStatic || cpi.Kind == MethodHandleKind.PutStatic || cpi.Kind == MethodHandleKind.InvokeStatic))
1579 {
1580 HardError err;
1581 string msg;
1582 switch (cpi.Kind)
1583 {
1584 case MethodHandleKind.GetField:
1585 case MethodHandleKind.GetStatic:
1586 case MethodHandleKind.PutField:
1587 case MethodHandleKind.PutStatic:
1588 err = HardError.NoSuchFieldError;
1589 msg = cpi.Name;
1590 break;
1591 default:
1592 err = HardError.NoSuchMethodError;
1593 msg = cpi.Class + "." + cpi.Name + cpi.Signature;
1594 break;
1595 }
1596
1597 SetHardError(_type.ClassLoader, ref instr, err, msg, cpi.Class, cpi.Name, SigToString(cpi.Signature));
1598 }
1599 else if (!cpi.Member.IsAccessibleFrom(cpi.GetClassType(), _type, cpi.GetClassType()))
1600 {
1601 if (cpi.Member.IsProtected && _type.IsSubTypeOf(cpi.Member.DeclaringType))
1602 {
1603 // this is allowed, the receiver will be narrowed to current type
1604 }
1605 else
1606 {
1607 SetHardError(_type.ClassLoader, ref instr, HardError.IllegalAccessException, "member is private: {0}.{1}/{2}/{3}, from {4}", cpi.Class, cpi.Name, SigToString(cpi.Signature), cpi.Kind, _type.Name);
1608 }
1609 }
1610 }
1611
1612 static string SigToString(string sig)
1613 {
1614 var sb = new ValueStringBuilder();
1615 var sep = "";
1616 int dims = 0;
1617 for (int i = 0; i < sig.Length; i++)
1618 {
1619 if (sig[i] == '(' || sig[i] == ')')
1620 {
1621 sb.Append(sig[i]);
1622 sep = "";
1623 continue;
1624 }
1625 else if (sig[i] == '[')
1626 {
1627 dims++;
1628 continue;
1629 }
1630
1631 sb.Append(sep);
1632 sep = ",";
1633 switch (sig[i])
1634 {
1635 case 'V':
1636 sb.Append("void");
1637 break;
1638 case 'B':
1639 sb.Append("byte");
1640 break;
1641 case 'Z':
1642 sb.Append("boolean");
1643 break;
1644 case 'S':
1645 sb.Append("short");
1646 break;
1647 case 'C':
1648 sb.Append("char");
1649 break;
1650 case 'I':
1651 sb.Append("int");
1652 break;
1653 case 'J':
1654 sb.Append("long");
1655 break;
1656 case 'F':
1657 sb.Append("float");
1658 break;
1659 case 'D':
1660 sb.Append("double");
1661 break;
1662 case 'L':
1663 var j = sig.IndexOf(';', i + 1);
1664 sb.Append(sig.AsSpan()[(i + 1)..j]);
1665 i = j;
1666 break;
1667 }
1668
1669 for (; dims != 0; dims--)
1670 sb.Append("[]");
1671 }
1672
1673 return sb.ToString();
1674 }
1675
1676 internal static InstructionFlags[] ComputePartialReachability(CodeInfo codeInfo, ClassFile.Method.Instruction[] instructions, UntangledExceptionTable exceptions, int initialInstructionIndex, bool skipFaultBlocks)
1677 {
1678 var flags = new InstructionFlags[instructions.Length];
1679 flags[initialInstructionIndex] |= InstructionFlags.Reachable;
1680 UpdatePartialReachability(flags, codeInfo, instructions, exceptions, skipFaultBlocks);
1681 return flags;
1682 }
1683
1684 static void UpdatePartialReachability(InstructionFlags[] flags, CodeInfo codeInfo, ClassFile.Method.Instruction[] instructions, UntangledExceptionTable exceptions, bool skipFaultBlocks)
1685 {
1686 var done = false;
1687 while (done == false)
1688 {
1689 done = true;
1690
1691 for (int i = 0; i < instructions.Length; i++)
1692 {
1693 if ((flags[i] & (InstructionFlags.Reachable | InstructionFlags.Processed)) == InstructionFlags.Reachable)
1694 {
1695 done = false;
1696 flags[i] |= InstructionFlags.Processed;
1697
1698 // mark the exception handlers reachable from this instruction
1699 for (int j = 0; j < exceptions.Length; j++)
1700 {
1701 if (exceptions[j].startIndex <= i && i < exceptions[j].endIndex)
1702 {
1703 int idx = exceptions[j].handlerIndex;
1704 if (!skipFaultBlocks || !RuntimeVerifierJavaType.IsFaultBlockException(codeInfo.GetRawStackTypeWrapper(idx, 0)))
1705 flags[idx] |= InstructionFlags.Reachable | InstructionFlags.BranchTarget;
1706 }
1707 }
1708
1709 MarkSuccessors(instructions, flags, i);
1710 }
1711 }
1712 }
1713 }
1714
1715 static void MarkSuccessors(ClassFile.Method.Instruction[] code, InstructionFlags[] flags, int index)
1716 {
1717 switch (ByteCodeMetaData.GetFlowControl(code[index].NormalizedOpCode))
1718 {
1719 case ByteCodeFlowControl.Switch:
1720 {
1721 for (int i = 0; i < code[index].SwitchEntryCount; i++)
1722 flags[code[index].GetSwitchTargetIndex(i)] |= InstructionFlags.Reachable | InstructionFlags.BranchTarget;
1723
1724 flags[code[index].DefaultTarget] |= InstructionFlags.Reachable | InstructionFlags.BranchTarget;
1725 break;
1726 }
1727 case ByteCodeFlowControl.Branch:
1728 flags[code[index].TargetIndex] |= InstructionFlags.Reachable | InstructionFlags.BranchTarget;
1729 break;
1730 case ByteCodeFlowControl.CondBranch:
1731 flags[code[index].TargetIndex] |= InstructionFlags.Reachable | InstructionFlags.BranchTarget;
1732 flags[index + 1] |= InstructionFlags.Reachable;
1733 break;
1734 case ByteCodeFlowControl.Return:
1735 case ByteCodeFlowControl.Throw:
1736 break;
1737 case ByteCodeFlowControl.Next:
1738 flags[index + 1] |= InstructionFlags.Reachable;
1739 break;
1740 default:
1741 throw new InvalidOperationException();
1742 }
1743 }
1744
1745 internal static UntangledExceptionTable UntangleExceptionBlocks(RuntimeContext context, ClassFile classFile, ClassFile.Method method)
1746 {
1747 var instructions = method.Instructions;
1748 var ar = new List<ExceptionTableEntry>(method.ExceptionTable);
1749
1750 // This optimization removes the recursive exception handlers that Java compiler place around
1751 // the exit of a synchronization block to be "safe" in the face of asynchronous exceptions.
1752 // (see http://weblog.ikvm.net/PermaLink.aspx?guid=3af9548e-4905-4557-8809-65a205ce2cd6)
1753 // We can safely remove them since the code we generate for this construct isn't async safe anyway,
1754 // but there is another reason why this optimization may be slightly controversial. In some
1755 // pathological cases it can cause observable differences, where the Sun JVM would spin in an
1756 // infinite loop, but we will throw an exception. However, the perf benefit is large enough to
1757 // warrant this "incompatibility".
1758 // Note that there is also code in the exception handler handling code that detects these bytecode
1759 // sequences to try to compile them into a fault block, instead of an exception handler.
1760 for (int i = 0; i < ar.Count; i++)
1761 {
1762 var ei = ar[i];
1763 if (ei.startIndex == ei.handlerIndex && ei.catchType.IsNil)
1764 {
1765 var index = ei.startIndex;
1766 if (index + 2 < instructions.Length &&
1767 ei.endIndex == index + 2 &&
1768 instructions[index].NormalizedOpCode == NormalizedByteCode.__aload &&
1769 instructions[index + 1].NormalizedOpCode == NormalizedByteCode.__monitorexit &&
1770 instructions[index + 2].NormalizedOpCode == NormalizedByteCode.__athrow)
1771 {
1772 // this is the async exception guard that Jikes and the Eclipse Java Compiler produce
1773 ar.RemoveAt(i);
1774 i--;
1775 }
1776 else if (index + 4 < instructions.Length &&
1777 ei.endIndex == index + 3 &&
1778 instructions[index].NormalizedOpCode == NormalizedByteCode.__astore &&
1779 instructions[index + 1].NormalizedOpCode == NormalizedByteCode.__aload &&
1780 instructions[index + 2].NormalizedOpCode == NormalizedByteCode.__monitorexit &&
1781 instructions[index + 3].NormalizedOpCode == NormalizedByteCode.__aload &&
1782 instructions[index + 4].NormalizedOpCode == NormalizedByteCode.__athrow &&
1783 instructions[index].NormalizedArg1 == instructions[index + 3].NormalizedArg1)
1784 {
1785 // this is the async exception guard that javac produces
1786 ar.RemoveAt(i);
1787 i--;
1788 }
1789 else if (index + 1 < instructions.Length &&
1790 ei.endIndex == index + 1 &&
1791 instructions[index].NormalizedOpCode == NormalizedByteCode.__astore)
1792 {
1793 // this is the finally guard that javac produces
1794 ar.RemoveAt(i);
1795 i--;
1796 }
1797 }
1798 }
1799
1800 // Modern versions of javac split try blocks when the try block contains a return statement.
1801 // Here we merge these exception blocks again, because it allows us to generate more efficient code.
1802 for (int i = 0; i < ar.Count - 1; i++)
1803 {
1804 if (ar[i].endIndex + 1 == ar[i + 1].startIndex &&
1805 ar[i].handlerIndex == ar[i + 1].handlerIndex &&
1806 ar[i].catchType == ar[i + 1].catchType &&
1807 IsReturn(instructions[ar[i].endIndex].NormalizedOpCode))
1808 {
1809 ar[i] = new ExceptionTableEntry(ar[i].startIndex, ar[i + 1].endIndex, ar[i].handlerIndex, ar[i].catchType, ar[i].ordinal);
1810 ar.RemoveAt(i + 1);
1811 i--;
1812 }
1813 }
1814
1815 restart:
1816 for (int i = 0; i < ar.Count; i++)
1817 {
1818 var ei = ar[i];
1819 for (int j = 0; j < ar.Count; j++)
1820 {
1821 var ej = ar[j];
1822 if (ei.startIndex <= ej.startIndex && ej.startIndex < ei.endIndex)
1823 {
1824 // 0006/test.j
1825 if (ej.endIndex > ei.endIndex)
1826 {
1827 var emi = new ExceptionTableEntry(ej.startIndex, ei.endIndex, ei.handlerIndex, ei.catchType, ei.ordinal);
1828 var emj = new ExceptionTableEntry(ej.startIndex, ei.endIndex, ej.handlerIndex, ej.catchType, ej.ordinal);
1829 ei = new ExceptionTableEntry(ei.startIndex, emi.startIndex, ei.handlerIndex, ei.catchType, ei.ordinal);
1830 ej = new ExceptionTableEntry(emj.endIndex, ej.endIndex, ej.handlerIndex, ej.catchType, ej.ordinal);
1831 ar[i] = ei;
1832 ar[j] = ej;
1833 ar.Insert(j, emj);
1834 ar.Insert(i + 1, emi);
1835 goto restart;
1836 }
1837 // 0007/test.j
1838 else if (j > i && ej.endIndex < ei.endIndex)
1839 {
1840 var emi = new ExceptionTableEntry(ej.startIndex, ej.endIndex, ei.handlerIndex, ei.catchType, ei.ordinal);
1841 var eei = new ExceptionTableEntry(ej.endIndex, ei.endIndex, ei.handlerIndex, ei.catchType, ei.ordinal);
1842 ei = new ExceptionTableEntry(ei.startIndex, emi.startIndex, ei.handlerIndex, ei.catchType, ei.ordinal);
1843 ar[i] = ei;
1844 ar.Insert(i + 1, eei);
1845 ar.Insert(i + 1, emi);
1846 goto restart;
1847 }
1848 }
1849 }
1850 }
1851 // Split try blocks at branch targets (branches from outside the try block)
1852 restart_split:
1853 for (int i = 0; i < ar.Count; i++)
1854 {
1855 var ei = ar[i];
1856 int start = ei.startIndex;
1857 int end = ei.endIndex;
1858 for (int j = 0; j < instructions.Length; j++)
1859 {
1860 if (j < start || j >= end)
1861 {
1862 switch (instructions[j].NormalizedOpCode)
1863 {
1864 case NormalizedByteCode.__tableswitch:
1865 case NormalizedByteCode.__lookupswitch:
1866 // start at -1 to have an opportunity to handle the default offset
1867 for (int k = -1; k < instructions[j].SwitchEntryCount; k++)
1868 {
1869 int targetIndex = (k == -1 ? instructions[j].DefaultTarget : instructions[j].GetSwitchTargetIndex(k));
1870 if (ei.startIndex < targetIndex && targetIndex < ei.endIndex)
1871 {
1872 var en = new ExceptionTableEntry(targetIndex, ei.endIndex, ei.handlerIndex, ei.catchType, ei.ordinal);
1873 ei = new ExceptionTableEntry(ei.startIndex, targetIndex, ei.handlerIndex, ei.catchType, ei.ordinal);
1874 ar[i] = ei;
1875 ar.Insert(i + 1, en);
1876 goto restart_split;
1877 }
1878 }
1879 break;
1880 case NormalizedByteCode.__ifeq:
1881 case NormalizedByteCode.__ifne:
1882 case NormalizedByteCode.__iflt:
1883 case NormalizedByteCode.__ifge:
1884 case NormalizedByteCode.__ifgt:
1885 case NormalizedByteCode.__ifle:
1886 case NormalizedByteCode.__if_icmpeq:
1887 case NormalizedByteCode.__if_icmpne:
1888 case NormalizedByteCode.__if_icmplt:
1889 case NormalizedByteCode.__if_icmpge:
1890 case NormalizedByteCode.__if_icmpgt:
1891 case NormalizedByteCode.__if_icmple:
1892 case NormalizedByteCode.__if_acmpeq:
1893 case NormalizedByteCode.__if_acmpne:
1894 case NormalizedByteCode.__ifnull:
1895 case NormalizedByteCode.__ifnonnull:
1896 case NormalizedByteCode.__goto:
1897 {
1898 int targetIndex = instructions[j].Arg1;
1899 if (ei.startIndex < targetIndex && targetIndex < ei.endIndex)
1900 {
1901 var en = new ExceptionTableEntry(targetIndex, ei.endIndex, ei.handlerIndex, ei.catchType, ei.ordinal);
1902 ei = new ExceptionTableEntry(ei.startIndex, targetIndex, ei.handlerIndex, ei.catchType, ei.ordinal);
1903 ar[i] = ei;
1904 ar.Insert(i + 1, en);
1905 goto restart_split;
1906 }
1907 break;
1908 }
1909 }
1910 }
1911 }
1912 }
1913
1914 // exception handlers are also a kind of jump, so we need to split try blocks around handlers as well
1915 for (int i = 0; i < ar.Count; i++)
1916 {
1917 var ei = ar[i];
1918 for (int j = 0; j < ar.Count; j++)
1919 {
1920 var ej = ar[j];
1921 if (ei.startIndex < ej.handlerIndex && ej.handlerIndex < ei.endIndex)
1922 {
1923 var en = new ExceptionTableEntry(ej.handlerIndex, ei.endIndex, ei.handlerIndex, ei.catchType, ei.ordinal);
1924 ei = new ExceptionTableEntry(ei.startIndex, ej.handlerIndex, ei.handlerIndex, ei.catchType, ei.ordinal);
1925 ar[i] = ei;
1926 ar.Insert(i + 1, en);
1927 goto restart_split;
1928 }
1929 }
1930 }
1931
1932 // filter out zero length try blocks
1933 for (int i = 0; i < ar.Count; i++)
1934 {
1935 var ei = ar[i];
1936 if (ei.startIndex == ei.endIndex)
1937 {
1938 ar.RemoveAt(i);
1939 i--;
1940 }
1941 else
1942 {
1943 // exception blocks that only contain harmless instructions (i.e. instructions that will *never* throw an exception)
1944 // are also filtered out (to improve the quality of the generated code)
1945 var exceptionType = ei.catchType.IsNil ? context.JavaBase.TypeOfjavaLangThrowable : classFile.GetConstantPoolClassType(ei.catchType);
1946 if (exceptionType.IsUnloadable)
1947 {
1948 // we can't remove handlers for unloadable types
1949 }
1950 else if (context.MethodAnalyzerFactory.JavaLangThreadDeathType.IsAssignableTo(exceptionType))
1951 {
1952 // We only remove exception handlers that could catch ThreadDeath in limited cases, because it can be thrown
1953 // asynchronously (and thus appear on any instruction). This is particularly important to ensure that
1954 // we run finally blocks when a thread is killed.
1955 // Note that even so, we aren't remotely async exception safe.
1956 int start = ei.startIndex;
1957 int end = ei.endIndex;
1958 for (int j = start; j < end; j++)
1959 {
1960 switch (instructions[j].NormalizedOpCode)
1961 {
1962 case NormalizedByteCode.__aload:
1963 case NormalizedByteCode.__iload:
1964 case NormalizedByteCode.__lload:
1965 case NormalizedByteCode.__fload:
1966 case NormalizedByteCode.__dload:
1967 case NormalizedByteCode.__astore:
1968 case NormalizedByteCode.__istore:
1969 case NormalizedByteCode.__lstore:
1970 case NormalizedByteCode.__fstore:
1971 case NormalizedByteCode.__dstore:
1972 break;
1973 case NormalizedByteCode.__dup:
1974 case NormalizedByteCode.__dup_x1:
1975 case NormalizedByteCode.__dup_x2:
1976 case NormalizedByteCode.__dup2:
1977 case NormalizedByteCode.__dup2_x1:
1978 case NormalizedByteCode.__dup2_x2:
1979 case NormalizedByteCode.__pop:
1980 case NormalizedByteCode.__pop2:
1981 break;
1982 case NormalizedByteCode.__return:
1983 case NormalizedByteCode.__areturn:
1984 case NormalizedByteCode.__ireturn:
1985 case NormalizedByteCode.__lreturn:
1986 case NormalizedByteCode.__freturn:
1987 case NormalizedByteCode.__dreturn:
1988 break;
1989 case NormalizedByteCode.__goto:
1990 // if there is a branch that stays inside the block, we should keep the block
1991 if (start <= instructions[j].TargetIndex && instructions[j].TargetIndex < end)
1992 goto next;
1993 break;
1994 default:
1995 goto next;
1996 }
1997 }
1998 ar.RemoveAt(i);
1999 i--;
2000 }
2001 else
2002 {
2003 int start = ei.startIndex;
2004 int end = ei.endIndex;
2005 for (int j = start; j < end; j++)
2006 if (ByteCodeMetaData.CanThrowException(instructions[j].NormalizedOpCode))
2007 goto next;
2008
2009 ar.RemoveAt(i);
2010 i--;
2011 }
2012 }
2013 next:;
2014 }
2015
2016 var exceptions = ar.ToArray();
2017 Array.Sort(exceptions, new ExceptionTableEntryComparer());
2018 return new UntangledExceptionTable(exceptions);
2019 }
2020
2026 static bool IsReturn(NormalizedByteCode bc)
2027 {
2028 return bc is
2029 NormalizedByteCode.__return or
2030 NormalizedByteCode.__areturn or
2031 NormalizedByteCode.__dreturn or
2032 NormalizedByteCode.__ireturn or
2033 NormalizedByteCode.__freturn or
2034 NormalizedByteCode.__lreturn;
2035 }
2036
2037 static bool AnalyzePotentialFaultBlocks(CodeInfo codeInfo, ClassFile.Method method, UntangledExceptionTable exceptions)
2038 {
2039 var code = method.Instructions;
2040 var changed = false;
2041 var done = false;
2042
2043 while (done == false)
2044 {
2045 done = true;
2046 var stack = new Stack<ExceptionTableEntry>();
2047 var current = new ExceptionTableEntry(0, code.Length, -1, new ClassConstantHandle(ushort.MaxValue), -1);
2048 stack.Push(current);
2049
2050 for (int i = 0; i < exceptions.Length; i++)
2051 {
2052 while (exceptions[i].startIndex >= current.endIndex)
2053 current = stack.Pop();
2054
2055 Debug.Assert(exceptions[i].startIndex >= current.startIndex && exceptions[i].endIndex <= current.endIndex);
2056 if (exceptions[i].catchType.IsNil && codeInfo.HasState(exceptions[i].handlerIndex) && RuntimeVerifierJavaType.IsFaultBlockException(codeInfo.GetRawStackTypeWrapper(exceptions[i].handlerIndex, 0)))
2057 {
2058 var flags = ComputePartialReachability(codeInfo, method.Instructions, exceptions, exceptions[i].handlerIndex, true);
2059 for (int j = 0; j < code.Length; j++)
2060 {
2061 if ((flags[j] & InstructionFlags.Reachable) != 0)
2062 {
2063 switch (code[j].NormalizedOpCode)
2064 {
2065 case NormalizedByteCode.__return:
2066 case NormalizedByteCode.__areturn:
2067 case NormalizedByteCode.__ireturn:
2068 case NormalizedByteCode.__lreturn:
2069 case NormalizedByteCode.__freturn:
2070 case NormalizedByteCode.__dreturn:
2071 goto not_fault_block;
2072 case NormalizedByteCode.__athrow:
2073 for (int k = i + 1; k < exceptions.Length; k++)
2074 if (exceptions[k].startIndex <= j && j < exceptions[k].endIndex)
2075 goto not_fault_block;
2076
2077 if (RuntimeVerifierJavaType.IsFaultBlockException(codeInfo.GetRawStackTypeWrapper(j, 0)) && codeInfo.GetRawStackTypeWrapper(j, 0) != codeInfo.GetRawStackTypeWrapper(exceptions[i].handlerIndex, 0))
2078 goto not_fault_block;
2079
2080 break;
2081 }
2082
2083 if (j < current.startIndex || j >= current.endIndex)
2084 goto not_fault_block;
2085 else if (exceptions[i].startIndex <= j && j < exceptions[i].endIndex)
2086 goto not_fault_block;
2087 else
2088 continue;
2089
2090 not_fault_block:
2091 RuntimeVerifierJavaType.ClearFaultBlockException(codeInfo.GetRawStackTypeWrapper(exceptions[i].handlerIndex, 0));
2092 done = false;
2093 changed = true;
2094 break;
2095 }
2096 }
2097 }
2098
2099 stack.Push(current);
2100 current = exceptions[i];
2101 }
2102 }
2103
2104 return changed;
2105 }
2106
2107 static void ConvertFinallyBlocks(CodeInfo codeInfo, ClassFile.Method method, UntangledExceptionTable exceptions)
2108 {
2109 var code = method.Instructions;
2110 var flags = ComputePartialReachability(codeInfo, code, exceptions, 0, false);
2111 for (int i = 0; i < exceptions.Length; i++)
2112 {
2113 if (exceptions[i].catchType.IsNil && codeInfo.HasState(exceptions[i].handlerIndex) && RuntimeVerifierJavaType.IsFaultBlockException(codeInfo.GetRawStackTypeWrapper(exceptions[i].handlerIndex, 0)))
2114 {
2115 if (IsSynchronizedBlockHandler(code, exceptions[i].handlerIndex) &&
2116 exceptions[i].endIndex - 2 >= exceptions[i].startIndex &&
2117 TryFindSingleTryBlockExit(code, flags, exceptions, new ExceptionTableEntry(exceptions[i].startIndex, exceptions[i].endIndex - 2, exceptions[i].handlerIndex, ClassConstantHandle.Nil, exceptions[i].ordinal), i, out var exit) &&
2118 exit == exceptions[i].endIndex - 2 &&
2119 (flags[exit + 1] & InstructionFlags.BranchTarget) == 0 &&
2120 MatchInstructions(code, exit, exceptions[i].handlerIndex + 1) &&
2121 MatchInstructions(code, exit + 1, exceptions[i].handlerIndex + 2) &&
2122 MatchExceptionCoverage(exceptions, i, exceptions[i].handlerIndex + 1, exceptions[i].handlerIndex + 3, exit, exit + 2) &&
2123 exceptions[i].handlerIndex <= ushort.MaxValue)
2124 {
2125 code[exit].PatchOpCode(NormalizedByteCode.__goto_finally, exceptions[i].endIndex, (short)exceptions[i].handlerIndex);
2126 exceptions.SetFinally(i);
2127 continue;
2128 }
2129
2130 if (TryFindSingleTryBlockExit(code, flags, exceptions, exceptions[i], i, out exit) &&
2131 // the stack must be empty
2132 codeInfo.GetStackHeight(exit) == 0 &&
2133 // the exit code must not be reachable (except from within the try-block),
2134 // because we're going to patch it to jump around the exit code
2135 !IsReachableFromOutsideTryBlock(codeInfo, code, exceptions, exceptions[i], exit))
2136 {
2137 if (MatchFinallyBlock(codeInfo, code, exceptions, exceptions[i].handlerIndex, exit, out var exitHandlerEnd, out var faultHandlerEnd))
2138 {
2139 if (exit != exitHandlerEnd &&
2140 codeInfo.GetStackHeight(exitHandlerEnd) == 0 &&
2141 MatchExceptionCoverage(exceptions, -1, exceptions[i].handlerIndex, faultHandlerEnd, exit, exitHandlerEnd))
2142 {
2143 // We use Arg2 (which is a short) to store the handler in the __goto_finally pseudo-opcode,
2144 // so we can only do that if handlerIndex fits in a short (note that we can use the sign bit too).
2145 if (exceptions[i].handlerIndex <= ushort.MaxValue)
2146 {
2147 code[exit].PatchOpCode(NormalizedByteCode.__goto_finally, exitHandlerEnd, (short)exceptions[i].handlerIndex);
2148 exceptions.SetFinally(i);
2149 }
2150 }
2151 }
2152
2153 continue;
2154 }
2155 }
2156 }
2157 }
2158
2159 static bool IsSynchronizedBlockHandler(ClassFile.Method.Instruction[] code, int index)
2160 {
2161 return
2162 code[index].NormalizedOpCode == NormalizedByteCode.__astore &&
2163 code[index + 1].NormalizedOpCode == NormalizedByteCode.__aload &&
2164 code[index + 2].NormalizedOpCode == NormalizedByteCode.__monitorexit &&
2165 code[index + 3].NormalizedOpCode == NormalizedByteCode.__aload &&
2166 code[index + 3].Arg1 == code[index].Arg1 &&
2167 code[index + 4].NormalizedOpCode == NormalizedByteCode.__athrow;
2168 }
2169
2170 static bool MatchExceptionCoverage(UntangledExceptionTable exceptions, int skipException, int startFault, int endFault, int startExit, int endExit)
2171 {
2172 for (int j = 0; j < exceptions.Length; j++)
2173 if (j != skipException && ExceptionCovers(exceptions[j], startFault, endFault) != ExceptionCovers(exceptions[j], startExit, endExit))
2174 return false;
2175
2176 return true;
2177 }
2178
2179 static bool ExceptionCovers(ExceptionTableEntry exception, int start, int end)
2180 {
2181 return exception.startIndex < end && exception.endIndex > start;
2182 }
2183
2184 static bool MatchFinallyBlock(CodeInfo codeInfo, ClassFile.Method.Instruction[] code, UntangledExceptionTable exceptions, int faultHandler, int exitHandler, out int exitHandlerEnd, out int faultHandlerEnd)
2185 {
2186 exitHandlerEnd = -1;
2187 faultHandlerEnd = -1;
2188 if (code[faultHandler].NormalizedOpCode != NormalizedByteCode.__astore)
2189 return false;
2190
2191 int startFault = faultHandler;
2192 int faultLocal = code[faultHandler++].NormalizedArg1;
2193 for (; ; )
2194 {
2195 if (code[faultHandler].NormalizedOpCode == NormalizedByteCode.__aload &&
2196 code[faultHandler].NormalizedArg1 == faultLocal &&
2197 code[faultHandler + 1].NormalizedOpCode == NormalizedByteCode.__athrow)
2198 {
2199 // make sure that instructions that we haven't covered aren't reachable
2200 var flags = ComputePartialReachability(codeInfo, code, exceptions, startFault, false);
2201 for (int i = 0; i < flags.Length; i++)
2202 if ((i < startFault || i > faultHandler + 1) && (flags[i] & InstructionFlags.Reachable) != 0)
2203 return false;
2204
2205 exitHandlerEnd = exitHandler;
2206 faultHandlerEnd = faultHandler;
2207 return true;
2208 }
2209
2210 if (!MatchInstructions(code, faultHandler, exitHandler))
2211 return false;
2212
2213 faultHandler++;
2214 exitHandler++;
2215 }
2216 }
2217
2218 static bool MatchInstructions(ClassFile.Method.Instruction[] code, int i, int j)
2219 {
2220 if (code[i].NormalizedOpCode != code[j].NormalizedOpCode)
2221 return false;
2222
2223 switch (ByteCodeMetaData.GetFlowControl(code[i].NormalizedOpCode))
2224 {
2225 case ByteCodeFlowControl.Branch:
2226 case ByteCodeFlowControl.CondBranch:
2227 if (code[i].Arg1 - i != code[j].Arg1 - j)
2228 return false;
2229
2230 break;
2231 case ByteCodeFlowControl.Switch:
2232 if (code[i].SwitchEntryCount != code[j].SwitchEntryCount)
2233 return false;
2234
2235 for (int k = 0; k < code[i].SwitchEntryCount; k++)
2236 if (code[i].GetSwitchTargetIndex(k) != code[j].GetSwitchTargetIndex(k))
2237 return false;
2238
2239 if (code[i].DefaultTarget != code[j].DefaultTarget)
2240 return false;
2241
2242 break;
2243 default:
2244 if (code[i].Arg1 != code[j].Arg1)
2245 return false;
2246 if (code[i].Arg2 != code[j].Arg2)
2247 return false;
2248
2249 break;
2250 }
2251
2252 return true;
2253 }
2254
2255 static bool IsReachableFromOutsideTryBlock(CodeInfo codeInfo, ClassFile.Method.Instruction[] code, UntangledExceptionTable exceptions, ExceptionTableEntry tryBlock, int instructionIndex)
2256 {
2257 var flags = new InstructionFlags[code.Length];
2258 flags[0] |= InstructionFlags.Reachable;
2259 // We mark the first instruction of the try-block as already processed, so that UpdatePartialReachability will skip the try-block.
2260 // Note that we can do this, because it is not possible to jump into the middle of a try-block (after the exceptions have been untangled).
2261 flags[tryBlock.startIndex] = InstructionFlags.Processed;
2262 // We mark the successor instructions of the instruction we're examinining as reachable,
2263 // to figure out if the code following the handler somehow branches back to it.
2264 MarkSuccessors(code, flags, instructionIndex);
2265 UpdatePartialReachability(flags, codeInfo, code, exceptions, false);
2266 return (flags[instructionIndex] & InstructionFlags.Reachable) != 0;
2267 }
2268
2269 static bool TryFindSingleTryBlockExit(ClassFile.Method.Instruction[] code, InstructionFlags[] flags, UntangledExceptionTable exceptions, ExceptionTableEntry exception, int exceptionIndex, out int exit)
2270 {
2271 exit = -1;
2272 var fail = false;
2273 var nextIsReachable = false;
2274
2275 for (int i = exception.startIndex; !fail && i < exception.endIndex; i++)
2276 {
2277 if ((flags[i] & InstructionFlags.Reachable) != 0)
2278 {
2279 nextIsReachable = false;
2280 for (int j = 0; j < exceptions.Length; j++)
2281 if (j != exceptionIndex && exceptions[j].startIndex >= exception.startIndex && exception.endIndex <= exceptions[j].endIndex)
2282 UpdateTryBlockExit(exception, exceptions[j].handlerIndex, ref exit, ref fail);
2283
2284 switch (ByteCodeMetaData.GetFlowControl(code[i].NormalizedOpCode))
2285 {
2286 case ByteCodeFlowControl.Switch:
2287 {
2288 for (int j = 0; j < code[i].SwitchEntryCount; j++)
2289 UpdateTryBlockExit(exception, code[i].GetSwitchTargetIndex(j), ref exit, ref fail);
2290
2291 UpdateTryBlockExit(exception, code[i].DefaultTarget, ref exit, ref fail);
2292 break;
2293 }
2294 case ByteCodeFlowControl.Branch:
2295 UpdateTryBlockExit(exception, code[i].TargetIndex, ref exit, ref fail);
2296 break;
2297 case ByteCodeFlowControl.CondBranch:
2298 UpdateTryBlockExit(exception, code[i].TargetIndex, ref exit, ref fail);
2299 nextIsReachable = true;
2300 break;
2301 case ByteCodeFlowControl.Return:
2302 fail = true;
2303 break;
2304 case ByteCodeFlowControl.Throw:
2305 break;
2306 case ByteCodeFlowControl.Next:
2307 nextIsReachable = true;
2308 break;
2309 default:
2310 throw new InvalidOperationException();
2311 }
2312 }
2313 }
2314
2315 if (nextIsReachable)
2316 UpdateTryBlockExit(exception, exception.endIndex, ref exit, ref fail);
2317
2318 return !fail && exit != -1;
2319 }
2320
2321 static void UpdateTryBlockExit(ExceptionTableEntry exception, int targetIndex, ref int exitIndex, ref bool fail)
2322 {
2323 if (exception.startIndex <= targetIndex && targetIndex < exception.endIndex)
2324 {
2325 // branch stays inside try block
2326 }
2327 else if (exitIndex == -1)
2328 {
2329 exitIndex = targetIndex;
2330 }
2331 else if (exitIndex != targetIndex)
2332 {
2333 fail = true;
2334 }
2335 }
2336
2337 void ConditionalPatchNoClassDefFoundError(ref ClassFile.Method.Instruction instruction, RuntimeJavaType tw)
2338 {
2339 var loader = _type.ClassLoader;
2340 if (loader.DisableDynamicBinding)
2341 SetHardError(loader, ref instruction, HardError.NoClassDefFoundError, "{0}", tw.Name);
2342 }
2343
2344 void SetHardError(RuntimeClassLoader classLoader, ref ClassFile.Method.Instruction instruction, HardError hardError, string message, params object[] args)
2345 {
2346 var text = string.Format(message, args);
2347
2348 switch (hardError)
2349 {
2350 case HardError.NoClassDefFoundError:
2351 classLoader.Diagnostics.EmittedNoClassDefFoundError(_classFile.Name + "." + _classFileMethod.Name + _classFileMethod.Signature, text);
2352 break;
2353 case HardError.IllegalAccessError:
2354 classLoader.Diagnostics.EmittedIllegalAccessError(_classFile.Name + "." + _classFileMethod.Name + _classFileMethod.Signature, text);
2355 break;
2356 case HardError.InstantiationError:
2357 classLoader.Diagnostics.EmittedInstantiationError(_classFile.Name + "." + _classFileMethod.Name + _classFileMethod.Signature, text);
2358 break;
2359 case HardError.IncompatibleClassChangeError:
2360 classLoader.Diagnostics.EmittedIncompatibleClassChangeError(_classFile.Name + "." + _classFileMethod.Name + _classFileMethod.Signature, text);
2361 break;
2362 case HardError.IllegalAccessException:
2363 classLoader.Diagnostics.EmittedIllegalAccessError(_classFile.Name + "." + _classFileMethod.Name + _classFileMethod.Signature, text);
2364 break;
2365 case HardError.NoSuchFieldError:
2366 classLoader.Diagnostics.EmittedNoSuchFieldError(_classFile.Name + "." + _classFileMethod.Name + _classFileMethod.Signature, text);
2367 break;
2368 case HardError.AbstractMethodError:
2369 classLoader.Diagnostics.EmittedAbstractMethodError(_classFile.Name + "." + _classFileMethod.Name + _classFileMethod.Signature, text);
2370 break;
2371 case HardError.NoSuchMethodError:
2372 classLoader.Diagnostics.EmittedNoSuchMethodError(_classFile.Name + "." + _classFileMethod.Name + _classFileMethod.Signature, text);
2373 break;
2374 case HardError.LinkageError:
2375 classLoader.Diagnostics.EmittedLinkageError(_classFile.Name + "." + _classFileMethod.Name + _classFileMethod.Signature, text);
2376 break;
2377 default:
2378 throw new InvalidOperationException();
2379 }
2380
2381 instruction.SetHardError(hardError, AllocErrorMessage(text));
2382 }
2383
2384 void PatchInvoke(RuntimeJavaType wrapper, ref ClassFile.Method.Instruction instr, StackState stack)
2385 {
2386 var cpi = GetMethodref(instr.Arg1);
2387 var invoke = instr.NormalizedOpCode;
2388 var isnew = false;
2389
2390 if (invoke == NormalizedByteCode.__invokevirtual &&
2391 cpi is { Class: "java.lang.invoke.MethodHandle", Name: "invoke" or "invokeExact" or "invokeBasic" })
2392 {
2393 if (cpi.GetArgTypes().Length > 127 && _context.MethodHandleUtil.SlotCount(cpi.GetArgTypes()) > 254)
2394 {
2395 instr.SetHardError(HardError.LinkageError, AllocErrorMessage("bad parameter count"));
2396 return;
2397 }
2398
2399 instr.PatchOpCode(NormalizedByteCode.__methodhandle_invoke);
2400 return;
2401 }
2402
2403 if (invoke == NormalizedByteCode.__invokestatic &&
2404 cpi is { Class: "java.lang.invoke.MethodHandle", Name: "linkToVirtual" or "linkToStatic" or "linkToSpecial" or "linkToInterface" } &&
2405 _context.JavaBase.TypeOfJavaLangInvokeMethodHandle.IsPackageAccessibleFrom(wrapper))
2406 {
2407 instr.PatchOpCode(NormalizedByteCode.__methodhandle_link);
2408 return;
2409 }
2410
2411 RuntimeJavaType thisType;
2412 if (invoke == NormalizedByteCode.__invokestatic)
2413 {
2414 thisType = null;
2415 }
2416 else
2417 {
2418 var args = cpi.GetArgTypes();
2419 for (int j = args.Length - 1; j >= 0; j--)
2420 stack.PopType(args[j]);
2421
2422 thisType = SigTypeToClassName(stack.PeekType(), cpi.GetClassType(), wrapper);
2423 if (ReferenceEquals(cpi.Name, StringConstants.INIT))
2424 {
2425 var type = stack.PopType();
2426 isnew = RuntimeVerifierJavaType.IsNew(type);
2427 }
2428 }
2429
2430 if (cpi.GetClassType().IsUnloadable)
2431 {
2432 if (wrapper.ClassLoader.DisableDynamicBinding)
2433 {
2434 SetHardError(wrapper.ClassLoader, ref instr, HardError.NoClassDefFoundError, "{0}", cpi.GetClassType().Name);
2435 }
2436 else
2437 {
2438 switch (invoke)
2439 {
2440 case NormalizedByteCode.__invokeinterface:
2441 instr.PatchOpCode(NormalizedByteCode.__dynamic_invokeinterface);
2442 break;
2443 case NormalizedByteCode.__invokestatic:
2444 instr.PatchOpCode(NormalizedByteCode.__dynamic_invokestatic);
2445 break;
2446 case NormalizedByteCode.__invokevirtual:
2447 instr.PatchOpCode(NormalizedByteCode.__dynamic_invokevirtual);
2448 break;
2449 case NormalizedByteCode.__invokespecial:
2450 if (isnew)
2451 instr.PatchOpCode(NormalizedByteCode.__dynamic_invokespecial);
2452 else
2453 throw new VerifyError("Invokespecial cannot call subclass methods");
2454
2455 break;
2456 default:
2457 throw new InvalidOperationException();
2458 }
2459 }
2460 }
2461 else if (invoke == NormalizedByteCode.__invokeinterface && !cpi.GetClassType().IsInterface)
2462 {
2463 SetHardError(wrapper.ClassLoader, ref instr, HardError.IncompatibleClassChangeError, "invokeinterface on non-interface");
2464 }
2465 else if (cpi.GetClassType().IsInterface && invoke != NormalizedByteCode.__invokeinterface && ((invoke != NormalizedByteCode.__invokestatic && invoke != NormalizedByteCode.__invokespecial) || _classFile.MajorVersion < 52))
2466 {
2467 SetHardError(wrapper.ClassLoader, ref instr, HardError.IncompatibleClassChangeError,
2468 _classFile.MajorVersion < 52
2469 ? "interface method must be invoked using invokeinterface"
2470 : "interface method must be invoked using invokeinterface, invokespecial or invokestatic");
2471 }
2472 else
2473 {
2474 var targetMethod = invoke == NormalizedByteCode.__invokespecial ? cpi.GetMethodForInvokespecial() : cpi.GetMethod();
2475 if (targetMethod != null)
2476 {
2477 string errmsg = CheckLoaderConstraints(cpi, targetMethod);
2478 if (errmsg != null)
2479 {
2480 SetHardError(wrapper.ClassLoader, ref instr, HardError.LinkageError, "{0}", errmsg);
2481 }
2482 else if (targetMethod.IsStatic == (invoke == NormalizedByteCode.__invokestatic))
2483 {
2484 if (targetMethod.IsAbstract && invoke == NormalizedByteCode.__invokespecial && (targetMethod.GetMethod() == null || targetMethod.GetMethod().IsAbstract))
2485 {
2486 SetHardError(wrapper.ClassLoader, ref instr, HardError.AbstractMethodError, "{0}.{1}{2}", cpi.Class, cpi.Name, cpi.Signature);
2487 }
2488 else if (invoke == NormalizedByteCode.__invokeinterface && targetMethod.IsPrivate)
2489 {
2490 SetHardError(wrapper.ClassLoader, ref instr, HardError.IncompatibleClassChangeError, "private interface method requires invokespecial, not invokeinterface: method {0}.{1}{2}", cpi.Class, cpi.Name, cpi.Signature);
2491 }
2492 else if (targetMethod.IsAccessibleFrom(cpi.GetClassType(), wrapper, thisType))
2493 {
2494 return;
2495 }
2496 else if (_host != null && targetMethod.IsAccessibleFrom(cpi.GetClassType(), _host, thisType))
2497 {
2498 switch (invoke)
2499 {
2500 case NormalizedByteCode.__invokespecial:
2501 instr.PatchOpCode(NormalizedByteCode.__privileged_invokespecial);
2502 break;
2503 case NormalizedByteCode.__invokestatic:
2504 instr.PatchOpCode(NormalizedByteCode.__privileged_invokestatic);
2505 break;
2506 case NormalizedByteCode.__invokevirtual:
2507 instr.PatchOpCode(NormalizedByteCode.__privileged_invokevirtual);
2508 break;
2509 default:
2510 throw new InvalidOperationException();
2511 }
2512
2513 return;
2514 }
2515 else
2516 {
2517 // NOTE special case for incorrect invocation of Object.clone(), because this could mean
2518 // we're calling clone() on an array
2519 // (bug in javac, see http://developer.java.sun.com/developer/bugParade/bugs/4329886.html)
2520 if (cpi.GetClassType() == _context.JavaBase.TypeOfJavaLangObject && thisType.IsArray && ReferenceEquals(cpi.Name, StringConstants.CLONE))
2521 {
2522 // Patch the instruction, so that the compiler doesn't need to do this test again.
2523 instr.PatchOpCode(NormalizedByteCode.__clone_array);
2524 return;
2525 }
2526 SetHardError(wrapper.ClassLoader, ref instr, HardError.IllegalAccessError, "tried to access method {0}.{1}{2} from class {3}", ToSlash(targetMethod.DeclaringType.Name), cpi.Name, ToSlash(cpi.Signature), ToSlash(wrapper.Name));
2527 }
2528 }
2529 else
2530 {
2531 SetHardError(wrapper.ClassLoader, ref instr, HardError.IncompatibleClassChangeError, "static call to non-static method (or v.v.)");
2532 }
2533 }
2534 else
2535 {
2536 SetHardError(wrapper.ClassLoader, ref instr, HardError.NoSuchMethodError, "{0}.{1}{2}", cpi.Class, cpi.Name, cpi.Signature);
2537 }
2538 }
2539 }
2540
2541 static string ToSlash(string str)
2542 {
2543 return str.Replace('.', '/');
2544 }
2545
2546 void PatchFieldAccess(RuntimeJavaType wrapper, RuntimeJavaMethod mw, ref ClassFile.Method.Instruction instr, StackState stack)
2547 {
2548 var cpi = GetFieldref(instr.Arg1);
2549 bool isStatic;
2550 bool write;
2551 RuntimeJavaType thisType;
2552 switch (instr.NormalizedOpCode)
2553 {
2554 case NormalizedByteCode.__getfield:
2555 isStatic = false;
2556 write = false;
2557 thisType = SigTypeToClassName(stack.PopObjectType(GetFieldref(instr.Arg1).GetClassType()), cpi.GetClassType(), wrapper);
2558 break;
2559 case NormalizedByteCode.__putfield:
2560 stack.PopType(GetFieldref(instr.Arg1).GetFieldType());
2561 isStatic = false;
2562 write = true;
2563 // putfield is allowed to access the unintialized this
2564 if (stack.PeekType() == _context.VerifierJavaTypeFactory.UninitializedThis && wrapper.IsAssignableTo(GetFieldref(instr.Arg1).GetClassType()))
2565 {
2566 thisType = wrapper;
2567 }
2568 else
2569 {
2570 thisType = SigTypeToClassName(stack.PopObjectType(GetFieldref(instr.Arg1).GetClassType()), cpi.GetClassType(), wrapper);
2571 }
2572 break;
2573 case NormalizedByteCode.__getstatic:
2574 isStatic = true;
2575 write = false;
2576 thisType = null;
2577 break;
2578 case NormalizedByteCode.__putstatic:
2579 // special support for when we're being called from IsSideEffectFreeStaticInitializer
2580 if (mw == null)
2581 {
2582 switch (GetFieldref(instr.Arg1).Signature[0])
2583 {
2584 case 'B':
2585 case 'Z':
2586 case 'C':
2587 case 'S':
2588 case 'I':
2589 stack.PopInt();
2590 break;
2591 case 'F':
2592 stack.PopFloat();
2593 break;
2594 case 'D':
2595 stack.PopDouble();
2596 break;
2597 case 'J':
2598 stack.PopLong();
2599 break;
2600 case 'L':
2601 case '[':
2602 if (stack.PopAnyType() != _context.VerifierJavaTypeFactory.Null)
2603 {
2604 throw new VerifyError();
2605 }
2606 break;
2607 default:
2608 throw new InvalidOperationException();
2609 }
2610 }
2611 else
2612 {
2613 stack.PopType(GetFieldref(instr.Arg1).GetFieldType());
2614 }
2615 isStatic = true;
2616 write = true;
2617 thisType = null;
2618 break;
2619 default:
2620 throw new InvalidOperationException();
2621 }
2622
2623 if (mw == null)
2624 {
2625 // We're being called from IsSideEffectFreeStaticInitializer,
2626 // no further checks are possible (nor needed).
2627 }
2628 else if (cpi.GetClassType().IsUnloadable)
2629 {
2630 if (wrapper.ClassLoader.DisableDynamicBinding)
2631 {
2632 SetHardError(wrapper.ClassLoader, ref instr, HardError.NoClassDefFoundError, "{0}", cpi.GetClassType().Name);
2633 }
2634 else
2635 {
2636 switch (instr.NormalizedOpCode)
2637 {
2638 case NormalizedByteCode.__getstatic:
2639 instr.PatchOpCode(NormalizedByteCode.__dynamic_getstatic);
2640 break;
2641 case NormalizedByteCode.__putstatic:
2642 instr.PatchOpCode(NormalizedByteCode.__dynamic_putstatic);
2643 break;
2644 case NormalizedByteCode.__getfield:
2645 instr.PatchOpCode(NormalizedByteCode.__dynamic_getfield);
2646 break;
2647 case NormalizedByteCode.__putfield:
2648 instr.PatchOpCode(NormalizedByteCode.__dynamic_putfield);
2649 break;
2650 default:
2651 throw new InvalidOperationException();
2652 }
2653 }
2654 return;
2655 }
2656 else
2657 {
2658 var field = cpi.GetField();
2659 if (field == null)
2660 {
2661 SetHardError(wrapper.ClassLoader, ref instr, HardError.NoSuchFieldError, "{0}.{1}", cpi.Class, cpi.Name);
2662 return;
2663 }
2664 if (false && cpi.GetFieldType() != field.FieldTypeWrapper && !cpi.GetFieldType().IsUnloadable & !field.FieldTypeWrapper.IsUnloadable)
2665 {
2666#if IMPORTER
2667 StaticCompiler.LinkageError("Field \"{2}.{3}\" is of type \"{0}\" instead of type \"{1}\" as expected by \"{4}\"", field.FieldTypeWrapper, cpi.GetFieldType(), cpi.GetClassType().Name, cpi.Name, wrapper.Name);
2668#endif
2669 SetHardError(wrapper.ClassLoader, ref instr, HardError.LinkageError, "Loader constraints violated: {0}.{1}", field.DeclaringType.Name, field.Name);
2670 return;
2671 }
2672 if (field.IsStatic != isStatic)
2673 {
2674 SetHardError(wrapper.ClassLoader, ref instr, HardError.IncompatibleClassChangeError, "Static field access to non-static field (or v.v.)");
2675 return;
2676 }
2677 if (!field.IsAccessibleFrom(cpi.GetClassType(), wrapper, thisType))
2678 {
2679 SetHardError(wrapper.ClassLoader, ref instr, HardError.IllegalAccessError, "Try to access field {0}.{1} from class {2}", field.DeclaringType.Name, field.Name, wrapper.Name);
2680 return;
2681 }
2682 // are we trying to mutate a final field? (they are read-only from outside of the defining class)
2683 if (write && field.IsFinal
2684 && ((isStatic ? wrapper != cpi.GetClassType() : wrapper != thisType) || (wrapper.ClassLoader.StrictFinalFieldSemantics && (isStatic ? (mw != null && mw.Name != "<clinit>") : (mw == null || mw.Name != "<init>")))))
2685 {
2686 SetHardError(wrapper.ClassLoader, ref instr, HardError.IllegalAccessError, "Field {0}.{1} is final", field.DeclaringType.Name, field.Name);
2687 return;
2688 }
2689 }
2690 }
2691
2692 // TODO this method should have a better name
2693 RuntimeJavaType SigTypeToClassName(RuntimeJavaType type, RuntimeJavaType nullType, RuntimeJavaType wrapper)
2694 {
2695 if (type == _context.VerifierJavaTypeFactory.UninitializedThis)
2696 {
2697 return wrapper;
2698 }
2699 else if (RuntimeVerifierJavaType.IsNew(type))
2700 {
2701 return ((RuntimeVerifierJavaType)type).UnderlyingType;
2702 }
2703 else if (type == _context.VerifierJavaTypeFactory.Null)
2704 {
2705 return nullType;
2706 }
2707 else
2708 {
2709 return type;
2710 }
2711 }
2712
2713 int AllocErrorMessage(string message)
2714 {
2715 _errorMessages ??= new List<string>();
2716 int index = _errorMessages.Count;
2717 _errorMessages.Add(message);
2718 return index;
2719 }
2720
2721 string CheckLoaderConstraints(ClassFile.ConstantPoolItemMI cpi, RuntimeJavaMethod mw)
2722 {
2723#if NETFRAMEWORK
2724 if (cpi.GetRetType() != mw.ReturnType && !cpi.GetRetType().IsUnloadable && !mw.ReturnType.IsUnloadable)
2725#else
2726 if (cpi.GetRetType() != mw.ReturnType && cpi.GetRetType().Name != mw.ReturnType.Name && !cpi.GetRetType().IsUnloadable && !mw.ReturnType.IsUnloadable)
2727#endif
2728 {
2729#if IMPORTER
2730 StaticCompiler.LinkageError("Method \"{2}.{3}{4}\" has a return type \"{0}\" instead of type \"{1}\" as expected by \"{5}\"", mw.ReturnType, cpi.GetRetType(), cpi.GetClassType().Name, cpi.Name, cpi.Signature, _classFile.Name);
2731#endif
2732 return "Loader constraints violated (return type): " + mw.DeclaringType.Name + "." + mw.Name + mw.Signature;
2733 }
2734
2735 var here = cpi.GetArgTypes();
2736 var there = mw.GetParameters();
2737 for (int i = 0; i < here.Length; i++)
2738 {
2739#if NETFRAMEWORK
2740 if (here[i] != there[i] && !here[i].IsUnloadable && !there[i].IsUnloadable)
2741#else
2742 if (here[i] != there[i] && here[i].Name != there[i].Name && !here[i].IsUnloadable && !there[i].IsUnloadable)
2743#endif
2744 {
2745#if IMPORTER
2746 StaticCompiler.LinkageError("Method \"{2}.{3}{4}\" has a argument type \"{0}\" instead of type \"{1}\" as expected by \"{5}\"", there[i], here[i], cpi.GetClassType().Name, cpi.Name, cpi.Signature, _classFile.Name);
2747#endif
2748 return "Loader constraints violated (arg " + i + "): " + mw.DeclaringType.Name + "." + mw.Name + mw.Signature;
2749 }
2750 }
2751
2752 return null;
2753 }
2754
2755 ClassFile.ConstantPoolItemInvokeDynamic GetInvokeDynamic(int index)
2756 {
2757 try
2758 {
2759 var item = _classFile.GetInvokeDynamic(new InvokeDynamicConstantHandle(checked((ushort)index)));
2760 if (item != null)
2761 {
2762 return item;
2763 }
2764 }
2765 catch (OverflowException)
2766 {
2767 // constant pool index out of range
2768 }
2769 catch (InvalidCastException)
2770 {
2771 // constant pool index not of proper type
2772 }
2773 catch (IndexOutOfRangeException)
2774 {
2775 // constant pool index out of range
2776 }
2777 catch (InvalidOperationException)
2778 {
2779 // specified constant pool entry doesn't contain a constant
2780 }
2781 catch (NullReferenceException)
2782 {
2783 // specified constant pool entry is empty (entry 0 or the filler following a wide entry)
2784 }
2785
2786 throw new VerifyError("Illegal constant pool index");
2787 }
2788
2789 ClassFile.ConstantPoolItemMI GetMethodref(int index)
2790 {
2791 try
2792 {
2793 var item = _classFile.GetMethodref(new MethodrefConstantHandle(checked((ushort)index)));
2794 if (item != null)
2795 return item;
2796 }
2797 catch (OverflowException)
2798 {
2799 // constant pool index out of range
2800 }
2801 catch (InvalidCastException)
2802 {
2803 // constant pool index not of proper type
2804 }
2805 catch (IndexOutOfRangeException)
2806 {
2807 // constant pool index out of range
2808 }
2809 catch (InvalidOperationException)
2810 {
2811 // specified constant pool entry doesn't contain a constant
2812 }
2813 catch (NullReferenceException)
2814 {
2815 // specified constant pool entry is empty (entry 0 or the filler following a wide entry)
2816 }
2817
2818 throw new VerifyError("Illegal constant pool index");
2819 }
2820
2821 ClassFile.ConstantPoolItemFieldref GetFieldref(int index)
2822 {
2823 try
2824 {
2825 var item = _classFile.GetFieldref(new FieldrefConstantHandle(checked((ushort)index)));
2826 if (item != null)
2827 return item;
2828 }
2829 catch (OverflowException)
2830 {
2831 // constant pool index out of range
2832 }
2833 catch (InvalidCastException)
2834 {
2835 // constant pool index not of proper type
2836 }
2837 catch (IndexOutOfRangeException)
2838 {
2839 // constant pool index out of range
2840 }
2841 catch (InvalidOperationException)
2842 {
2843 // specified constant pool entry doesn't contain a constant
2844 }
2845 catch (NullReferenceException)
2846 {
2847 // specified constant pool entry is empty (entry 0 or the filler following a wide entry)
2848 }
2849
2850 throw new VerifyError("Illegal constant pool index");
2851 }
2852
2853 ClassFile.ConstantType GetConstantPoolConstantType(int slot)
2854 {
2855 try
2856 {
2857 return _classFile.GetConstantPoolConstantType(new ConstantHandle(ConstantKind.Unknown, checked((ushort)slot)));
2858 }
2859 catch (OverflowException)
2860 {
2861 // constant pool index out of range
2862 }
2863 catch (IndexOutOfRangeException)
2864 {
2865 // constant pool index out of range
2866 }
2867 catch (InvalidOperationException)
2868 {
2869 // specified constant pool entry doesn't contain a constant
2870 }
2871 catch (NullReferenceException)
2872 {
2873 // specified constant pool entry is empty (entry 0 or the filler following a wide entry)
2874 }
2875
2876 throw new VerifyError("Illegal constant pool index");
2877 }
2878
2879 RuntimeJavaType GetConstantPoolClassType(int slot)
2880 {
2881 try
2882 {
2883 return _classFile.GetConstantPoolClassType(new ClassConstantHandle(checked((ushort)slot)));
2884 }
2885 catch (OverflowException)
2886 {
2887 // constant pool index out of range
2888 }
2889 catch (InvalidCastException)
2890 {
2891 // constant pool index out of range
2892 }
2893 catch (IndexOutOfRangeException)
2894 {
2895 // specified constant pool entry doesn't contain a constant
2896 }
2897 catch (NullReferenceException)
2898 {
2899 // specified constant pool entry is empty (entry 0 or the filler following a wide entry)
2900 }
2901
2902 throw new VerifyError("Illegal constant pool index");
2903 }
2904
2905 RuntimeJavaType GetConstantPoolClassType(ClassConstantHandle handle)
2906 {
2907 return GetConstantPoolClassType(handle.Slot);
2908 }
2909
2910 internal void ClearFaultBlockException(int instructionIndex)
2911 {
2912 Debug.Assert(_state[instructionIndex].GetStackHeight() == 1);
2913 _state[instructionIndex].ClearFaultBlockException();
2914 }
2915
2916 }
2917
2918}
global::java.lang.invoke.LambdaForm.Name Name
IKVM.Runtime.ClassFile.Method.InstructionFlags InstructionFlags
Definition atomic.cs:37
RuntimeContext Context
Gets the RuntimeContext that hosts this method analyzer.
Runtime support for a class loader.
Maintains services relevant to an instane of the IKVM runtime.
RuntimePrimitiveJavaTypeFactory PrimitiveJavaTypeFactory
Gets the RuntimePrimitiveJavaTypeFactory associated with this instance of the runtime.
RuntimeVerifierJavaTypeFactory VerifierJavaTypeFactory
Gets the RuntimeVerifierJavaTypeFactory associated with this instance of the runtime.
IKVM.Runtime.ClassFile.Method.ExceptionTableEntry ExceptionTableEntry
Definition compiler.cs:42