IllegalAccessError and proxy - java

I'm translating this code to Clojure. As you can see, I have to extend the class ArthurFrame, yet I'm getting an IllegalAccessError everytime I use (proxy [ArthurFrame] [] ...).
Any idea why? Here is the class's source.
Thanks!
EDIT: Here is the full error stack for running (proxy [ArthurFrame] []) on the REPL.
EDIT 2: Actually it seems that even instantiating the class yields an error. Here is the output from (ArthurFrame. wid):
tried to access class com.trolltech.demos.ArthurFrame from class user$eval__2205
[Thrown class java.lang.IllegalAccessError]
Restarts:
0: [ABORT] Return to SLIME's top level.
Backtrace:
0: user$eval__2205.invoke(NO_SOURCE_FILE:1)
1: clojure.lang.Compiler.eval(Compiler.java:4642)
2: clojure.core$eval__5254.invoke(core.clj:2031)
3: swank.commands.basic$eval_region__907.invoke(basic.clj:40)
4: swank.commands.basic$eval_region__907.invoke(basic.clj:31)
5: swank.commands.basic$eval__927$listener_eval__929.invoke(basic.clj:54)
6: clojure.lang.Var.invoke(Var.java:359)
7: user$eval__2202.invoke(NO_SOURCE_FILE)
8: clojure.lang.Compiler.eval(Compiler.java:4642)
9: clojure.core$eval__5254.invoke(core.clj:2031)
10: swank.core$eval_in_emacs_package__455.invoke(core.clj:59)
11: swank.core$eval_for_emacs__533.invoke(core.clj:128)
12: clojure.lang.Var.invoke(Var.java:367)
13: clojure.lang.AFn.applyToHelper(AFn.java:179)
14: clojure.lang.Var.applyTo(Var.java:476)
15: clojure.core$apply__4379.invoke(core.clj:434)
16: swank.core$eval_from_control__458.invoke(core.clj:66)
17: swank.core$eval_loop__461.invoke(core.clj:71)
18: swank.core$spawn_repl_thread__595$fn__627$fn__629.invoke(core.clj:183)
19: clojure.lang.AFn.applyToHelper(AFn.java:171)
20: clojure.lang.AFn.applyTo(AFn.java:164)
21: clojure.core$apply__4379.invoke(core.clj:434)
22: swank.core$spawn_repl_thread__595$fn__627.doInvoke(core.clj:180)
23: clojure.lang.RestFn.invoke(RestFn.java:402)
24: clojure.lang.AFn.run(AFn.java:37)
25: java.lang.Thread.run(Thread.java:619)

The problem is that ArthurFrame visibility is package not public so the proxy can't access it since the proxy doesn't belong to the package com.trolltech.demos. You have to make ArthurFrame public.

The most likely cause of your problem is that something has not been recompiled. Here's the javadoc description of the IllegalAccessError exception:
Thrown if an application attempts to access or modify a field, or to call a method that it does not have access to.
Normally, this error is caught by the compiler; this error can only occur at run time if the definition of a class has incompatibly changed.
To be more specific, this normally happens when you have classes A and B, where B depends on some members of A. Then you do something like this:
You compile A, then B.
Make an incompatible change to A and recompile it without recompiling B. In this case, the change would involve reducing the visibility of some member of A that B uses so that the member should no longer be visible to B.
Run an application that uses A and B and and you will get an IllegalAccessError.
EDIT
The proxy class that is trying to do the accessing looks like it must have been generated by the Clojure compiler. So maybe there is a Clojure compiler bug ... or maybe you changed the visibility of ArthurFrame after you ran the Clojure compiler. Either way, one possible fix is to change the visibility of the ArthurFrame to public.

Related

Byte code difference in #-values after change Ant to Maven

