diff --git a/eval4j/.gitignore b/eval4j/.gitignore
new file mode 100644
index 00000000000..673af156045
--- /dev/null
+++ b/eval4j/.gitignore
@@ -0,0 +1,3 @@
+out
+.idea/workspace.xml
+.idea/uiDesigner.xml
diff --git a/eval4j/.idea/.name b/eval4j/.idea/.name
new file mode 100644
index 00000000000..731b3d6f54e
--- /dev/null
+++ b/eval4j/.idea/.name
@@ -0,0 +1 @@
+eval4j
\ No newline at end of file
diff --git a/eval4j/.idea/compiler.xml b/eval4j/.idea/compiler.xml
new file mode 100644
index 00000000000..217af471a9e
--- /dev/null
+++ b/eval4j/.idea/compiler.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/eval4j/.idea/copyright/profiles_settings.xml b/eval4j/.idea/copyright/profiles_settings.xml
new file mode 100644
index 00000000000..3572571ad83
--- /dev/null
+++ b/eval4j/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/eval4j/.idea/encodings.xml b/eval4j/.idea/encodings.xml
new file mode 100644
index 00000000000..e206d70d859
--- /dev/null
+++ b/eval4j/.idea/encodings.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/eval4j/.idea/kannotator.xml b/eval4j/.idea/kannotator.xml
new file mode 100644
index 00000000000..48c712c3946
--- /dev/null
+++ b/eval4j/.idea/kannotator.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/eval4j/.idea/libraries/KotlinJavaRuntime.xml b/eval4j/.idea/libraries/KotlinJavaRuntime.xml
new file mode 100644
index 00000000000..0b98ab4814d
--- /dev/null
+++ b/eval4j/.idea/libraries/KotlinJavaRuntime.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/eval4j/.idea/libraries/junit_junit_4_11.xml b/eval4j/.idea/libraries/junit_junit_4_11.xml
new file mode 100644
index 00000000000..2895188a94b
--- /dev/null
+++ b/eval4j/.idea/libraries/junit_junit_4_11.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/eval4j/.idea/libraries/org_ow2_asm_asm_debug_all_4_1.xml b/eval4j/.idea/libraries/org_ow2_asm_asm_debug_all_4_1.xml
new file mode 100644
index 00000000000..7d9e29eb73c
--- /dev/null
+++ b/eval4j/.idea/libraries/org_ow2_asm_asm_debug_all_4_1.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/eval4j/.idea/misc.xml b/eval4j/.idea/misc.xml
new file mode 100644
index 00000000000..97320410eec
--- /dev/null
+++ b/eval4j/.idea/misc.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/eval4j/.idea/modules.xml b/eval4j/.idea/modules.xml
new file mode 100644
index 00000000000..313c2996166
--- /dev/null
+++ b/eval4j/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/eval4j/.idea/runConfigurations/Reflection_Test.xml b/eval4j/.idea/runConfigurations/Reflection_Test.xml
new file mode 100644
index 00000000000..71cd9c632f9
--- /dev/null
+++ b/eval4j/.idea/runConfigurations/Reflection_Test.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/eval4j/.idea/scopes/scope_settings.xml b/eval4j/.idea/scopes/scope_settings.xml
new file mode 100644
index 00000000000..922003b8433
--- /dev/null
+++ b/eval4j/.idea/scopes/scope_settings.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/eval4j/.idea/vcs.xml b/eval4j/.idea/vcs.xml
new file mode 100644
index 00000000000..9d32e507a98
--- /dev/null
+++ b/eval4j/.idea/vcs.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/eval4j/ReadMe.md b/eval4j/ReadMe.md
new file mode 100644
index 00000000000..9fbab56cb9b
--- /dev/null
+++ b/eval4j/ReadMe.md
@@ -0,0 +1,2 @@
+eval4j is a Java byte code interpreter written in Java.
+Its primary use case is implementing expression evaluation in debuggers.
\ No newline at end of file
diff --git a/eval4j/annotations/jdi/com/sun/jdi/annotations.xml b/eval4j/annotations/jdi/com/sun/jdi/annotations.xml
new file mode 100644
index 00000000000..5a32c14d723
--- /dev/null
+++ b/eval4j/annotations/jdi/com/sun/jdi/annotations.xml
@@ -0,0 +1,56 @@
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
\ No newline at end of file
diff --git a/eval4j/annotations/jdi/com/sun/jdi/connect/annotations.xml b/eval4j/annotations/jdi/com/sun/jdi/connect/annotations.xml
new file mode 100644
index 00000000000..7aaa5401216
--- /dev/null
+++ b/eval4j/annotations/jdi/com/sun/jdi/connect/annotations.xml
@@ -0,0 +1,5 @@
+
+ -
+
+
+
\ No newline at end of file
diff --git a/eval4j/annotations/jdi/com/sun/jdi/event/annotations.xml b/eval4j/annotations/jdi/com/sun/jdi/event/annotations.xml
new file mode 100644
index 00000000000..a92ff00c346
--- /dev/null
+++ b/eval4j/annotations/jdi/com/sun/jdi/event/annotations.xml
@@ -0,0 +1,8 @@
+
+ -
+
+
+ -
+
+
+
\ No newline at end of file
diff --git a/eval4j/annotations/jdi/com/sun/jdi/request/annotations.xml b/eval4j/annotations/jdi/com/sun/jdi/request/annotations.xml
new file mode 100644
index 00000000000..3f72d2e9760
--- /dev/null
+++ b/eval4j/annotations/jdi/com/sun/jdi/request/annotations.xml
@@ -0,0 +1,8 @@
+
+ -
+
+
+ -
+
+
+
\ No newline at end of file
diff --git a/eval4j/annotations/jdi/java/lang/annotations.xml b/eval4j/annotations/jdi/java/lang/annotations.xml
new file mode 100644
index 00000000000..a2bfc485d6e
--- /dev/null
+++ b/eval4j/annotations/jdi/java/lang/annotations.xml
@@ -0,0 +1,8 @@
+
+ -
+
+
+ -
+
+
+
\ No newline at end of file
diff --git a/eval4j/annotations/org.ow2.asm_asm-debug-all_4.1/org/objectweb/asm/annotations.xml b/eval4j/annotations/org.ow2.asm_asm-debug-all_4.1/org/objectweb/asm/annotations.xml
new file mode 100644
index 00000000000..c608a4842d3
--- /dev/null
+++ b/eval4j/annotations/org.ow2.asm_asm-debug-all_4.1/org/objectweb/asm/annotations.xml
@@ -0,0 +1,554 @@
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
diff --git a/eval4j/annotations/org.ow2.asm_asm-debug-all_4.1/org/objectweb/asm/commons/annotations.xml b/eval4j/annotations/org.ow2.asm_asm-debug-all_4.1/org/objectweb/asm/commons/annotations.xml
new file mode 100644
index 00000000000..c7c2221a34f
--- /dev/null
+++ b/eval4j/annotations/org.ow2.asm_asm-debug-all_4.1/org/objectweb/asm/commons/annotations.xml
@@ -0,0 +1,1037 @@
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+
diff --git a/eval4j/annotations/org.ow2.asm_asm-debug-all_4.1/org/objectweb/asm/signature/annotations.xml b/eval4j/annotations/org.ow2.asm_asm-debug-all_4.1/org/objectweb/asm/signature/annotations.xml
new file mode 100644
index 00000000000..f53e24f7bcb
--- /dev/null
+++ b/eval4j/annotations/org.ow2.asm_asm-debug-all_4.1/org/objectweb/asm/signature/annotations.xml
@@ -0,0 +1,110 @@
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
diff --git a/eval4j/annotations/org.ow2.asm_asm-debug-all_4.1/org/objectweb/asm/tree/analysis/annotations.xml b/eval4j/annotations/org.ow2.asm_asm-debug-all_4.1/org/objectweb/asm/tree/analysis/annotations.xml
new file mode 100644
index 00000000000..582d9bf2dc6
--- /dev/null
+++ b/eval4j/annotations/org.ow2.asm_asm-debug-all_4.1/org/objectweb/asm/tree/analysis/annotations.xml
@@ -0,0 +1,599 @@
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+
diff --git a/eval4j/annotations/org.ow2.asm_asm-debug-all_4.1/org/objectweb/asm/tree/annotations.xml b/eval4j/annotations/org.ow2.asm_asm-debug-all_4.1/org/objectweb/asm/tree/annotations.xml
new file mode 100644
index 00000000000..d2f989a9d19
--- /dev/null
+++ b/eval4j/annotations/org.ow2.asm_asm-debug-all_4.1/org/objectweb/asm/tree/annotations.xml
@@ -0,0 +1,593 @@
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
diff --git a/eval4j/annotations/org.ow2.asm_asm-debug-all_4.1/org/objectweb/asm/util/annotations.xml b/eval4j/annotations/org.ow2.asm_asm-debug-all_4.1/org/objectweb/asm/util/annotations.xml
new file mode 100644
index 00000000000..18b031add3f
--- /dev/null
+++ b/eval4j/annotations/org.ow2.asm_asm-debug-all_4.1/org/objectweb/asm/util/annotations.xml
@@ -0,0 +1,1496 @@
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
diff --git a/eval4j/annotations/org.ow2.asm_asm-debug-all_4.1/org/objectweb/asm/xml/annotations.xml b/eval4j/annotations/org.ow2.asm_asm-debug-all_4.1/org/objectweb/asm/xml/annotations.xml
new file mode 100644
index 00000000000..cad2755ff73
--- /dev/null
+++ b/eval4j/annotations/org.ow2.asm_asm-debug-all_4.1/org/objectweb/asm/xml/annotations.xml
@@ -0,0 +1,290 @@
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+
diff --git a/eval4j/eval4j.iml b/eval4j/eval4j.iml
new file mode 100644
index 00000000000..cc20df994ed
--- /dev/null
+++ b/eval4j/eval4j.iml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/eval4j/src/org/jetbrains/eval4j/interpreter.kt b/eval4j/src/org/jetbrains/eval4j/interpreter.kt
new file mode 100644
index 00000000000..f70c76239e2
--- /dev/null
+++ b/eval4j/src/org/jetbrains/eval4j/interpreter.kt
@@ -0,0 +1,367 @@
+package org.jetbrains.eval4j
+
+import org.objectweb.asm.tree.analysis.*
+import org.objectweb.asm.Handle
+import org.objectweb.asm.Opcodes.*
+import org.objectweb.asm.Type
+import org.objectweb.asm.tree.AbstractInsnNode
+import org.objectweb.asm.tree.FieldInsnNode
+import org.objectweb.asm.tree.IntInsnNode
+import org.objectweb.asm.tree.LdcInsnNode
+import org.objectweb.asm.tree.MethodInsnNode
+import org.objectweb.asm.tree.MultiANewArrayInsnNode
+import org.objectweb.asm.tree.TypeInsnNode
+import org.objectweb.asm.tree.JumpInsnNode
+import org.objectweb.asm.tree.IincInsnNode
+
+class UnsupportedByteCodeException(message: String) : RuntimeException(message)
+
+trait Eval {
+ fun loadClass(classType: Type): Value
+ fun loadString(str: String): Value
+ fun newInstance(classType: Type): Value
+ fun isInstanceOf(value: Value, targetType: Type): Boolean
+
+ fun newArray(arrayType: Type, size: Int): Value
+ fun newMultiDimensionalArray(arrayType: Type, dimensionSizes: List): Value
+ fun getArrayLength(array: Value): Value
+ fun getArrayElement(array: Value, index: Value): Value
+ fun setArrayElement(array: Value, index: Value, newValue: Value)
+
+ fun getStaticField(fieldDesc: FieldDescription): Value
+ fun setStaticField(fieldDesc: FieldDescription, newValue: Value)
+ fun invokeStaticMethod(methodDesc: MethodDescription, arguments: List): Value
+
+ fun getField(instance: Value, fieldDesc: FieldDescription): Value
+ fun setField(instance: Value, fieldDesc: FieldDescription, newValue: Value)
+ fun invokeMethod(instance: Value, methodDesc: MethodDescription, arguments: List, invokespecial: Boolean = false): Value
+}
+
+class SingleInstructionInterpreter(private val eval: Eval) : Interpreter(ASM4) {
+ override fun newValue(`type`: Type?): Value? {
+ if (`type` == null) {
+ return NOT_A_VALUE
+ }
+
+ return makeNotInitializedValue(`type`)
+ }
+
+ override fun newOperation(insn: AbstractInsnNode): Value? {
+ return when (insn.getOpcode()) {
+ ACONST_NULL -> {
+ return NULL_VALUE
+ }
+
+ ICONST_M1 -> int(-1)
+ ICONST_0 -> int(0)
+ ICONST_1 -> int(1)
+ ICONST_2 -> int(2)
+ ICONST_3 -> int(3)
+ ICONST_4 -> int(4)
+ ICONST_5 -> int(5)
+
+ LCONST_0 -> long(0)
+ LCONST_1 -> long(1)
+
+ FCONST_0 -> float(0.0)
+ FCONST_1 -> float(1.0)
+ FCONST_2 -> float(2.0)
+
+ DCONST_0 -> double(0.0)
+ DCONST_1 -> double(1.0)
+
+ BIPUSH, SIPUSH -> int((insn as IntInsnNode).operand)
+
+ LDC -> {
+ val cst = ((insn as LdcInsnNode)).cst
+ when (cst) {
+ is Int -> int(cst)
+ is Float -> float(cst)
+ is Long -> long(cst)
+ is Double -> double(cst)
+ is String -> eval.loadString(cst)
+ is Type -> {
+ val sort = (cst as Type).getSort()
+ when (sort) {
+ Type.OBJECT, Type.ARRAY -> eval.loadClass(cst)
+ Type.METHOD -> throw UnsupportedByteCodeException("Mothod handles are not supported")
+ else -> throw UnsupportedByteCodeException("Illegal LDC constant " + cst)
+ }
+ }
+ is Handle -> throw UnsupportedByteCodeException("Method handles are not supported")
+ else -> throw UnsupportedByteCodeException("Illegal LDC constant " + cst)
+ }
+ }
+ JSR -> LabelValue((insn as JumpInsnNode).label)
+ GETSTATIC -> eval.getStaticField(FieldDescription(insn as FieldInsnNode))
+ NEW -> eval.newInstance(Type.getObjectType((insn as TypeInsnNode).desc))
+ else -> throw UnsupportedByteCodeException("$insn")
+ }
+ }
+
+ override fun copyOperation(insn: AbstractInsnNode, value: Value): Value {
+ return value
+ }
+
+ override fun unaryOperation(insn: AbstractInsnNode, value: Value): Value? {
+ return when (insn.getOpcode()) {
+ INEG -> int(-value.int)
+ IINC -> int(value.int + (insn as IincInsnNode).incr)
+ L2I -> int(value.long.toInt())
+ F2I -> int(value.float.toInt())
+ D2I -> int(value.double.toInt())
+ I2B -> byte(value.int.toByte())
+ I2C -> char(value.int.toChar())
+ I2S -> short(value.int.toShort())
+
+ FNEG -> float(-value.float)
+ I2F -> float(value.int.toFloat())
+ L2F -> float(value.long.toFloat())
+ D2F -> float(value.double.toFloat())
+
+ LNEG -> long(-value.long)
+ I2L -> long(value.int.toLong())
+ F2L -> long(value.float.toLong())
+ D2L -> long(value.double.toLong())
+
+ DNEG -> double(-value.double)
+ I2D -> double(value.int.toDouble())
+ L2D -> double(value.long.toDouble())
+ F2D -> double(value.float.toDouble())
+
+ IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IFNULL, IFNONNULL -> {
+ // Handled by interpreter loop, see checkUnaryCondition()
+ null
+ }
+
+ // TODO: switch
+ TABLESWITCH,
+ LOOKUPSWITCH -> throw UnsupportedByteCodeException("Switch is not supported yet")
+
+ PUTSTATIC -> {
+ eval.setStaticField(FieldDescription(insn as FieldInsnNode), value)
+ null
+ }
+
+ GETFIELD -> eval.getField(value, FieldDescription(insn as FieldInsnNode))
+
+ NEWARRAY -> {
+ val typeStr = when ((insn as IntInsnNode).operand) {
+ T_BOOLEAN -> "[Z"
+ T_CHAR -> "[C"
+ T_BYTE -> "[B"
+ T_SHORT -> "[S"
+ T_INT -> "[I"
+ T_FLOAT -> "[F"
+ T_DOUBLE -> "[D"
+ T_LONG -> "[J"
+ else -> throw AnalyzerException(insn, "Invalid array type")
+ }
+ eval.newArray(Type.getType(typeStr), value.int)
+ }
+ ANEWARRAY -> {
+ val desc = (insn as TypeInsnNode).desc
+ eval.newArray(Type.getType("[" + Type.getObjectType(desc)), value.int)
+ }
+ ARRAYLENGTH -> eval.getArrayLength(value)
+
+ ATHROW -> {
+ // Handled by interpreter loop
+ null
+ }
+
+ CHECKCAST -> {
+ val targetType = Type.getObjectType((insn as TypeInsnNode).desc)
+ if (eval.isInstanceOf(value, targetType)) {
+ ObjectValue(value.obj, targetType)
+ }
+ else {
+ throwEvalException(ClassCastException("Value '$value' cannot be cast to $targetType"))
+ }
+ }
+
+ INSTANCEOF -> {
+ val targetType = Type.getObjectType((insn as TypeInsnNode).desc)
+ boolean(eval.isInstanceOf(value, targetType))
+ }
+
+ // TODO: maybe just do nothing?
+ MONITORENTER, MONITOREXIT -> throw UnsupportedByteCodeException("Monitor instructions are not supported")
+
+ else -> throw UnsupportedByteCodeException("$insn")
+ }
+ }
+
+ public fun checkUnaryCondition(value: Value, opcode: Int): Boolean {
+ return when (opcode) {
+ IFEQ -> value.int == 0
+ IFNE -> value.int != 0
+ IFLT -> value.int < 0
+ IFGT -> value.int > 0
+ IFLE -> value.int <= 0
+ IFGE -> value.int >= 0
+ IFNULL -> value.obj == null
+ IFNONNULL -> value.obj != null
+ else -> throw UnsupportedByteCodeException("Unknown opcode: $opcode")
+ }
+ }
+
+ override fun binaryOperation(insn: AbstractInsnNode, value1: Value, value2: Value): Value? {
+ return when (insn.getOpcode()) {
+ IALOAD, BALOAD, CALOAD, SALOAD,
+ FALOAD, LALOAD, DALOAD,
+ AALOAD -> eval.getArrayElement(value1, value2)
+
+ IADD -> int(value1.int + value2.int)
+ ISUB -> int(value1.int - value2.int)
+ IMUL -> int(value1.int * value2.int)
+ IDIV -> int(value1.int / value2.int)
+ IREM -> int(value1.int % value2.int)
+ ISHL -> int(value1.int shl value2.int)
+ ISHR -> int(value1.int shr value2.int)
+ IUSHR -> int(value1.int ushr value2.int)
+ IAND -> int(value1.int and value2.int)
+ IOR -> int(value1.int or value2.int)
+ IXOR -> int(value1.int xor value2.int)
+
+ LADD -> long(value1.long + value2.long)
+ LSUB -> long(value1.long - value2.long)
+ LMUL -> long(value1.long * value2.long)
+ LDIV -> long(value1.long / value2.long)
+ LREM -> long(value1.long % value2.long)
+ LSHL -> long(value1.long shl value2.int)
+ LSHR -> long(value1.long shr value2.int)
+ LUSHR -> long(value1.long ushr value2.int)
+ LAND -> long(value1.long and value2.long)
+ LOR -> long(value1.long or value2.long)
+ LXOR -> long(value1.long xor value2.long)
+
+ FADD -> float(value1.float + value2.float)
+ FSUB -> float(value1.float - value2.float)
+ FMUL -> float(value1.float * value2.float)
+ FDIV -> float(value1.float / value2.float)
+ FREM -> float(value1.float % value2.float)
+
+ DADD -> double(value1.double + value2.double)
+ DSUB -> double(value1.double - value2.double)
+ DMUL -> double(value1.double * value2.double)
+ DDIV -> double(value1.double / value2.double)
+ DREM -> double(value1.double % value2.double)
+
+ LCMP -> {
+ val l1 = value1.long
+ val l2 = value2.long
+
+ int(when {
+ l1 > l2 -> 1
+ l1 == l2 -> 0
+ else -> -1
+ })
+ }
+
+ FCMPL,
+ FCMPG -> {
+ val l1 = value1.float
+ val l2 = value2.float
+
+ int(when {
+ l1 > l2 -> 1
+ l1 == l2 -> 0
+ l1 < l2 -> -1
+ // one of them is NaN
+ else -> if (insn.getOpcode() == FCMPG) 1 else -1
+ })
+ }
+
+ DCMPL,
+ DCMPG -> {
+ val l1 = value1.double
+ val l2 = value2.double
+
+ int(when {
+ l1 > l2 -> 1
+ l1 == l2 -> 0
+ l1 < l2 -> -1
+ // one of them is NaN
+ else -> if (insn.getOpcode() == DCMPG) 1 else -1
+ })
+ }
+
+ IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, IF_ACMPNE -> {
+ // Handled by interpreter loop, see checkBinaryCondition()
+ null
+ }
+
+ PUTFIELD -> {
+ eval.setField(value1, FieldDescription(insn as FieldInsnNode), value2)
+ null
+ }
+
+ else -> throw UnsupportedByteCodeException("$insn")
+ }
+ }
+
+ public fun checkBinaryCondition(value1: Value, value2: Value, opcode: Int): Boolean {
+ return when (opcode) {
+ IF_ICMPEQ -> value1.int == value2.int
+ IF_ICMPNE -> value1.int != value2.int
+ IF_ICMPLT -> value1.int < value2.int
+ IF_ICMPGT -> value1.int > value2.int
+ IF_ICMPLE -> value1.int <= value2.int
+ IF_ICMPGE -> value1.int >= value2.int
+
+ IF_ACMPEQ -> value1.obj == value2.obj
+ IF_ACMPNE -> value1.obj != value2.obj
+ else -> throw UnsupportedByteCodeException("Unknown opcode: $opcode")
+ }
+ }
+
+ override fun ternaryOperation(insn: AbstractInsnNode, value1: Value, value2: Value, value3: Value): Value? {
+ return when (insn.getOpcode()) {
+ IASTORE, LASTORE, FASTORE, DASTORE, AASTORE, BASTORE, CASTORE, SASTORE -> {
+ eval.setArrayElement(value1, value2, value3)
+ null
+ }
+ else -> throw UnsupportedByteCodeException("$insn")
+ }
+ }
+
+ override fun naryOperation(insn: AbstractInsnNode, values: List): Value {
+ return when (insn.getOpcode()) {
+ MULTIANEWARRAY -> {
+ val node = insn as MultiANewArrayInsnNode
+ eval.newMultiDimensionalArray(Type.getType(node.desc), values.map { v -> v.int })
+ }
+
+ INVOKEVIRTUAL, INVOKESPECIAL, INVOKEINTERFACE -> {
+ eval.invokeMethod(
+ values[0],
+ MethodDescription(insn as MethodInsnNode),
+ values.subList(1, values.size()),
+ insn.getOpcode() == INVOKESPECIAL
+ )
+ }
+
+ INVOKESTATIC -> eval.invokeStaticMethod(MethodDescription(insn as MethodInsnNode), values)
+
+ INVOKEDYNAMIC -> throw UnsupportedByteCodeException("INVOKEDYNAMIC is not supported")
+ else -> throw UnsupportedByteCodeException("$insn")
+ }
+ }
+
+
+ override fun returnOperation(insn: AbstractInsnNode, value: Value, expected: Value) {
+ when (insn.getOpcode()) {
+ IRETURN, LRETURN, FRETURN, DRETURN, ARETURN -> {
+ // Handled by interpreter loop
+ }
+
+ else -> throw UnsupportedByteCodeException("$insn")
+ }
+ }
+
+ override fun merge(v: Value, w: Value): Value {
+ // We always remember the NEW value
+ return w
+ }
+}
diff --git a/eval4j/src/org/jetbrains/eval4j/interpreterLoop.kt b/eval4j/src/org/jetbrains/eval4j/interpreterLoop.kt
new file mode 100644
index 00000000000..b63d49a689c
--- /dev/null
+++ b/eval4j/src/org/jetbrains/eval4j/interpreterLoop.kt
@@ -0,0 +1,248 @@
+package org.jetbrains.eval4j
+
+import org.objectweb.asm.tree.AbstractInsnNode
+import org.objectweb.asm.tree.analysis.Frame
+import org.objectweb.asm.tree.MethodNode
+import org.objectweb.asm.Type
+import org.objectweb.asm.Opcodes.*
+import org.objectweb.asm.tree.JumpInsnNode
+import org.objectweb.asm.tree.VarInsnNode
+import org.objectweb.asm.util.Printer
+import org.objectweb.asm.tree.TryCatchBlockNode
+import java.util.ArrayList
+
+trait InterpreterResult {
+ fun toString(): String
+}
+
+class ExceptionThrown(val exception: Value): InterpreterResult {
+ override fun toString(): String = "Thrown $exception"
+}
+
+data class ValueReturned(val result: Value): InterpreterResult {
+ override fun toString(): String = "Returned $result"
+}
+
+class AbnormalTermination(val message: String): InterpreterResult {
+ override fun toString(): String = "Terminated abnormally: $message"
+}
+
+trait InterpretationEventHandler {
+
+ class object {
+ object NONE : InterpretationEventHandler {
+ override fun instructionProcessed(insn: AbstractInsnNode): InterpreterResult? = null
+ override fun exceptionThrown(currentState: Frame, currentInsn: AbstractInsnNode, exception: Value): InterpreterResult? = null
+ override fun exceptionCaught(currentState: Frame, currentInsn: AbstractInsnNode, exception: Value): InterpreterResult? = null
+ }
+ }
+
+ // If a non-null value is returned, interpreter loop is terminated and that value is used as a result
+ fun instructionProcessed(insn: AbstractInsnNode): InterpreterResult?
+
+ fun exceptionThrown(currentState: Frame, currentInsn: AbstractInsnNode, exception: Value): InterpreterResult?
+ fun exceptionCaught(currentState: Frame, currentInsn: AbstractInsnNode, exception: Value): InterpreterResult?
+}
+
+class ThrownFromEvalException(cause: Throwable): RuntimeException(cause) {
+ fun toString(): String = "Thrown by evaluator: ${getCause()}"
+}
+
+class ThrownFromEvaluatedCodeException(val exception: Value): RuntimeException() {
+ fun toString(): String = "Thrown from evaluated code: $exception"
+}
+
+fun interpreterLoop(
+ m: MethodNode,
+ initialState: Frame,
+ eval: Eval,
+ handler: InterpretationEventHandler = InterpretationEventHandler.NONE
+): InterpreterResult {
+ val firstInsn = m.instructions.getFirst()
+ if (firstInsn == null) throw IllegalArgumentException("Empty method")
+
+ var currentInsn = firstInsn!!
+
+ fun goto(nextInsn: AbstractInsnNode?) {
+ if (nextInsn == null) throw IllegalArgumentException("Instruction flow ended with no RETURN")
+ currentInsn = nextInsn
+ }
+
+ val interpreter = SingleInstructionInterpreter(eval)
+ val frame = Frame(initialState)
+ val handlers = computeHandlers(m)
+
+ class ResultException(val result: InterpreterResult): RuntimeException()
+
+ fun exceptionCaught(exceptionValue: Value, instanceOf: (Type) -> Boolean): Boolean {
+ val catchBlocks = handlers[m.instructions.indexOf(currentInsn)] ?: listOf()
+ for (catch in catchBlocks) {
+ val exceptionTypeInternalName = catch.`type`
+ if (exceptionTypeInternalName != null) {
+ val exceptionType = Type.getObjectType(exceptionTypeInternalName)
+ if (instanceOf(exceptionType)) {
+ val handled = handler.exceptionCaught(frame, currentInsn, exceptionValue)
+ if (handled != null) throw ResultException(handled)
+ frame.clearStack()
+ frame.push(exceptionValue)
+ goto(catch.handler)
+ return true
+ }
+ }
+ }
+ return false
+ }
+
+ fun exceptionCaught(exceptionValue: Value): Boolean = exceptionCaught(exceptionValue) {
+ exceptionType -> eval.isInstanceOf(exceptionValue, exceptionType)
+ }
+
+ fun exceptionFromEvalCaught(exception: Throwable, exceptionValue: Value): Boolean {
+ return exceptionCaught(exceptionValue) {
+ exceptionType ->
+ try {
+ val exceptionClass = exception.javaClass
+ val _class = Class.forName(
+ exceptionType.getInternalName().replace('/', '.'),
+ true,
+ exceptionClass.getClassLoader()
+ )
+ _class.isAssignableFrom(exceptionClass)
+ }
+ catch (e: ClassNotFoundException) {
+ // If the class is not available in this VM, it can not be a superclass of an exception trown in it
+ false
+ }
+ }
+ }
+
+ try {
+ while (true) {
+ val insnOpcode = currentInsn.getOpcode()
+ val insnType = currentInsn.getType()
+
+ when (insnType) {
+ AbstractInsnNode.LABEL,
+ AbstractInsnNode.FRAME,
+ AbstractInsnNode.LINE -> {
+ // skip to the next instruction
+ }
+
+ else -> {
+ when (insnOpcode) {
+ GOTO -> {
+ goto((currentInsn as JumpInsnNode).label)
+ continue
+ }
+
+ RET -> {
+ val varNode = currentInsn as VarInsnNode
+ val address = frame.getLocal(varNode.`var`)
+ goto((address as LabelValue).value)
+ continue
+ }
+
+ // TODO: switch
+ LOOKUPSWITCH -> UnsupportedByteCodeException("LOOKUPSWITCH is not supported yet")
+ TABLESWITCH -> UnsupportedByteCodeException("TABLESWITCH is not supported yet")
+
+ IRETURN, LRETURN, FRETURN, DRETURN, ARETURN -> {
+ val value = frame.getStack(0)!!
+ val expectedType = Type.getReturnType(m.desc)
+ if (expectedType.getSort() == Type.OBJECT) {
+ val coerced = if (value != NULL_VALUE && value.asmType != expectedType)
+ ObjectValue(value.obj, expectedType)
+ else value
+ return ValueReturned(coerced)
+ }
+ if (value.asmType != expectedType) {
+ assert(insnOpcode == IRETURN, "Only ints should be coerced: " + Printer.OPCODES[insnOpcode])
+
+ val coerced = when (expectedType.getSort()) {
+ Type.BOOLEAN -> boolean(value.boolean)
+ Type.BYTE -> byte(value.int.toByte())
+ Type.SHORT -> short(value.int.toShort())
+ Type.CHAR -> char(value.int.toChar())
+ else -> throw UnsupportedByteCodeException("Should not be coerced: $expectedType")
+ }
+ return ValueReturned(coerced)
+ }
+ return ValueReturned(value)
+ }
+ RETURN -> return ValueReturned(VOID_VALUE)
+ IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IFNULL, IFNONNULL -> {
+ if (interpreter.checkUnaryCondition(frame.getStack(0)!!, insnOpcode)) {
+ frame.execute(currentInsn, interpreter)
+ goto((currentInsn as JumpInsnNode).label)
+ continue
+ }
+ }
+ IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, IF_ACMPNE -> {
+ if (interpreter.checkBinaryCondition(frame.getStack(0)!!, frame.getStack(1)!!, insnOpcode)) {
+ frame.execute(currentInsn, interpreter)
+ goto((currentInsn as JumpInsnNode).label)
+ continue
+ }
+ }
+
+ ATHROW -> {
+ val exceptionValue = frame.getStack(0)!!
+ val handled = handler.exceptionThrown(frame, currentInsn, exceptionValue)
+ if (handled != null) return handled
+ if (exceptionCaught(exceptionValue)) continue
+ return ExceptionThrown(exceptionValue)
+ }
+
+ // Workaround for a bug in Kotlin: NoPatterMatched exception is thrown otherwise!
+ else -> {}
+ }
+
+ try {
+ frame.execute(currentInsn, interpreter)
+ }
+ catch (e: ThrownFromEvalException) {
+ val exception = e.getCause()!!
+ val exceptionValue = ObjectValue(exception, Type.getType(exception.javaClass))
+ val handled = handler.exceptionThrown(frame, currentInsn,
+ exceptionValue)
+ if (handled != null) return handled
+ if (exceptionFromEvalCaught(exception, exceptionValue)) continue
+ return ExceptionThrown(exceptionValue)
+ }
+ catch (e: ThrownFromEvaluatedCodeException) {
+ val handled = handler.exceptionThrown(frame, currentInsn, e.exception)
+ if (handled != null) return handled
+ if (exceptionCaught(e.exception)) continue
+ return ExceptionThrown(e.exception)
+ }
+ }
+ }
+
+ val handled = handler.instructionProcessed(currentInsn)
+ if (handled != null) return handled
+
+ goto(currentInsn.getNext())
+ }
+ }
+ catch(e: ResultException) {
+ return e.result
+ }
+}
+
+// Copied from org.objectweb.asm.tree.analysis.Analyzer.analyze()
+fun computeHandlers(m: MethodNode): Array?> {
+ val insns = m.instructions
+ val handlers = Array?>(insns.size()) {null}
+ for (tcb in m.tryCatchBlocks) {
+ val begin = insns.indexOf(tcb.start)
+ val end = insns.indexOf(tcb.end)
+ for (i in begin..end - 1) {
+ val insnHandlers = handlers[i] ?: ArrayList()
+ handlers[i] = insnHandlers
+
+ insnHandlers.add(tcb)
+ }
+ }
+
+ return handlers
+}
\ No newline at end of file
diff --git a/eval4j/src/org/jetbrains/eval4j/jdi/jdiEval.kt b/eval4j/src/org/jetbrains/eval4j/jdi/jdiEval.kt
new file mode 100644
index 00000000000..dd927dc64a4
--- /dev/null
+++ b/eval4j/src/org/jetbrains/eval4j/jdi/jdiEval.kt
@@ -0,0 +1,213 @@
+package org.jetbrains.eval4j.jdi
+
+import org.jetbrains.eval4j.*
+import org.objectweb.asm.Type
+import com.sun.jdi
+
+val CLASS = Type.getType(javaClass>())
+
+class JDIEval(
+ private val vm: jdi.VirtualMachine,
+ private val classLoader: jdi.ClassLoaderReference,
+ private val thread: jdi.ThreadReference
+) : Eval {
+ override fun loadClass(classType: Type): Value {
+ val loadedClasses = vm.classesByName(classType.getInternalName())
+ if (!loadedClasses.isEmpty()) {
+ return loadedClasses[0].classObject().asValue()
+ }
+ return invokeStaticMethod(
+ MethodDescription(
+ CLASS.getInternalName(),
+ "forName",
+ "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;",
+ true
+ ),
+ listOf(
+ vm.mirrorOf(classType.getInternalName().replace('/', '.')).asValue(),
+ boolean(true),
+ classLoader.asValue()
+ )
+ )
+ }
+
+ override fun loadString(str: String): Value = vm.mirrorOf(str)!!.asValue()
+
+ override fun newInstance(classType: Type): Value {
+ return NewObjectValue(classType)
+ }
+
+ override fun isInstanceOf(value: Value, targetType: Type): Boolean {
+ assert(targetType.getSort() == Type.OBJECT, "Can't check isInstanceOf() for non-object type $targetType")
+
+ val _class = loadClass(targetType)
+ return invokeMethod(
+ _class,
+ MethodDescription(
+ CLASS.getInternalName(),
+ "isInstance",
+ "(Ljava/lang/Object;)Z",
+ false
+ ),
+ listOf(value)).boolean
+ }
+
+ fun Type.asReferenceType(): jdi.ReferenceType = loadClass(this).jdiClass!!.reflectedType()
+ fun Type.asArrayType(): jdi.ArrayType = asReferenceType() as jdi.ArrayType
+
+ override fun newArray(arrayType: Type, size: Int): Value {
+ val jdiArrayType = arrayType.asArrayType()
+ return jdiArrayType.newInstance(size).asValue()
+ }
+
+ private val Type.arrayElementType: Type
+ get(): Type {
+ assert(getSort() == Type.ARRAY, "Not an array type: $this")
+ return Type.getType(getDescriptor().substring(1))
+ }
+
+ private fun fillArray(elementType: Type, size: Int, nestedSizes: List): Value {
+ val arr = newArray(Type.getType("[" + elementType.getDescriptor()), size)
+ if (!nestedSizes.isEmpty()) {
+ val nestedElementType = elementType.arrayElementType
+ val nestedSize = nestedSizes[0]
+ val tail = nestedSizes.tail
+ for (i in 0..size - 1) {
+ setArrayElement(arr, int(i), fillArray(nestedElementType, nestedSize, tail))
+ }
+ }
+ return arr
+ }
+
+ override fun newMultiDimensionalArray(arrayType: Type, dimensionSizes: List): Value {
+ return fillArray(arrayType.arrayElementType, dimensionSizes[0], dimensionSizes.tail)
+ }
+
+ private fun Value.array() = jdiObj.checkNull() as jdi.ArrayReference
+
+ override fun getArrayLength(array: Value): Value {
+ return int(array.array().length())
+ }
+
+ override fun getArrayElement(array: Value, index: Value): Value {
+ return array.array().getValue(index.int).asValue()
+ }
+
+ override fun setArrayElement(array: Value, index: Value, newValue: Value) {
+ array.array().setValue(index.int, newValue.asJdiValue(vm))
+ }
+
+ private fun findField(fieldDesc: FieldDescription): jdi.Field {
+ val _class = fieldDesc.ownerType.asReferenceType()
+ val field = _class.fieldByName(fieldDesc.name)
+ if (field == null) {
+ throwEvalException(NoSuchFieldError("Field not found: $fieldDesc"))
+ }
+ return field
+ }
+
+ private fun findStaticField(fieldDesc: FieldDescription): jdi.Field {
+ val field = findField(fieldDesc)
+ if (!field.isStatic()) {
+ throwEvalException(NoSuchFieldError("Field is not static: $fieldDesc"))
+ }
+ return field
+ }
+
+ override fun getStaticField(fieldDesc: FieldDescription): Value {
+ val field = findStaticField(fieldDesc)
+ return mayThrow { field.declaringType().getValue(field) }.asValue()
+ }
+
+ override fun setStaticField(fieldDesc: FieldDescription, newValue: Value) {
+ val field = findStaticField(fieldDesc)
+
+ if (field.isFinal()) {
+ throwEvalException(NoSuchFieldError("Can't modify a final field: $field"))
+ }
+
+ val _class = field.declaringType()
+ if (_class !is jdi.ClassType) {
+ throwEvalException(NoSuchFieldError("Can't a field in a non-class: $field"))
+ }
+
+ val jdiValue = newValue.asJdiValue(vm)
+ mayThrow { _class.setValue(field, jdiValue) }
+ }
+
+ private fun findMethod(methodDesc: MethodDescription): jdi.Method {
+ val _class = methodDesc.ownerType.asReferenceType()
+ val method = when (_class) {
+ is jdi.ClassType -> {
+ val m = _class.concreteMethodByName(methodDesc.name, methodDesc.desc)
+ if (m == null) listOf() else listOf(m)
+ }
+ else -> _class.methodsByName(methodDesc.name, methodDesc.desc)
+ }
+ if (method.isEmpty()) {
+ throwEvalException(NoSuchMethodError("Method not found: $methodDesc"))
+ }
+ return method[0]
+ }
+
+ override fun invokeStaticMethod(methodDesc: MethodDescription, arguments: List): Value {
+ val method = findMethod(methodDesc)
+ if (!method.isStatic()) {
+ throwEvalException(NoSuchMethodError("Method is not static: $methodDesc"))
+ }
+ val _class = method.declaringType()
+ if (_class !is jdi.ClassType) throwEvalException(NoSuchMethodError("Static method is a non-class type: $method"))
+
+ val args = arguments.map { v -> v.asJdiValue(vm) }
+ val result = mayThrow { _class.invokeMethod(thread, method, args, 0) }
+ return result.asValue()
+ }
+
+ override fun getField(instance: Value, fieldDesc: FieldDescription): Value {
+ val field = findField(fieldDesc)
+ val obj = instance.jdiObj.checkNull()
+
+ return mayThrow { obj.getValue(field) }.asValue()
+ }
+
+ override fun setField(instance: Value, fieldDesc: FieldDescription, newValue: Value) {
+ val field = findField(fieldDesc)
+ val obj = instance.jdiObj.checkNull()
+
+ val jdiValue = newValue.asJdiValue(vm)
+ mayThrow { obj.setValue(field, jdiValue) }
+ }
+
+ override fun invokeMethod(instance: Value, methodDesc: MethodDescription, arguments: List, invokespecial: Boolean): Value {
+ if (invokespecial) {
+ if (methodDesc.name == "") {
+ // Constructor call
+ val ctor = findMethod(methodDesc)
+ val _class = (instance as NewObjectValue).asmType.asReferenceType() as jdi.ClassType
+ val args = arguments.map { v -> v.asJdiValue(vm) }
+ val result = mayThrow { _class.newInstance(thread, ctor, args, 0) }
+ instance.value = result
+ return result.asValue()
+ }
+ else {
+ // TODO
+ throw UnsupportedOperationException("invokespecial is not suported yet")
+ }
+ }
+ val method = findMethod(methodDesc)
+
+ val obj = instance.jdiObj.checkNull()
+ val args = arguments.map { v -> v.asJdiValue(vm) }
+ val result = mayThrow { obj.invokeMethod(thread, method, args, 0) }
+ return result.asValue()
+ }
+}
+
+fun mayThrow(f: () -> T): T {
+ try {
+ return f()
+ }
+ catch (e: jdi.InvocationException) {
+ throw ThrownFromEvaluatedCodeException(e.exception().asValue())
+ }
+}
\ No newline at end of file
diff --git a/eval4j/src/org/jetbrains/eval4j/jdi/jdiValues.kt b/eval4j/src/org/jetbrains/eval4j/jdi/jdiValues.kt
new file mode 100644
index 00000000000..519a645d85c
--- /dev/null
+++ b/eval4j/src/org/jetbrains/eval4j/jdi/jdiValues.kt
@@ -0,0 +1,79 @@
+package org.jetbrains.eval4j.jdi
+
+import org.jetbrains.eval4j.*
+import org.objectweb.asm.tree.analysis.Frame
+import org.objectweb.asm.tree.MethodNode
+import org.objectweb.asm.Type
+import org.objectweb.asm.Opcodes.*
+import com.sun.jdi
+
+fun makeInitialFrame(methodNode: MethodNode, arguments: List): Frame {
+ val isStatic = (methodNode.access and ACC_STATIC) != 0
+ assert(isStatic, "Instance methods are not supported: $methodNode")
+
+ val params = Type.getArgumentTypes(methodNode.desc)
+ assert(params.size == arguments.size(), "Wrong number of arguments for $methodNode: $arguments")
+
+ val frame = Frame(methodNode.maxLocals, methodNode.maxStack)
+ frame.setReturn(makeNotInitializedValue(Type.getReturnType(methodNode.desc)))
+
+ for ((i, arg) in arguments.withIndices()) {
+ frame.setLocal(i, arg)
+ }
+
+ for (i in arguments.size..methodNode.maxLocals - 1) {
+ frame.setLocal(i, NOT_A_VALUE)
+ }
+
+ return frame
+}
+
+class JDIFailureException(message: String?, cause: Throwable? = null): RuntimeException(message, cause)
+
+fun T?.sure(message: String? = null): T = this ?: throw JDIFailureException(message)
+
+fun jdi.Value?.asValue(): Value {
+ return when (this) {
+ null -> NULL_VALUE
+ is jdi.VoidValue -> VOID_VALUE
+ is jdi.BooleanValue -> IntValue(intValue(), Type.BOOLEAN_TYPE)
+ is jdi.ByteValue -> IntValue(intValue(), Type.BYTE_TYPE)
+ is jdi.ShortValue -> IntValue(intValue(), Type.SHORT_TYPE)
+ is jdi.CharValue -> IntValue(intValue(), Type.CHAR_TYPE)
+ is jdi.IntegerValue -> IntValue(intValue(), Type.INT_TYPE)
+ is jdi.LongValue -> LongValue(longValue())
+ is jdi.FloatValue -> FloatValue(floatValue())
+ is jdi.DoubleValue -> DoubleValue(doubleValue())
+ is jdi.ObjectReference -> ObjectValue(this, `type`().asType())
+ else -> throw JDIFailureException("Unknown value: $this")
+ }
+}
+
+fun jdi.Type.asType(): Type = Type.getType(this.signature())
+
+val Value.jdiObj: jdi.ObjectReference?
+ get() = this.obj as jdi.ObjectReference?
+
+val Value.jdiClass: jdi.ClassObjectReference?
+ get() = this.jdiObj as jdi.ClassObjectReference?
+
+fun Value.asJdiValue(vm: jdi.VirtualMachine): jdi.Value? {
+ return when (this) {
+ NULL_VALUE -> null
+ VOID_VALUE -> vm.mirrorOfVoid()
+ is IntValue -> when (asmType) {
+ Type.BOOLEAN_TYPE -> vm.mirrorOf(boolean)
+ Type.BYTE_TYPE -> vm.mirrorOf(int.toByte())
+ Type.SHORT_TYPE -> vm.mirrorOf(int.toShort())
+ Type.CHAR_TYPE -> vm.mirrorOf(int.toChar())
+ Type.INT_TYPE -> vm.mirrorOf(int)
+ else -> throw JDIFailureException("Unknown value type: $this")
+ }
+ is LongValue -> vm.mirrorOf(value)
+ is FloatValue -> vm.mirrorOf(value)
+ is DoubleValue -> vm.mirrorOf(value)
+ is ObjectValue -> value as jdi.ObjectReference
+ is NewObjectValue -> this.obj as jdi.ObjectReference
+ else -> throw JDIFailureException("Unknown value: $this")
+ }
+}
\ No newline at end of file
diff --git a/eval4j/src/org/jetbrains/eval4j/members.kt b/eval4j/src/org/jetbrains/eval4j/members.kt
new file mode 100644
index 00000000000..68bdbbccc4d
--- /dev/null
+++ b/eval4j/src/org/jetbrains/eval4j/members.kt
@@ -0,0 +1,56 @@
+package org.jetbrains.eval4j
+
+import org.objectweb.asm.tree.MethodInsnNode
+import org.objectweb.asm.Opcodes.*
+import org.objectweb.asm.*
+import org.objectweb.asm.tree.FieldInsnNode
+
+open data class MemberDescription(
+ val ownerInternalName: String,
+ val name: String,
+ val desc: String,
+ val isStatic: Boolean
+)
+
+val MemberDescription.ownerType: Type
+ get() = Type.getObjectType(ownerInternalName)
+
+class MethodDescription(
+ ownerInternalName: String,
+ name: String,
+ desc: String,
+ isStatic: Boolean
+) : MemberDescription(ownerInternalName, name, desc, isStatic)
+
+fun MethodDescription(insn: MethodInsnNode): MethodDescription =
+ MethodDescription(
+ insn.owner,
+ insn.name,
+ insn.desc,
+ insn.getOpcode() == INVOKESTATIC
+ )
+
+val MethodDescription.returnType: Type
+ get() = Type.getReturnType(desc)
+
+val MethodDescription.parameterTypes: List
+ get() = Type.getArgumentTypes(desc).toList()
+
+
+class FieldDescription(
+ ownerInternalName: String,
+ name: String,
+ desc: String,
+ isStatic: Boolean
+) : MemberDescription(ownerInternalName, name, desc, isStatic)
+
+fun FieldDescription(insn: FieldInsnNode): FieldDescription =
+ FieldDescription(
+ insn.owner,
+ insn.name,
+ insn.desc,
+ insn.getOpcode() in setOf(GETSTATIC, PUTSTATIC)
+ )
+
+val FieldDescription.fieldType: Type
+ get() = Type.getType(desc)
diff --git a/eval4j/src/org/jetbrains/eval4j/values.kt b/eval4j/src/org/jetbrains/eval4j/values.kt
new file mode 100644
index 00000000000..445fd92fcae
--- /dev/null
+++ b/eval4j/src/org/jetbrains/eval4j/values.kt
@@ -0,0 +1,111 @@
+package org.jetbrains.eval4j
+
+import org.objectweb.asm.Type
+import org.objectweb.asm.tree.LabelNode
+
+trait Value : org.objectweb.asm.tree.analysis.Value {
+ val asmType: Type
+ val valid: Boolean
+ override fun getSize(): Int = asmType.getSize()
+
+ override fun toString(): String
+}
+
+object NOT_A_VALUE: Value {
+ override val asmType = Type.getType("")
+ override val valid = false
+ override fun getSize(): Int = 1
+
+ override fun toString() = "NOT_A_VALUE"
+}
+
+object VOID_VALUE: Value {
+ override val asmType: Type = Type.VOID_TYPE
+ override val valid: Boolean = false
+ override fun toString() = "VOID_VALUE"
+}
+
+fun makeNotInitializedValue(t: Type): Value? {
+ return when (t.getSort()) {
+ Type.VOID -> null
+ else -> NotInitialized(t)
+ }
+}
+
+class NotInitialized(override val asmType: Type): Value {
+ override val valid = false
+ override fun toString() = "NotInitialized: $asmType"
+}
+
+abstract class AbstractValueBase(
+ override val asmType: Type
+) : Value {
+ override val valid = true
+ abstract val value: V
+
+ override fun toString() = "$value: $asmType"
+
+ override fun equals(other: Any?): Boolean {
+ if (other !is AbstractValue<*>) return false
+
+ return value == other.value && asmType == other.asmType
+ }
+
+ override fun hashCode(): Int {
+ return value.hashCode() + 17 * asmType.hashCode()
+ }
+}
+
+abstract class AbstractValue(
+ override val value: V,
+ asmType: Type
+) : AbstractValueBase(asmType)
+
+class IntValue(value: Int, asmType: Type): AbstractValue(value, asmType)
+class LongValue(value: Long): AbstractValue(value, Type.LONG_TYPE)
+class FloatValue(value: Float): AbstractValue(value, Type.FLOAT_TYPE)
+class DoubleValue(value: Double): AbstractValue(value, Type.DOUBLE_TYPE)
+class ObjectValue(value: Any?, asmType: Type): AbstractValue(value, asmType)
+class NewObjectValue(asmType: Type): AbstractValueBase(asmType) {
+ override var value: Any? = null
+}
+
+class LabelValue(value: LabelNode): AbstractValue(value, Type.VOID_TYPE)
+
+fun boolean(v: Boolean) = IntValue(if (v) 1 else 0, Type.BOOLEAN_TYPE)
+fun byte(v: Byte) = IntValue(v.toInt(), Type.BYTE_TYPE)
+fun short(v: Short) = IntValue(v.toInt(), Type.SHORT_TYPE)
+fun char(v: Char) = IntValue(v.toInt(), Type.CHAR_TYPE)
+fun int(v: Int) = IntValue(v, Type.INT_TYPE)
+fun long(v: Long) = LongValue(v)
+fun float(v: Float) = FloatValue(v)
+fun double(v: Double) = DoubleValue(v)
+//fun obj(v: T, t: Type = if (v != null) Type.getType(v.javaClass) else Type.getType(javaClass())) = ObjectValue(v, t)
+
+val NULL_VALUE = ObjectValue(null, Type.getObjectType("null"))
+
+val Value.boolean: Boolean get() = (this as IntValue).value == 1
+val Value.int: Int get() = (this as IntValue).value
+val Value.long: Long get() = (this as LongValue).value
+val Value.float: Float get() = (this as FloatValue).value
+val Value.double: Double get() = (this as DoubleValue).value
+val Value.obj: Any?
+ get(): Any? {
+ if (this is NewObjectValue) {
+ val v = value
+ if (v == null) throw IllegalStateException("Trying to access an unitialized object: $this")
+ return v
+ }
+ return (this as AbstractValue<*>).value
+ }
+
+fun T?.checkNull(): T {
+ if (this == null) {
+ throwEvalException(NullPointerException())
+ }
+ return this
+}
+
+fun throwEvalException(e: Throwable): Nothing {
+ throw ThrownFromEvalException(e)
+}
\ No newline at end of file
diff --git a/eval4j/test/org/jetbrains/eval4j/jdi/test/Debugee.java b/eval4j/test/org/jetbrains/eval4j/jdi/test/Debugee.java
new file mode 100644
index 00000000000..0f5241b7cc2
--- /dev/null
+++ b/eval4j/test/org/jetbrains/eval4j/jdi/test/Debugee.java
@@ -0,0 +1,9 @@
+package org.jetbrains.eval4j.jdi.test;
+
+public class Debugee {
+ public static void main(String[] args) {
+ // BREAKPOINT
+ Runtime.getRuntime();
+ System.out.println("hi");
+ }
+}
diff --git a/eval4j/test/org/jetbrains/eval4j/jdi/test/jdiTest.kt b/eval4j/test/org/jetbrains/eval4j/jdi/test/jdiTest.kt
new file mode 100644
index 00000000000..48dff47a059
--- /dev/null
+++ b/eval4j/test/org/jetbrains/eval4j/jdi/test/jdiTest.kt
@@ -0,0 +1,132 @@
+package org.jetbrains.eval4j.jdi.test
+
+import org.jetbrains.eval4j.*
+import com.sun.jdi
+import junit.framework.TestSuite
+import org.jetbrains.eval4j.test.buildTestSuite
+import junit.framework.TestCase
+import org.jetbrains.eval4j.interpreterLoop
+import org.junit.Assert.*
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.atomic.AtomicInteger
+import org.jetbrains.eval4j.ExceptionThrown
+import org.jetbrains.eval4j.MethodDescription
+import org.jetbrains.eval4j.ValueReturned
+import org.jetbrains.eval4j.jdi.*
+
+val DEBUGEE_CLASS = javaClass()
+
+fun suite(): TestSuite {
+ val connectors = jdi.Bootstrap.virtualMachineManager().launchingConnectors()
+ val connector = connectors[0]
+ println("Using connector $connector")
+
+ val connectorArgs = connector.defaultArguments()
+
+ val debugeeName = DEBUGEE_CLASS.getName()
+ println("Debugee name: $debugeeName")
+ connectorArgs["main"]!!.setValue(debugeeName)
+ connectorArgs["options"]!!.setValue("-classpath out/production/eval4j:out/test/eval4j")
+ val vm = connector.launch(connectorArgs)!!
+
+ val req = vm.eventRequestManager().createClassPrepareRequest()
+ req.addClassFilter("*.Debugee")
+ req.enable()
+
+ val latch = CountDownLatch(1)
+ var classLoader : jdi.ClassLoaderReference? = null
+ var thread : jdi.ThreadReference? = null
+
+ Thread {
+ val eventQueue = vm.eventQueue()
+ @mainLoop while (true) {
+ val eventSet = eventQueue.remove()
+ for (event in eventSet.eventIterator()) {
+ when (event) {
+ is jdi.event.ClassPrepareEvent -> {
+ val _class = event.referenceType()!!
+ if (_class.name() == debugeeName) {
+ for (l in _class.allLineLocations()) {
+ if (l.method().name() == "main") {
+ classLoader = l.method().declaringType().classLoader()
+ val breakpointRequest = vm.eventRequestManager().createBreakpointRequest(l)
+ breakpointRequest.enable()
+ println("Breakpoint: $breakpointRequest")
+ vm.resume()
+ break
+ }
+ }
+ }
+ }
+ is jdi.event.BreakpointEvent -> {
+ println("Suspended at: " + event.location())
+
+ thread = event.thread()
+ latch.countDown()
+
+ break @mainLoop
+ }
+ else -> {}
+ }
+ }
+ }
+ }.start()
+
+ vm.resume()
+
+ latch.await()
+
+ var remainingTests = AtomicInteger(0)
+
+ val suite = buildTestSuite {
+ methodNode, ownerClass, expected ->
+ remainingTests.incrementAndGet()
+ object : TestCase("test" + methodNode.name.capitalize()) {
+
+ override fun runTest() {
+ val eval = JDIEval(
+ vm, classLoader!!, thread!!
+ )
+ val value = interpreterLoop(
+ methodNode,
+ makeInitialFrame(methodNode, listOf()),
+ eval
+ )
+
+ fun jdi.ObjectReference?.callToString(): String? {
+ if (this == null) return "null"
+ return (eval.invokeMethod(
+ this.asValue(),
+ MethodDescription(
+ "java/lang/Object",
+ "toString",
+ "()Ljava/lang/String;",
+ false
+ ),
+ listOf()).jdiObj as jdi.StringReference).value()
+
+ }
+
+ try {
+ if (value is ExceptionThrown) {
+ val str = value.exception.jdiObj.callToString()
+ System.err.println("Exception: $str")
+ }
+
+ if (expected is ValueReturned && value is ValueReturned && value.result is ObjectValue) {
+ assertEquals(expected.result.obj.toString(), value.result.jdiObj.callToString())
+ }
+ else {
+ assertEquals(expected, value)
+ }
+ }
+ finally {
+ if (remainingTests.decrementAndGet() == 0) vm.resume()
+ }
+
+ }
+ }
+ }
+
+ return suite
+}
\ No newline at end of file
diff --git a/eval4j/test/org/jetbrains/eval4j/test/ArrayHelper.java b/eval4j/test/org/jetbrains/eval4j/test/ArrayHelper.java
new file mode 100644
index 00000000000..cf816d4ebf4
--- /dev/null
+++ b/eval4j/test/org/jetbrains/eval4j/test/ArrayHelper.java
@@ -0,0 +1,16 @@
+package org.jetbrains.eval4j.test;
+
+import java.lang.reflect.Array;
+
+public class ArrayHelper {
+ public static Object newMultiArray(Class> elementType, Integer... dimensions) {
+ int[] dims = new int[dimensions.length];
+ int i = 0;
+ for (Integer dimension : dimensions) {
+ dims[i] = dimension;
+ i++;
+ }
+
+ return Array.newInstance(elementType, dims);
+ }
+}
diff --git a/eval4j/test/org/jetbrains/eval4j/test/TestData.java b/eval4j/test/org/jetbrains/eval4j/test/TestData.java
new file mode 100644
index 00000000000..1b2cf2310d3
--- /dev/null
+++ b/eval4j/test/org/jetbrains/eval4j/test/TestData.java
@@ -0,0 +1,462 @@
+package org.jetbrains.eval4j.test;
+
+class TestData {
+ static void returnVoid() {
+ }
+
+ static boolean returnBoolean() {
+ return true;
+ }
+
+ static byte returnByte() {
+ return 2;
+ }
+
+ static short returnShort() {
+ return 2;
+ }
+
+ static char returnChar() {
+ return '2';
+ }
+
+ static int returnInt() {
+ return 2;
+ }
+
+ static long returnLong() {
+ return 2;
+ }
+
+ static float returnFloat() {
+ return 2.0f;
+ }
+
+ static double returnDouble() {
+ return 2.0d;
+ }
+
+ static Object returnNull() {
+ return null;
+ }
+
+ static String returnString() {
+ return "str";
+ }
+
+ static Object returnStringAsObject() {
+ return "str";
+ }
+
+ static int variable() {
+ int i = 153;
+ return i;
+ }
+
+ static int unaryMinus() {
+ int i = 153;
+ return -i;
+ }
+
+ static int ifThen() {
+ boolean a = true;
+ if (a)
+ return 2;
+ return 1;
+ }
+
+ static int ifElse() {
+ boolean a = false;
+ if (a) {
+ return 2;
+ }
+ else {
+ return 1;
+ }
+ }
+
+ static int loop() {
+ int i = 0;
+ while (i < 10) i++;
+ return i;
+ }
+
+ static int loopWithBreak() {
+ int i = 0;
+ while (true) {
+ if (i > 10) break;
+ i++;
+ }
+ return i;
+ }
+
+ static int loopWithReturn() {
+ int i = 0;
+ while (true) {
+ if (i > 10) return i;
+ i++;
+ }
+ }
+
+ static int simpleFinally() {
+ int i = 5;
+ try {
+ return i;
+ }
+ finally {
+ i = 3;
+ }
+ }
+
+ static int simpleFinallyWithReturn() {
+ int i = 5;
+ try {
+ return i;
+ }
+ finally {
+ return 3;
+ }
+ }
+
+ static int simpleFinallyWithContinueInLoop() {
+ int i = 5;
+ while (true) {
+ try {
+ if (i % 2 == 0) continue;
+ if (i > 10) return i;
+ }
+ finally {
+ i++;
+ }
+ }
+ }
+
+ static int simpleFinallyWithBreakInLoop() {
+ int i = 5;
+ while (true) {
+ try {
+ if (i % 2 == 0) break;
+ }
+ finally {
+ i++;
+ }
+ }
+ return i;
+ }
+
+ static Object call() {
+ return Integer.valueOf(1);
+ }
+
+ static Object callWithObject() {
+ return String.valueOf("str");
+ }
+
+ static Object getStaticField() {
+ return C.FOO;
+ }
+
+ static int FIELD = 0;
+
+ static int putStaticField() {
+ FIELD = 5;
+ int f1 = FIELD;
+ FIELD = 6;
+ int f2 = FIELD;
+ return f2 + f1;
+ }
+
+ static class C {
+ static String FOO = "FOO";
+
+ int y = 15;
+
+ C(int y) {
+ this.y = y;
+ }
+
+ C() {}
+
+ int getY() {
+ return y;
+ }
+
+ static C newC() {
+ return new C();
+ }
+
+ static void throwException() {
+ throw new RuntimeException();
+ }
+ }
+
+ static int getInstanceField() {
+ return C.newC().y;
+ }
+
+ static int putInstanceField() {
+ C c = C.newC();
+ c.y = 5;
+ int f1 = c.y;
+ c.y = 6;
+ int f2 = c.y;
+ return f1 + f2;
+ }
+
+ static int instanceMethod() {
+ return C.newC().getY();
+ }
+
+ static int constructorCallNoArgs() {
+ return new C().y;
+ }
+
+ static int constructorCallWithArgs() {
+ return new C(10).y;
+ }
+
+ static class MyEx extends RuntimeException {
+ final int x;
+
+ MyEx(int x) {
+ this.x = x;
+ }
+ }
+
+ static int tryCatch() {
+ try {
+ throw new MyEx(10);
+ }
+ catch (MyEx e) {
+ return e.x;
+ }
+ }
+
+ static int tryWiderCatch() {
+ int a = 10;
+ try {
+ if (a > 0) {
+ throw new MyEx(10);
+ }
+ } catch (Exception e) {
+ return ((MyEx) e).x;
+ }
+ return 2;
+ }
+
+ static int classCastException() {
+ Object a = "";
+ try {
+ Integer s = (Integer) a;
+ }
+ catch (ClassCastException e) {
+ return 1;
+ }
+ return 2;
+ }
+
+ static String classLiteral() {
+ return String.class.toString();
+ }
+
+ static int callThrowingMethod() {
+ try {
+ C.throwException();
+ }
+ catch (RuntimeException e) {
+ return 1;
+ }
+ return 0;
+ }
+
+ static int NPE() {
+ try {
+ Object x = null;
+ x.toString();
+ }
+ catch (NullPointerException e) {
+ return 1;
+ }
+ return 0;
+ }
+
+ static Class> arrayClass() {
+ return int[].class;
+ }
+
+ static int arrayOfByte() {
+ byte[] a = new byte[] {1, 2, 3};
+ int sum = 0;
+ for (int i : a) {
+ sum += i;
+ }
+ return sum;
+ }
+
+ static int arrayOfShort() {
+ short[] a = new short[] {1, 2, 3};
+ int sum = 0;
+ for (int i : a) {
+ sum += i;
+ }
+ return sum;
+ }
+
+ static int arrayOfChar() {
+ char[] a = new char[] {1, 2, 3};
+ int sum = 0;
+ for (int i : a) {
+ sum += i;
+ }
+ return sum;
+ }
+
+ static int arrayOfInt() {
+ int[] a = new int[] {1, 2, 3};
+ int sum = 0;
+ for (int i : a) {
+ sum += i;
+ }
+ return sum;
+ }
+
+ static int arrayOfLong() {
+ long[] a = new long[] {1, 2, 3};
+ int sum = 0;
+ for (long i : a) {
+ sum += i;
+ }
+ return sum;
+ }
+
+ static float arrayOfFloat() {
+ float[] a = new float[] {1, 2, 3};
+ float sum = 0;
+ for (float i : a) {
+ sum += i;
+ }
+ return sum;
+ }
+
+ static double arrayOfDouble() {
+ double[] a = new double[] {1, 2, 3};
+ double sum = 0;
+ for (double i : a) {
+ sum += i;
+ }
+ return sum;
+ }
+
+ static String arrayOfString() {
+ String[] a = new String[] {"1", "2", "3"};
+ String sum = "";
+ for (String i : a) {
+ sum += i;
+ }
+ return sum;
+ }
+
+ static int arrayOfByte2() {
+ byte[][] a = new byte[][] {{1}, {2}, {3}};
+ int sum = 0;
+ for (byte[] aa: a)
+ for (int i : aa) {
+ sum += i;
+ }
+ return sum;
+ }
+
+ static int arrayOfShort2() {
+ short[][] a = new short[][] {{1}, {2}, {3}};
+ int sum = 0;
+ for (short[] aa: a)
+ for (int i : aa) {
+ sum += i;
+ }
+ return sum;
+ }
+
+ static int arrayOfChar2() {
+ char[][] a = new char[][] {{1}, {2}, {3}};
+ int sum = 0;
+ for (char[] aa: a)
+ for (int i : aa) {
+ sum += i;
+ }
+ return sum;
+ }
+
+ static int arrayOfInt2() {
+ int[][] a = new int[][] {{1}, {2}, {3}};
+ int sum = 0;
+ for (int[] aa: a)
+ for (int i : aa) {
+ sum += i;
+ }
+ return sum;
+ }
+
+ static int arrayOfLong2() {
+ long[][] a = new long[][] {{1}, {2}, {3}};
+ int sum = 0;
+ for (long[] aa: a)
+ for (long i : aa) {
+ sum += i;
+ }
+ return sum;
+ }
+
+ static float arrayOfFloat2() {
+ float[][] a = new float[][] {{1}, {2}, {3}};
+ float sum = 0;
+ for (float[] aa: a)
+ for (float i : aa) {
+ sum += i;
+ }
+ return sum;
+ }
+
+ static double arrayOfDouble2() {
+ double[][] a = new double[][] {{1}, {2}, {3}};
+ double sum = 0;
+ for (double[] aa: a)
+ for (double i : aa) {
+ sum += i;
+ }
+ return sum;
+ }
+
+ static String arrayOfString2() {
+ String[][] a = new String[][] {{"1"}, {"2"}, {"3"}};
+ String sum = "";
+ for (String[] aa: a)
+ for (String i : aa) {
+ sum += i;
+ }
+ return sum;
+ }
+
+ static String multiArrayOfInt() {
+ int[][] a = new int[2][2];
+ String s = "";
+ for (int[] x : a)
+ for (int y : x) {
+ s += y;
+ }
+ return s;
+ }
+
+ static String multiArrayOfString() {
+ String[][] a = new String[2][2];
+ for (String[] x : a)
+ for (int i = 0; i < x.length; i++) {
+ x[i] = i + "";
+ }
+ String s = "";
+ for (String[] x : a)
+ for (String y : x) {
+ s += y;
+ }
+ return s;
+ }
+}
diff --git a/eval4j/test/org/jetbrains/eval4j/test/main.kt b/eval4j/test/org/jetbrains/eval4j/test/main.kt
new file mode 100644
index 00000000000..0b48bcbcfed
--- /dev/null
+++ b/eval4j/test/org/jetbrains/eval4j/test/main.kt
@@ -0,0 +1,356 @@
+package org.jetbrains.eval4j.test
+
+import org.objectweb.asm.*
+import org.objectweb.asm.Opcodes.*
+import org.objectweb.asm.tree.MethodNode
+import java.lang.reflect.Modifier
+import org.jetbrains.eval4j.*
+import org.junit.Assert.*
+import junit.framework.TestSuite
+import junit.framework.TestCase
+import java.lang.reflect.Method
+import java.lang.reflect.Field
+import java.lang.reflect.Constructor
+import java.lang.reflect.InvocationTargetException
+import java.lang.reflect.Array as JArray
+import org.objectweb.asm.tree.analysis.Frame
+
+fun suite(): TestSuite = buildTestSuite {
+ methodNode, ownerClass, expected ->
+ object : TestCase("test" + methodNode.name.capitalize()) {
+
+ override fun runTest() {
+ val value = interpreterLoop(
+ methodNode,
+ initFrame(
+ ownerClass.getInternalName(),
+ methodNode
+ ),
+ REFLECTION_EVAL
+ )
+
+ assertEquals(expected, value)
+ }
+ }
+}
+
+fun Class<*>.getInternalName(): String = Type.getType(this).getInternalName()
+
+fun initFrame(
+ owner: String,
+ m: MethodNode
+): Frame {
+ val current = Frame(m.maxLocals, m.maxStack)
+ current.setReturn(makeNotInitializedValue(Type.getReturnType(m.desc)))
+
+ var local = 0
+ if ((m.access and ACC_STATIC) == 0) {
+ val ctype = Type.getObjectType(owner)
+ current.setLocal(local++, makeNotInitializedValue(ctype))
+ }
+
+ val args = Type.getArgumentTypes(m.desc)
+ for (i in 0..args.size - 1) {
+ current.setLocal(local++, makeNotInitializedValue(args[i]))
+ if (args[i].getSize() == 2) {
+ current.setLocal(local++, NOT_A_VALUE)
+ }
+ }
+
+ while (local < m.maxLocals) {
+ current.setLocal(local++, NOT_A_VALUE)
+ }
+
+ return current
+}
+
+fun objectToValue(obj: Any?, expectedType: Type): Value {
+ return when (expectedType.getSort()) {
+ Type.VOID -> VOID_VALUE
+ Type.BOOLEAN -> boolean(obj as Boolean)
+ Type.BYTE -> byte(obj as Byte)
+ Type.SHORT -> short(obj as Short)
+ Type.CHAR -> char(obj as Char)
+ Type.INT -> int(obj as Int)
+ Type.LONG -> long(obj as Long)
+ Type.DOUBLE -> double(obj as Double)
+ Type.FLOAT -> float(obj as Float)
+ Type.OBJECT -> if (obj == null) NULL_VALUE else ObjectValue(obj, expectedType)
+ else -> throw UnsupportedOperationException("Unsupported result type: $expectedType")
+ }
+}
+
+object REFLECTION_EVAL : Eval {
+
+ val lookup = ReflectionLookup(javaClass().getClassLoader()!!)
+
+ override fun loadClass(classType: Type): Value {
+ return ObjectValue(findClass(classType), Type.getType(javaClass>()))
+ }
+
+ override fun loadString(str: String): Value = ObjectValue(str, Type.getType(javaClass()))
+
+ override fun newInstance(classType: Type): Value {
+ return NewObjectValue(classType)
+ }
+
+ override fun isInstanceOf(value: Value, targetType: Type): Boolean {
+ val _class = findClass(targetType)
+ return _class.isInstance(value.obj)
+ }
+
+ [suppress("UNCHECKED_CAST")]
+ override fun newArray(arrayType: Type, size: Int): Value {
+ return ObjectValue(JArray.newInstance(findClass(arrayType).getComponentType() as Class, size), arrayType)
+ }
+
+ override fun newMultiDimensionalArray(arrayType: Type, dimensionSizes: List): Value {
+ return ObjectValue(ArrayHelper.newMultiArray(findClass(arrayType.getElementType()), *dimensionSizes.copyToArray()), arrayType)
+ }
+
+ override fun getArrayLength(array: Value): Value {
+ return int(JArray.getLength(array.obj.checkNull()))
+ }
+
+ override fun getArrayElement(array: Value, index: Value): Value {
+ val asmType = array.asmType
+ val elementType = if (asmType.getDimensions() == 1) asmType.getElementType() else Type.getType(asmType.getDescriptor().substring(1))
+ val arr = array.obj.checkNull()
+ val ind = index.int
+ return when (elementType.getSort()) {
+ Type.BOOLEAN -> boolean(JArray.getBoolean(arr, ind))
+ Type.BYTE -> byte(JArray.getByte(arr, ind))
+ Type.SHORT -> short(JArray.getShort(arr, ind))
+ Type.CHAR -> char(JArray.getChar(arr, ind))
+ Type.INT -> int(JArray.getInt(arr, ind))
+ Type.LONG -> long(JArray.getLong(arr, ind))
+ Type.FLOAT -> float(JArray.getFloat(arr, ind))
+ Type.DOUBLE -> double(JArray.getDouble(arr, ind))
+ Type.OBJECT,
+ Type.ARRAY -> {
+ val value = JArray.get(arr, ind)
+ if (value == null) NULL_VALUE else ObjectValue(value, Type.getType(value.javaClass))
+ }
+ else -> throw UnsupportedOperationException("Unsupported array element type: $elementType")
+ }
+ }
+
+ override fun setArrayElement(array: Value, index: Value, newValue: Value) {
+ val arr = array.obj.checkNull()
+ val ind = index.int
+ if (array.asmType.getDimensions() > 1) {
+ JArray.set(arr, ind, newValue.obj)
+ return
+ }
+ val elementType = array.asmType.getElementType()
+ when (elementType.getSort()) {
+ Type.BOOLEAN -> JArray.setBoolean(arr, ind, newValue.boolean)
+ Type.BYTE -> JArray.setByte(arr, ind, newValue.int.toByte())
+ Type.SHORT -> JArray.setShort(arr, ind, newValue.int.toShort())
+ Type.CHAR -> JArray.setChar(arr, ind, newValue.int.toChar())
+ Type.INT -> JArray.setInt(arr, ind, newValue.int)
+ Type.LONG -> JArray.setLong(arr, ind, newValue.long)
+ Type.FLOAT -> JArray.setFloat(arr, ind, newValue.float)
+ Type.DOUBLE -> JArray.setDouble(arr, ind, newValue.double)
+ Type.OBJECT,
+ Type.ARRAY -> {
+ JArray.set(arr, ind, newValue.obj)
+ }
+ else -> throw UnsupportedOperationException("Unsupported array element type: $elementType")
+ }
+ }
+
+ fun mayThrow(f: () -> T): T {
+ try {
+ try {
+ return f()
+ }
+ catch (ite: InvocationTargetException) {
+ throw ite.getCause() ?: ite
+ }
+ }
+ catch (e: Throwable) {
+ throw ThrownFromEvaluatedCodeException(ObjectValue(e, Type.getType(e.javaClass)))
+ }
+ }
+
+ override fun getStaticField(fieldDesc: FieldDescription): Value {
+ val field = findStaticField(fieldDesc)
+
+ val result = mayThrow {field.get(null)}
+ return objectToValue(result, fieldDesc.fieldType)
+ }
+
+ override fun setStaticField(fieldDesc: FieldDescription, newValue: Value) {
+ val field = findStaticField(fieldDesc)
+ val obj = newValue.obj
+ mayThrow {field.set(null, obj)}
+ }
+
+ fun findStaticField(fieldDesc: FieldDescription): Field {
+ assertTrue(fieldDesc.isStatic)
+ val field = findClass(fieldDesc).findField(fieldDesc)
+ assertNotNull("Field not found: $fieldDesc", field)
+ assertTrue("Field is not static: $field", (field!!.getModifiers() and Modifier.STATIC) != 0)
+ return field
+ }
+
+ override fun invokeStaticMethod(methodDesc: MethodDescription, arguments: List): Value {
+ assertTrue(methodDesc.isStatic)
+ val method = findClass(methodDesc).findMethod(methodDesc)
+ assertNotNull("Method not found: $methodDesc", method)
+ val args = arguments.map { v -> v.obj }.copyToArray()
+ val result = mayThrow {method!!.invoke(null, *args)}
+ return objectToValue(result, methodDesc.returnType)
+ }
+
+ fun findClass(memberDesc: MemberDescription): Class = findClass(Type.getObjectType(memberDesc.ownerInternalName))
+
+ fun findClass(asmType: Type): Class {
+ val owner = lookup.findClass(asmType)
+ assertNotNull("Class not found: ${asmType}", owner)
+ return owner as Class
+ }
+
+ override fun getField(instance: Value, fieldDesc: FieldDescription): Value {
+ val obj = instance.obj.checkNull()
+ val field = findInstanceField(obj, fieldDesc)
+
+ return objectToValue(mayThrow {field.get(obj)}, fieldDesc.fieldType)
+ }
+
+ override fun setField(instance: Value, fieldDesc: FieldDescription, newValue: Value) {
+ val obj = instance.obj.checkNull()
+ val field = findInstanceField(obj, fieldDesc)
+
+ val newObj = newValue.obj
+ mayThrow {field.set(obj, newObj)}
+ }
+
+ fun findInstanceField(obj: Any, fieldDesc: FieldDescription): Field {
+ val _class = obj.javaClass
+ val field = _class.findField(fieldDesc)
+ assertNotNull("Field not found: $fieldDesc", field)
+ return field!!
+ }
+
+ override fun invokeMethod(instance: Value, methodDesc: MethodDescription, arguments: List, invokespecial: Boolean): Value {
+ if (invokespecial) {
+ if (methodDesc.name == "") {
+ // Constructor call
+ [suppress("UNCHECKED_CAST")]
+ val _class = findClass((instance as NewObjectValue).asmType)
+ val ctor = _class.findConstructor(methodDesc)
+ assertNotNull("Constructor not found: $methodDesc", ctor)
+ val args = arguments.map { v -> v.obj }.copyToArray()
+ val result = mayThrow {ctor!!.newInstance(*args)}
+ instance.value = result
+ return objectToValue(result, instance.asmType)
+ }
+ else {
+ // TODO
+ throw UnsupportedOperationException("invokespecial is not suported yet")
+ }
+ }
+ val obj = instance.obj.checkNull()
+ val method = obj.javaClass.findMethod(methodDesc)
+ assertNotNull("Method not found: $methodDesc", method)
+ val args = arguments.map { v -> v.obj }.copyToArray()
+ val result = mayThrow {method!!.invoke(obj, *args)}
+ return objectToValue(result, methodDesc.returnType)
+ }
+}
+
+class ReflectionLookup(val classLoader: ClassLoader) {
+ [suppress("UNCHECKED_CAST")]
+ fun findClass(asmType: Type): Class<*>? {
+ return when (asmType.getSort()) {
+ Type.BOOLEAN -> java.lang.Boolean.TYPE
+ Type.BYTE -> java.lang.Byte.TYPE
+ Type.SHORT -> java.lang.Short.TYPE
+ Type.CHAR -> java.lang.Character.TYPE
+ Type.INT -> java.lang.Integer.TYPE
+ Type.LONG -> java.lang.Long.TYPE
+ Type.FLOAT -> java.lang.Float.TYPE
+ Type.DOUBLE -> java.lang.Double.TYPE
+ Type.OBJECT -> classLoader.loadClass(asmType.getInternalName().replace('/', '.'))
+ Type.ARRAY -> Class.forName(asmType.getDescriptor().replace('/', '.'))
+ else -> throw UnsupportedOperationException("Unsupported type: $asmType")
+ }
+ }
+}
+
+[suppress("UNCHECKED_CAST")]
+fun Class.findMethod(methodDesc: MethodDescription): Method? {
+ for (declared in getDeclaredMethods()) {
+ if (methodDesc.matches(declared)) return declared
+ }
+
+ val fromSuperClass = (getSuperclass() as Class).findMethod(methodDesc)
+ if (fromSuperClass != null) return fromSuperClass
+
+ for (supertype in getInterfaces()) {
+ val fromSuper = (supertype as Class).findMethod(methodDesc)
+ if (fromSuper != null) return fromSuper
+ }
+
+ return null
+}
+
+[suppress("UNCHECKED_CAST")]
+fun Class.findConstructor(methodDesc: MethodDescription): Constructor? {
+ for (declared in getDeclaredConstructors()) {
+ if (methodDesc.matches(declared)) return declared as Constructor
+ }
+ return null
+}
+
+fun MethodDescription.matches(ctor: Constructor<*>): Boolean {
+ val methodParams = ctor.getParameterTypes()!!
+ if (parameterTypes.size() != methodParams.size) return false
+ for ((i, p) in parameterTypes.withIndices()) {
+ if (!p.matches(methodParams[i])) return false
+ }
+
+ return true;
+}
+
+fun MethodDescription.matches(method: Method): Boolean {
+ if (name != method.getName()) return false
+
+ val methodParams = method.getParameterTypes()!!
+ if (parameterTypes.size() != methodParams.size) return false
+ for ((i, p) in parameterTypes.withIndices()) {
+ if (!p.matches(methodParams[i])) return false
+ }
+
+ return returnType.matches(method.getReturnType()!!)
+}
+
+[suppress("UNCHECKED_CAST")]
+fun Class.findField(fieldDesc: FieldDescription): Field? {
+ for (declared in getDeclaredFields()) {
+ if (fieldDesc.matches(declared)) return declared
+ }
+
+ val superclass = getSuperclass()
+ if (superclass != null) {
+ val fromSuperClass = (superclass as Class).findField(fieldDesc)
+ if (fromSuperClass != null) return fromSuperClass
+ }
+
+ for (supertype in getInterfaces()) {
+ val fromSuper = (supertype as Class).findField(fieldDesc)
+ if (fromSuper != null) return fromSuper
+ }
+
+ return null
+}
+
+fun FieldDescription.matches(field: Field): Boolean {
+ if (name != field.getName()) return false
+
+ return fieldType.matches(field.getType()!!)
+}
+
+fun Type.matches(_class: Class<*>): Boolean = this == Type.getType(_class)
\ No newline at end of file
diff --git a/eval4j/test/org/jetbrains/eval4j/test/suiteBuilder.kt b/eval4j/test/org/jetbrains/eval4j/test/suiteBuilder.kt
new file mode 100644
index 00000000000..8b26aa120b8
--- /dev/null
+++ b/eval4j/test/org/jetbrains/eval4j/test/suiteBuilder.kt
@@ -0,0 +1,76 @@
+package org.jetbrains.eval4j.test
+
+import org.objectweb.asm.*
+import org.objectweb.asm.Opcodes.*
+import org.objectweb.asm.tree.MethodNode
+import java.lang.reflect.Modifier
+import org.jetbrains.eval4j.*
+import org.junit.Assert.*
+import junit.framework.TestSuite
+import junit.framework.TestCase
+import java.lang.reflect.Method
+import java.lang.reflect.Field
+import java.lang.reflect.Constructor
+import java.lang.reflect.InvocationTargetException
+import java.lang.reflect.Array as JArray
+import org.objectweb.asm.tree.analysis.Interpreter
+import org.objectweb.asm.tree.analysis.Frame
+
+fun buildTestSuite(
+ create: (MethodNode, Class<*>, InterpreterResult?) -> TestCase
+): TestSuite {
+ val suite = TestSuite()
+
+ val ownerClass = javaClass()
+ val inputStream = ownerClass.getClassLoader()!!.getResourceAsStream(ownerClass.getInternalName() + ".class")!!
+
+ ClassReader(inputStream).accept(object : ClassVisitor(ASM4) {
+
+ override fun visitMethod(access: Int, name: String, desc: String, signature: String?, exceptions: Array?): MethodVisitor? {
+ return object : MethodNode(access, name, desc, signature, exceptions) {
+ override fun visitEnd() {
+ val testCase = buildTestCase(ownerClass, this, create)
+ if (testCase != null) {
+ suite.addTest(testCase)
+ }
+ }
+ }
+ }
+ }, 0)
+
+ return suite
+}
+
+fun buildTestCase(ownerClass: Class,
+ methodNode: MethodNode,
+ create: (MethodNode, Class, InterpreterResult?) -> TestCase): TestCase? {
+ var expected: InterpreterResult? = null
+ for (method in ownerClass.getDeclaredMethods()) {
+ if (method.getName() == methodNode.name) {
+ if ((method.getModifiers() and Modifier.STATIC) == 0) {
+ println("Skipping instance method: $method")
+ }
+ else if (method.getParameterTypes()!!.size > 0) {
+ println("Skipping method with parameters: $method")
+ }
+ else {
+ method.setAccessible(true)
+ val result = method.invoke(null)
+ val returnType = Type.getType(method.getReturnType()!!)
+ try {
+ expected = ValueReturned(objectToValue(result, returnType))
+ }
+ catch (e: UnsupportedOperationException) {
+ println("Skipping $method: $e")
+ }
+ }
+ }
+ }
+
+ if (expected == null) {
+ println("Method not found: ${methodNode.name}")
+ return null
+ }
+
+ return create(methodNode, ownerClass, expected)
+}
\ No newline at end of file