From ac3bb4279ed2a35f9a7d07dd708b07416106bc32 Mon Sep 17 00:00:00 2001
From: Ross Bamford <roscopeco@gmail.com>
Date: Mon, 28 May 2018 10:22:20 +0100
Subject: [PATCH 1/3] Ignore eclipse files

---
 .gitignore | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/.gitignore b/.gitignore
index 064a6aa..20cd526 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,10 @@
 .idea
 *.iml
 
+.project
+.settings
+.classpath
+
 out
 generated
 generated-*

From f1733550da1922b271ce2e36a44a0269aa9491c0 Mon Sep 17 00:00:00 2001
From: Ross Bamford <roscopeco@gmail.com>
Date: Mon, 28 May 2018 11:53:34 +0100
Subject: [PATCH 2/3] Add symbolic references test

---
 pom.xml                                       |  1 +
 symbolic-references/README.md                 | 32 ++++++
 symbolic-references/pom.xml                   | 30 ++++++
 .../java9/symbolic_references/TestClass.java  |  5 +
 .../TestClassGenerator.java                   | 97 +++++++++++++++++++
 .../symbolic_references/CheckCastTest.java    | 19 ++++
 6 files changed, 184 insertions(+)
 create mode 100644 symbolic-references/README.md
 create mode 100644 symbolic-references/pom.xml
 create mode 100644 symbolic-references/src/main/java/wtf/java9/symbolic_references/TestClass.java
 create mode 100644 symbolic-references/src/main/java/wtf/java9/symbolic_references/TestClassGenerator.java
 create mode 100644 symbolic-references/src/test/java/wtf/java9/symbolic_references/CheckCastTest.java

diff --git a/pom.xml b/pom.xml
index 307cee6..fb528fd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,6 +24,7 @@
 		<module>noto-sans</module>
 		<module>tycho-jdt</module>
 		<module>xml-transformer</module>
+		<module>symbolic-references</module>
 	</modules>
 
 	<dependencies>