I changed the compilation of a project from Ant to Maven. We still use Java 1.7.0_80. The ByteCode of all classes stayed the same except for a Comparator class. I disassembled the class with javap -c. The only differences are in #-values, e.g.:
before: invokevirtual #2
after: invokevirtual #29
What do these #-values mean? Is this a functional difference or just a difference in naming?
EDIT: The first method in Bytecode before and after:
public de.continentale.kvrl.model.comp.KvRLRechBetragEuroComparator();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public de.continentale.kvrl.model.comp.KvRLRechBetragEuroComparator();
Code:
0: aload_0
1: invokespecial #21 // Method java/lang/Object."<init>":()V
4: return
The #29 refers to an index in the constant table that describes the method being invoked. It would have been better if you had pasted the full output of javap -c, because the comment at the end of the line indicates the actual method descriptor.
Example of javap -c on an invocation of an instance method test in class Z:
7: invokevirtual #19 // Method snippet/Z.test:()V
You can ignore the # number, as long as the method descriptor in the comment is the same.
Why
As to the "why" question, it is very likely caused by the debug in the ant and maven compiler task/plugin settings.
By default, the maven-compiler-plugin compiles with the debug configuration option set to true, which will attach among other the name of the source file, and thing like line number tables and local variable names.
https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html
By default, the ant java compiler has this set to false.
https://ant.apache.org/manual/Tasks/javac.html
To compare and check, set the debug parameter of the Ant Javac task to true and see if that makes the generated .class files the same.

The type java.lang.CharSequence cannot be resolved. TomCat

Hi Guys I'm not new to java web edition. I have a websystem which my login page (index.jsp) runs fine. When a login is successful that's where the problem comes, i get this error.
org.apache.jasper.JasperException: Unable to compile class for JSP:
An error occurred at line: 12 in the jsp file: /MSS_portal_frameset.jsp
The type java.lang.CharSequence cannot be resolved. It is indirectly referenced from required .class files
9: StringBuffer result = new StringBuffer();
10:
11: while ((e = str.indexOf(pattern, s)) >= 0) {
12: result.append(str.substring(s, e));
13: result.append(replace);
14: s = e+pattern.length();
15: }
Stacktrace:
org.apache.jasper.compiler.DefaultErrorHandler.javacError(DefaultErrorHandler.java:85)
org.apache.jasper.compiler.ErrorDispatcher.javacError(ErrorDispatcher.java:330)
org.apache.jasper.compiler.JDTCompiler.generateClass(JDTCompiler.java:435)
org.apache.jasper.compiler.Compiler.compile(Compiler.java:298)
org.apache.jasper.compiler.Compiler.compile(Compiler.java:277)
org.apache.jasper.compiler.Compiler.compile(Compiler.java:265)
org.apache.jasper.JspCompilationContext.compile(JspCompilationContext.java:564)
org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:299)
org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:315)
org.apache.jasper.servlet.JspServlet.service(JspServlet.java:265)
javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
That section of the code is inside scriptlets <% %>.
java.lang.CharSequence was added in Java 1.4, so if it can't find it, you're either running a very old Java, or you classpath is really messed up.
You should replace use of StringBuffer with StringBuilder, which was added in Java 5. Use of StringBuffer is obsolete.
Quoting javadoc:
As of release JDK 5, this class (StringBuffer) has been supplemented with an equivalent class designed for use by a single thread, StringBuilder. The StringBuilder class should generally be used in preference to this one, as it supports all of the same operations but it is faster, as it performs no synchronization.

JVM Verify Error 'Illegal type at constant pool'

I am currently writing my own compiler and I am trying to compile the following code:
List[String] list = List("a", "b", "c", "d")
list stream map((String s) => s.toUpperCase())
System out println list
The compiler has no problem parsing, linking or compiling the code, but when it comes to executing it, the JVM throws the following error:
java.lang.VerifyError: Illegal type at constant pool entry 40 in class dyvil.test.Main
Exception Details:
Location:
dyvil/test/Main.main([Ljava/lang/String;)V #29: invokevirtual
Reason:
Constant pool index 40 is invalid
Bytecode:
...
I tried to use javap to find the problem, and this is the instruction #29:
29: invokevirtual #40 // InterfaceMethod java/util/Collection.stream:()Ljava/util/stream/Stream;
And the entry in the Constant Pool (also using javap):
#37 = Utf8 stream
#38 = Utf8 ()Ljava/util/stream/Stream;
#39 = NameAndType #37:#38 // stream:()Ljava/util/stream/Stream;
#40 = InterfaceMethodref #36.#39 // java/util/Collection.stream:()Ljava/util/stream/Stream;
When opening the class with the Eclipse Class File Viewer, the line where #29 should be reads:
Class Format Exception
and all following instructions are not shown anymore (except for Locals, ...). However, the ASM Bytecode Plugin writes
INVOKEVIRTUAL java/util/Collection.stream ()Ljava/util/stream/Stream;
at that line, which appears to be valid. What am I doing wrong / missing here?
I figured out my mistake. The error lies here:
invokevirtual #40 // InterfaceMethod
^^^^^^^ ^^^^^^^^^
I am using invokevirtual on an interface method, which is generally not a good idea. However I think that the error thrown by the verifier should be a bit more clear about what is actually wrong.
With ASM you will see this error because of an incorrect isInterface flag. The argument isInterface of visitMethodInsn refers to the target/owner/destination and not the current context.
i.o.w when using INVOKEVIRTUAL, isInterface is false.
see issue here for more details.

how to figure out which inner anonymous class indicated by a class name?

I used MAT tool in Eclipse to investigate a memory leak issue. I found that the leak was caused by an anonymous inner class instance in my app. The class name displayed in MAT is com.mycompany.myproduct.MyActivity$3. There are many anonymous inner classes defined in MyActivity.java. How do I know the which inner class com.mycompany.myproduct.MyActivity$3 indicates?
Thanks.
On the Oracle compiler, they're numbered in order of occurrence in the class. I'm not sure if that's part of any specification or consistent with other implementations.
You could decompile the class--JD-GUI is a great tool for that--and then you'll see what you want to know. You could even just go with basic disassembly using javap -c. It'll give you a rough idea of where the classes occur.
Hint: debugger somehow knows which classes are where. So you can, too!
Try using javap on this example with two anonymous classes:
import java.util.*;
public class Test {
public static void main(String [] args) {
Map m = new HashMap(){{System.out.print(1);}};
Map m1 = new HashMap(){{System.out.print(2);}};
}
}
Compile it and run javap -c -l:
$ javap -c -l Test
Compiled from "Test.java"
public class Test extends java.lang.Object{
public Test();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
public static void main(java.lang.String[]);
Code:
0: new #2; //class Test$1
3: dup
4: invokespecial #3; //Method Test$1."<init>":()V
7: astore_1
8: new #4; //class Test$2
11: dup
12: invokespecial #5; //Method Test$2."<init>":()V
15: astore_2
16: return
LineNumberTable:
line 5: 0
line 7: 8
line 9: 16
}
As you can see, the first class got name Test$1, the second one—Test$2. Hope tat helps.
For more specific information, decompile the specific classes you're interested in, e.g. javap -c -l Test\$2. Pay attention to line numbers: they will give you a hint on where in the source file the class was defined.
When you compile your code securely you have MyActivity$1.class, MyActivity$2.class, MyActivity$3.class, and so on. You can use a java decompiler (over your .class) in order to identify the anonymus class that is throwing the exception.
The whole point of anonymous classes is that they are just that. As you have found its not easy to figure out which one its come from. Usually the numbering starts at one, so my guess is that it will be the third declared anonymous class that is your problem.
In this situation, you maybe better off to refactor your code to not have any anonymous classes. Otherwise I suggest attaching the debugger and stepping the code.

Tricky try-catch java code

public class Strange1 {
public static void main(String[] args) {
try {
Missing m = new Missing();
} catch (java.lang.NoClassDefFoundError ex) {
System.out.println("Got it!");
}
}
}
public class Strange2 {
public static void main(String[] args) {
Missing m;
try {
m = new Missing();
} catch (java.lang.NoClassDefFoundError ex) {
System.out.println("Got it!");
}
}
}
class Missing {
Missing() { }
}
If you run Strange1 and Strange2 after deleting Missing.class, Strange1 will throw NoClassDefFoundError; but Strange2 will print Got it!
Can anyone explain that? Thanks.
updated:
java bytecode for Strange1 :
0 new info.liuxuan.test.Missing [16]
3 dup
4 invokespecial info.liuxuan.test.Missing() [18]
7 astore_1 [m]
8 goto 20
11 astore_1 [ex]
12 getstatic java.lang.System.out : java.io.PrintStream [19]
15 ldc <String "Got it!"> [25]
17 invokevirtual java.io.PrintStream.println(java.lang.String) : void [27]
20 return
Exception Table:
[pc: 0, pc: 8] -> 11 when : java.lang.NoClassDefFoundError
Line numbers:
[pc: 0, line: 14]
[pc: 11, line: 15]
[pc: 12, line: 16]
[pc: 20, line: 18]
Local variable table:
[pc: 0, pc: 21] local: args index: 0 type: java.lang.String[]
[pc: 8, pc: 11] local: m index: 1 type: info.liuxuan.test.Missing
[pc: 12, pc: 20] local: ex index: 1 type: java.lang.NoClassDefFoundError
java bytecode for Strange2 :
0 new info.liuxuan.test.Missing [16]
3 dup
4 invokespecial info.liuxuan.test.Missing() [18]
7 astore_1 [m]
8 goto 20
11 astore_2 [ex]
12 getstatic java.lang.System.out : java.io.PrintStream [19]
15 ldc <String "Got it!"> [25]
17 invokevirtual java.io.PrintStream.println(java.lang.String) : void [27]
20 return
Exception Table:
[pc: 0, pc: 8] -> 11 when : java.lang.NoClassDefFoundError
Line numbers:
[pc: 0, line: 15]
[pc: 11, line: 16]
[pc: 12, line: 17]
[pc: 20, line: 19]
Local variable table:
[pc: 0, pc: 21] local: args index: 0 type: java.lang.String[]
[pc: 8, pc: 11] local: m index: 1 type: info.liuxuan.test.Missing
[pc: 12, pc: 20] local: ex index: 2 type: java.lang.NoClassDefFoundError
There is only one place is different:
11 astore_1 [ex]
and
11 astore_2 [ex]
updated again:
Everyone can try it in eclipse.
Prior to saying anything, i doub't this code won't even compile. because when compiler cannot find a class (Since its deleted). may be you are getting an error when trying to compile it using javac command. if thats the case its pretty normal and in no way its weird.
and let me add an another point.. check your imports, to contain Missing class. if it is there then remove it. and tell us whats happening.
I created two java files. Strange1.java contained classes Strange1 and Missing. Strange2.java contained Strange2 class. I removed Missing.class.
I got "Got it!" from both.
Please see the following details:
manohar#manohar-natty:~$ java -version
java version "1.6.0_25"
Java(TM) SE Runtime Environment (build 1.6.0_25-b06)
Java HotSpot(TM) Server VM (build 20.0-b11, mixed mode)
manohar#manohar-natty:~$ gedit Strange1.java
manohar#manohar-natty:~$ gedit Strange2.java
manohar#manohar-natty:~$ javac Strange1.java
manohar#manohar-natty:~$ javac Strange2.java
manohar#manohar-natty:~$ java Strange1
manohar#manohar-natty:~$ java Strange2
manohar#manohar-natty:~$ rm Missing.class
manohar#manohar-natty:~$ java Strange1
Got it!
manohar#manohar-natty:~$ java Strange2
Got it!
I executed it in Ubuntu 11.04 linux machine.
So it might be the java's version that you are using.
NoClassDefFoundError is thrown whenever the first reference(declaring or creating an instance) to the missing class is made. Now, throwing an error or catching it depends on whether you use try-catch block for your first reference or not.
Output
The behavior of both programs depends on the version of javac used to compile them and not the version of java used to run the compiled classes. However, it's easier to use the same javac and java versions.
We'll be using J2SE 5.0 and Java SE 6 because those are the earliest versions where the programs' behavior deviates.
With build 1.5.0_22-b03:
$ jdk1.5.0_22/bin/javac {Strange1,Strange2,Missing}.java
$ rm Missing.class
$ jdk1.5.0_22/bin/java Strange1
Exception in thread "main" java.lang.NoClassDefFoundError: Missing
$ jdk1.5.0_22/bin/java Strange2
Got it!
$ jdk1.5.0_22/bin/javap -c Strange1
Compiled from "Strange1.java"
public class Strange1 extends java.lang.Object{
public Strange1();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2; //class Missing
3: dup
4: invokespecial #3; //Method Missing."<init>":()V
7: astore_1
8: goto 20
11: astore_1
12: getstatic #5; //Field java/lang/System.out:Ljava/io/PrintStream;
15: ldc #6; //String Got it!
17: invokevirtual #7; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
20: return
Exception table:
from to target type
0 8 11 Class java/lang/NoClassDefFoundError
}
Compiled from "Strange2.java"
public class Strange2 extends java.lang.Object{
public Strange2();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2; //class Missing
3: dup
4: invokespecial #3; //Method Missing."<init>":()V
7: astore_1
8: goto 20
11: astore_2
12: getstatic #5; //Field java/lang/System.out:Ljava/io/PrintStream;
15: ldc #6; //String Got it!
17: invokevirtual #7; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
20: return
Exception table:
from to target type
0 8 11 Class java/lang/NoClassDefFoundError
}
With build 1.6.0_45-b06:
$ jdk1.6.0_45/bin/javac {Strange1,Strange2,Missing}.java
$ rm Missing.class
$ jdk1.6.0_45/bin/java Strange1
Got it!
$ jdk1.6.0_45/bin/java Strange2
Got it!
$ jdk1.6.0_45/bin/javap -c Strange1
<Same output as the corresponding command for J2SE 5.0>
$ jdk1.6.0_45/bin/javap -c Strange2
<Same output as the corresponding command for J2SE 5.0>
Analysis
The bytecode of Strange1 and Strange2 is nearly identical, except for the mapping of the catch parameter ex to a VM local variable. Strange1 stores it in VM variable 1, i.e., 11: astore_1. Strange2 stores it in VM variable 2, i.e., 11: astore_2.
In both classes, the local variable m is stored in VM variable 1. Both versions of main also have a merge point where the flow of control from two different code paths converge. The merge point is 20: return. It can be reached either by completing the try block normally, i.e., 8: goto 20, or by completing the catch block and falling through from instruction 17.
The existence of the merge point causes an exception during the verification of class Strange1, but not class Strange2 in J2SE 5.0.
JLS, Java SE 6 Edition - Chapter 12 - Execution specifies activities that occur during execution of a program:
The Java Virtual Machine starts up by loading a specified class and then invoking the method main in this specified class. Section §12.1 outlines the loading, linking, and initialization steps involved in executing main, as an introduction to the concepts in this chapter. Further sections specify the details of loading (§12.2), linking (§12.3), and initialization (§12.4).
JLS, Java SE 6 Edition - Section 12.3 - Linking of Classes and Interfaces specifies that the first activity that is involved in linking is verification.
JVMS, Java SE 7 Edition - Section 4.10 - Verification of class Files specifies the two strategies that a VM may use for verification:
There are two strategies that Java Virtual Machine implementations may
use for verification:
Verification by type checking must be used to verify class files whose version number is greater than or equal to 50.0.
Verification by type inference must be supported by all Java Virtual Machine implementations, except those conforming to the Java
ME CLDC and Java Card profiles, in order to verify class files whose
version number is less than 50.0.
Verification on Java Virtual Machine implementations supporting the Java ME CLDC and Java Card profiles is governed by their
respective specifications.
(Verification by type checking was added to JVMS, Second Edition for Java SE 6, and JVMS, Java SE 7 Edition incorporates these changes in a more accessible way.)
Verification by type inference (for class files whose version number is less than 50.0, i.e., Java SE 6)
To merge two local variable array states, corresponding pairs of local
variables are compared. If the two types are not identical, then
unless both contain reference values, the verifier records that the
local variable contains an unusable value. If both of the pair of
local variables contain reference values, the merged state contains a
reference to an instance of the first common superclass of the two
types.
JVMS, Second Edition - Section 4.9.2 - The Bytecode Verifier
When instruction 20 is reached from instruction 8 in Strange1.main, VM variable 1 contains an instance of the class Missing. When reached from instruction 17, it contains an instance of the class NoClassDefFoundError.
Because Missing.class has been deleted, the verifier can't load it to determine the first common superclass, and throws a NoClassDefFoundError. Note that there is no stack trace printed for the uncaught exception because it's thrown during verification, before class initialization and long before main begins execution.
Verification by type checking (for class files whose version number is greater than or equal to 50.0, i.e., Java SE 6)
(I've tried my best to follow the rules as precisely as possible. However, they are complex, dense and new to me. Hence, if you spot any mistakes, please feel free to correct them. If you could summarize the rules, that'd be great too.)
Because of the StackMapTable attribute, it's not necessary for the verifier to compute the first common superclass to merge the two VM variable 1's types as in the other strategy. Also, there's no need to actually load classes Missing, NoClassDefFoundError, or any other classes other than Strange1 for verification thanks to the constant pool and how the rules work.
Hence, there are no exceptions during verification. If you modify the catch block to print out the exception's stack trace, you'll see that the exception is thrown during the execution of Strange1.main with a proper stack trace:
# Modify Strange1.main's catch block to print out the exception's stack trace
$ jdk1.6.0_45/bin/javac {Strange1,Missing}.java
$ rm Missing.class
$ jdk1.6.0_45/bin/java Strange1
java.lang.NoClassDefFoundError: Missing
at Strange1.main(Strange1.java:4)
Caused by: java.lang.ClassNotFoundException: Missing
at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
... 1 more
Got it!
The StackMapTable of Strange1:
$ jdk1.6.0_45/bin/javap -verbose Strange1 | tail
line 10: 20
StackMapTable: number_of_entries = 2
frame_type = 75 /* same_locals_1_stack_item */
stack = [ class java/lang/NoClassDefFoundError ]
frame_type = 8 /* same */
}
Let's go through the rules (as the verifier) to prove that Strange1.main is type safe. Each instruction of Strange1.main is verified to be type safe and satisfies all applicable (exception) handlers:
methodWithCodeIsTypeSafe(Strange1Class, MainMethod) :-
parseCodeAttribute(Strange1Class, MainMethod, FrameSize, MaxStack,
ParsedCode, Handlers, StackMap),
mergeStackMapAndCode(StackMap, ParsedCode, MergedCode),
methodInitialStackFrame(Strange1Class, MainMethod, FrameSize, StackFrame, ReturnType),
Environment = environment(Strange1Class, MainMethod, ReturnType, MergedCode,
MaxStack, Handlers),
handlersAreLegal(Environment),
mergedCodeIsTypeSafe(Environment, MergedCode, StackFrame).
mergedCodeIsTypeSafe(Environment, [instruction(Offset, Parse) | MoreCode],
frame(Locals, OperandStack, Flags)) :-
instructionIsTypeSafe(Parse, Environment, Offset,
frame(Locals, OperandStack, Flags),
NextStackFrame, ExceptionStackFrame),
instructionSatisfiesHandlers(Environment, Offset, ExceptionStackFrame),
mergedCodeIsTypeSafe(Environment, MoreCode, NextStackFrame).
mergedCodeIsTypeSafe(Environment, [stackMap(Offset, MapFrame) | MoreCode],
afterGoto) :-
mergedCodeIsTypeSafe(Environment, MoreCode, MapFrame).
instructionSatisfiesHandlers(Environment, Offset, ExceptionStackFrame) :-
exceptionHandlers(Environment, Handlers),
sublist(isApplicableHandler(Offset), Handlers, ApplicableHandlers),
checklist(instructionSatisfiesHandler(Environment, ExceptionStackFrame),
ApplicableHandlers).
instructionSatisfiesHandler(Environment, StackFrame, Handler) :-
...
/* The stack consists of just the exception. */
StackFrame = frame(Locals, _, Flags),
ExcStackFrame = frame(Locals, [ ExceptionClass ], Flags),
operandStackHasLegalLength(Environment, ExcStackFrame),
targetIsTypeSafe(Environment, ExcStackFrame, Target).
targetIsTypeSafe(Environment, StackFrame, Target) :-
offsetStackFrame(Environment, Target, Frame),
frameIsAssignable(StackFrame, Frame).
Instructions 0-7 are type safe because instructionIsTypeSafe is true and frameIsAssignable(Environment, frame(Locals, [ NoClassDefFoundErrorClass ], Flags), frame(Locals, [ NoClassDefFoundErrorClass ], Flags)) is true for each of them thanks to the first stack map frame for the (exception) handler target at instruction 19 (= 75 - 64):
frame_type = 75 /* same_locals_1_stack_item */
stack = [ class java/lang/NoClassDefFoundError ]
Instructions 12-17 are type safe thanks to the constant pool and the corresponding rules which are not listed (because they don't make up of the StackMapTable attribute).
There are 3 instructions left to prove their type-safety:
8: goto 20 is type safe thanks to the second stack map frame for the target at instruction 20 (= 75 - 64 + 8 + 1 = 19 + 8 + 1), instructing the verifier that the stack frame there is the same as the previous stack frame at instruction 8:
frame_type = 8 /* same */
instructionIsTypeSafe(goto(Target), Environment, _Offset, StackFrame,
afterGoto, ExceptionStackFrame) :-
targetIsTypeSafe(Environment, StackFrame, Target),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
11: astore_1 is type safe because the store is type safe, because it can pop a NoClassDefFoundError that is a subtype of reference off the stack (thanks to the first stack map frame again), and then legally assign that type to the local variable 1, i.e., Locals = [arrayOf(String), class(Missing, Lm)] -> NewLocals = [arrayOf(String), class(NoClassDefFoundError, Ln)].
An astore instruction with operand Index is type safe and yields an outgoing type state NextStackFrame, if a store instruction with operand Index and type reference is type safe and yields an outgoing type state NextStackFrame.
instructionIsTypeSafe(astore(Index), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
storeIsTypeSafe(Environment, Index, reference, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
More precisely, the store is type safe if one can pop a type ActualType that "matches" Type (that is, is a subtype of Type) off the operand stack (§4.10.1.4), and then legally assign that type the local variable LIndex.
storeIsTypeSafe(_Environment, Index, Type,
frame(Locals, OperandStack, Flags),
frame(NextLocals, NextOperandStack, Flags)) :-
popMatchingType(OperandStack, Type, NextOperandStack, ActualType),
modifyLocalVariable(Index, ActualType, Locals, NextLocals).
20: return is type safe because Strange1.main declares a void return type, and the enclosing method is not an <init> method:
A return instruction is type safe if the enclosing method declares a void return type, and either:
- The enclosing method is not an <init> method, or
- this has already been completely initialized at the point where the instruction occurs.
instructionIsTypeSafe(return, Environment, _Offset, StackFrame,
afterGoto, ExceptionStackFrame) :-
thisMethodReturnType(Environment, void),
StackFrame = frame(_Locals, _OperandStack, Flags),
notMember(flagThisUninit, Flags),
exceptionStackFrame(StackFrame, ExceptionStackFrame).

Categories

Resources