diff --git a/symbolic-references/README.md b/symbolic-references/README.md
new file mode 100644
index 0000000..6835d0f
--- /dev/null
+++ b/symbolic-references/README.md
@@ -0,0 +1,32 @@
+# Symbolic References
+
+This is related to the class loading tests, and really falls into the "Should Break" category,
+as previous behaviour was not strictly to-spec.
+
+## Stricter interpretation of Symbolic References in .class files
+
+Java 9 appears to be more strict in its interpretation of the term "Symbolic Reference" in
+class files. The spec says:
+
+> For a nonarray class or an interface, the name is the fully qualified name of the class or interface.
+
+(This is expected in "binary format", with forward-slashes instead of periods, for example
+`java/lang/String`.)
+
+In Java 8, you could get away with using a _Type Descriptor_ when really you should have
+used a symbolic reference. A good example is CHECKCAST (which is used in this test). 
+Under Java 8, the instruction could have referenced a constant pool entry with either
+`java/lang/String` (to spec) or `Ljava/lang/String;` (a descriptor, and not strictly
+to spec) and it would work fine. 
+
+Under Java 9, such classes will not load. You'll instead receive a `ClassFormatError`:
+
+```
+[ERROR] loadClassWithDescriptorCheckcast  Time elapsed: 0.031 s  <<< ERROR!
+java.lang.RuntimeException: Unrecoverable Error
+        at wtf.java9.symbolic_references.CheckCastTest.loadClassWithDescriptorCheckcast(CheckCastTest.java:13)
+Caused by: java.lang.reflect.InvocationTargetException
+        at wtf.java9.symbolic_references.CheckCastTest.loadClassWithDescriptorCheckcast(CheckCastTest.java:13)
+Caused by: java.lang.ClassFormatError: Illegal class name "Ljava/lang/String;" in class file TestClassImpl
+        at wtf.java9.symbolic_references.CheckCastTest.loadClassWithDescriptorCheckcast(CheckCastTest.java:13)
+```
diff --git a/symbolic-references/pom.xml b/symbolic-references/pom.xml
new file mode 100644
index 0000000..109764e
--- /dev/null
+++ b/symbolic-references/pom.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+
+	<groupId>wtf.java9</groupId>
+	<artifactId>symbolic-references</artifactId>
+	<version>1.0-SNAPSHOT</version>
+
+	<parent>
+		<groupId>wtf.java9</groupId>
+		<artifactId>seriously-wtf</artifactId>
+		<version>1.0-SNAPSHOT</version>
+		<relativePath>../pom.xml</relativePath>
+	</parent>
+	
+	<dependencies>
+		<dependency>
+			<groupId>org.ow2.asm</groupId>
+			<artifactId>asm</artifactId>
+			<version>6.2</version>
+		</dependency>
+		<dependency>
+			<groupId>org.ow2.asm</groupId>
+			<artifactId>asm-tree</artifactId>
+			<version>6.2</version>
+		</dependency>
+	</dependencies>
+</project>
diff --git a/symbolic-references/src/main/java/wtf/java9/symbolic_references/TestClass.java b/symbolic-references/src/main/java/wtf/java9/symbolic_references/TestClass.java
new file mode 100644
index 0000000..35316a3
--- /dev/null
+++ b/symbolic-references/src/main/java/wtf/java9/symbolic_references/TestClass.java
@@ -0,0 +1,5 @@
+package wtf.java9.symbolic_references;
+
+public interface TestClass {
+  public String generatedMethod(Object o);
+}
diff --git a/symbolic-references/src/main/java/wtf/java9/symbolic_references/TestClassGenerator.java b/symbolic-references/src/main/java/wtf/java9/symbolic_references/TestClassGenerator.java
new file mode 100644
index 0000000..2414300
--- /dev/null
+++ b/symbolic-references/src/main/java/wtf/java9/symbolic_references/TestClassGenerator.java
@@ -0,0 +1,97 @@
+package wtf.java9.symbolic_references;
+
+import static org.objectweb.asm.Opcodes.*;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.tree.ClassNode;
+
+public class TestClassGenerator {
+  private static final Method defineClass;       
+  static {
+    try {
+      defineClass = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
+    } catch (NoSuchMethodException e) {
+      throw new IllegalStateException("Unrecoverable Error: NoSuchMethodException 'defineClass' on ClassLoader.\n"
+          + "This is most likely an environment issue.", e);
+    }
+    defineClass.setAccessible(true);
+  }
+
+  public Class<? extends TestClass> generateClass() {
+    return defineClass(getClass().getClassLoader());
+    
+  }
+
+  byte[] generateBytecode() {
+    ClassNode cv = new ClassNode();
+    
+    cv.visit(V1_8, 
+             ACC_PUBLIC | ACC_SYNTHETIC | ACC_SUPER, 
+             "TestClassImpl", 
+             null, 
+             "java/lang/Object", 
+             new String[] { "wtf/java9/symbolic_references/TestClass" });
+    
+    generateConstructor(cv);
+    generateGeneratedMethod(cv);
+    
+    cv.visitEnd();
+    
+    ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
+    cv.accept(writer);
+    return writer.toByteArray();    
+  }
+  
+  void generateConstructor(ClassVisitor cv) {
+    MethodVisitor mv = cv.visitMethod(ACC_PUBLIC | ACC_SYNTHETIC, 
+        "<init>", 
+        "()V",
+        "", 
+        null);
+
+    mv.visitCode();
+    mv.visitVarInsn(ALOAD, 0);
+    mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+    mv.visitInsn(RETURN);
+    mv.visitEnd();        
+  }
+  
+  void generateGeneratedMethod(ClassVisitor cv) {
+    MethodVisitor mv = cv.visitMethod(ACC_PUBLIC | ACC_SYNTHETIC, 
+        "generatedMethod", 
+        "(Ljava/lang/Object;)Ljava/lang/String;",
+        "", 
+        null);
+
+    mv.visitCode();
+    mv.visitVarInsn(ALOAD, 1);
+    
+    // The following line initiates the failure. Java 9 is 
+    // more strict here and requires an internal name
+    // rather than a descriptor. So change to "java/lang/String".
+    mv.visitTypeInsn(CHECKCAST, "Ljava/lang/String;"); 
+    
+    mv.visitInsn(ARETURN);
+    mv.visitEnd();    
+  }
+
+  @SuppressWarnings("unchecked")
+  Class<? extends TestClass> defineClass(ClassLoader loader) {
+    byte[] code = generateBytecode();
+    try {
+      return (Class<? extends TestClass>)defineClass.invoke(loader, "TestClassImpl", code, 0, code.length);      
+    } catch (InvocationTargetException e) {
+      System.err.println("InvocationTargetException: in defineClass: " + e.getMessage());      
+      throw new RuntimeException("Unrecoverable Error", e);
+    } catch (IllegalAccessException e) {
+      System.err.println("IllegalAccessException: in defineClass: " + e.getMessage());      
+      throw new RuntimeException("Unrecoverable Error", e);
+    }
+  }
+
+}
diff --git a/symbolic-references/src/test/java/wtf/java9/symbolic_references/CheckCastTest.java b/symbolic-references/src/test/java/wtf/java9/symbolic_references/CheckCastTest.java
new file mode 100644
index 0000000..cfec34a
--- /dev/null
+++ b/symbolic-references/src/test/java/wtf/java9/symbolic_references/CheckCastTest.java
@@ -0,0 +1,19 @@
+package wtf.java9.symbolic_references;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.jupiter.api.Test;
+
+import wtf.java9.symbolic_references.TestClass;
+import wtf.java9.symbolic_references.TestClassGenerator;
+
+public class CheckCastTest {
+  
+  /* Works under Java < 9, fails with ClassFormatError under Java 9. */
+  @Test
+  public void loadClassWithDescriptorCheckcast() throws Exception {
+    Class<? extends TestClass> clz = new TestClassGenerator().generateClass();    
+    TestClass testClass = clz.newInstance();
+    assertThat(testClass.generatedMethod("hello")).isEqualTo("hello");
+  }
+}

From d371b3e253fb4751181419e40e5b6fe6cfe5b59b Mon Sep 17 00:00:00 2001
From: Ross Bamford <roscopeco@gmail.com>
Date: Mon, 28 May 2018 19:21:30 +0100
Subject: [PATCH 3/3] Add link to symbolic references

---
 README.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/README.md b/README.md
index 3e2f755..947055c 100644
--- a/README.md
+++ b/README.md
@@ -19,6 +19,7 @@ So far we have "trouble" with...
 * [Noto Sans](noto-sans)
 * [weird Tycho/JDT behavior](tycho-jdt)
 * [XML transformations](xml-transformer)
+* [Symbolic References](symbolic-references)
 
 ## Observe!