Created
June 18, 2020 08:01
-
-
Save TuomasKiviaho/3d6dae166e85c4aaba94afb0318b4e4d to your computer and use it in GitHub Desktop.
sirthias/parboiled: JDK11 reporting illegal reflective access #128
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/parboiled-java/src/main/java/org/parboiled/transform/ASMSettings.java b/parboiled-java/src/main/java/org/parboiled/transform/ASMSettings.java | |
index af0876f..f16a66a 100644 | |
--- a/parboiled-java/src/main/java/org/parboiled/transform/ASMSettings.java | |
+++ b/parboiled-java/src/main/java/org/parboiled/transform/ASMSettings.java | |
@@ -9,6 +9,7 @@ | |
public class ASMSettings | |
{ | |
public static final int ASM_API = Opcodes.ASM7; | |
+ @Deprecated | |
public static final int JDK_VERSION = Opcodes.V1_7; | |
public static final int FRAMES = ClassWriter.COMPUTE_FRAMES; | |
} | |
diff --git a/parboiled-java/src/main/java/org/parboiled/transform/ActionClassGenerator.java b/parboiled-java/src/main/java/org/parboiled/transform/ActionClassGenerator.java | |
index 9f310c2..a513655 100644 | |
--- a/parboiled-java/src/main/java/org/parboiled/transform/ActionClassGenerator.java | |
+++ b/parboiled-java/src/main/java/org/parboiled/transform/ActionClassGenerator.java | |
@@ -17,6 +17,11 @@ | |
package org.parboiled.transform; | |
import static org.parboiled.common.Preconditions.*; | |
+ | |
+import java.util.function.BiConsumer; | |
+import java.util.function.IntSupplier; | |
+import java.util.function.Supplier; | |
+ | |
import org.objectweb.asm.ClassWriter; | |
import org.objectweb.asm.MethodVisitor; | |
import org.objectweb.asm.Type; | |
@@ -24,10 +29,15 @@ | |
class ActionClassGenerator extends GroupClassGenerator { | |
+ @Deprecated | |
public ActionClassGenerator(boolean forceCodeBuilding) { | |
super(forceCodeBuilding); | |
} | |
+ public ActionClassGenerator(boolean forceCodeBuilding, BiConsumer<String, Supplier<byte[]>> classInjector, IntSupplier classFileVersion) { | |
+ super(forceCodeBuilding, classInjector, classFileVersion); | |
+ } | |
+ | |
public boolean appliesTo(ParserClassNode classNode, RuleMethod method) { | |
checkArgNotNull(method, "method"); | |
return method.containsExplicitActions(); | |
diff --git a/parboiled-java/src/main/java/org/parboiled/transform/AsmUtils.java b/parboiled-java/src/main/java/org/parboiled/transform/AsmUtils.java | |
index 0ec279c..90fdc7e 100644 | |
--- a/parboiled-java/src/main/java/org/parboiled/transform/AsmUtils.java | |
+++ b/parboiled-java/src/main/java/org/parboiled/transform/AsmUtils.java | |
@@ -199,6 +199,7 @@ | |
* @param classLoader the class loader to use | |
* @return the class instance or null | |
*/ | |
+ @Deprecated | |
public static Class<?> findLoadedClass(String className, ClassLoader classLoader) { | |
checkArgNotNull(className, "className"); | |
checkArgNotNull(classLoader, "classLoader"); | |
@@ -231,6 +232,7 @@ | |
* @param classLoader the class loader to use | |
* @return the class instance | |
*/ | |
+ @Deprecated | |
public static Class<?> loadClass(String className, byte[] code, ClassLoader classLoader) { | |
checkArgNotNull(className, "className"); | |
checkArgNotNull(code, "code"); | |
diff --git a/parboiled-java/src/main/java/org/parboiled/transform/ClassNodeInitializer.java b/parboiled-java/src/main/java/org/parboiled/transform/ClassNodeInitializer.java | |
index aae55a9..4fc8c61 100644 | |
--- a/parboiled-java/src/main/java/org/parboiled/transform/ClassNodeInitializer.java | |
+++ b/parboiled-java/src/main/java/org/parboiled/transform/ClassNodeInitializer.java | |
@@ -28,6 +28,7 @@ | |
import org.parboiled.support.Checks; | |
import java.io.IOException; | |
+import java.util.function.IntSupplier; | |
import static org.parboiled.transform.AsmUtils.createClassReader; | |
import static org.parboiled.transform.AsmUtils.getExtendedParserClassName; | |
@@ -37,6 +38,8 @@ | |
* Initializes the basic ParserClassNode fields and collects all methods. | |
*/ | |
class ClassNodeInitializer extends ClassVisitor { | |
+ | |
+ private IntSupplier classFileVersion; | |
private ParserClassNode classNode; | |
private Class<?> ownerClass; | |
@@ -45,8 +48,17 @@ | |
private boolean hasDontLabelAnnotation; | |
private boolean hasSkipActionsInPredicates; | |
+ @Deprecated | |
public ClassNodeInitializer() { | |
super(ASMSettings.ASM_API); | |
+ classFileVersion = () -> ASMSettings.JDK_VERSION; | |
+ } | |
+ | |
+ public ClassNodeInitializer(IntSupplier classFileVersion) { | |
+ this(); | |
+ if (classFileVersion != null) { | |
+ this.classFileVersion = classFileVersion; | |
+ } | |
} | |
public void process(ParserClassNode classNode) throws IOException { | |
@@ -87,7 +99,7 @@ | |
Checks.ensure((access & ACC_PRIVATE) == 0, "Parser class '%s' must not be private", name); | |
Checks.ensure((access & ACC_FINAL) == 0, "Parser class '%s' must not be final.", name); | |
classNode.visit( | |
- ASMSettings.JDK_VERSION, | |
+ classFileVersion.getAsInt(), | |
ACC_PUBLIC, | |
getExtendedParserClassName(name), | |
null, | |
diff --git a/parboiled-java/src/main/java/org/parboiled/transform/GroupClassGenerator.java b/parboiled-java/src/main/java/org/parboiled/transform/GroupClassGenerator.java | |
index 2e6c460..a5ef3f9 100644 | |
--- a/parboiled-java/src/main/java/org/parboiled/transform/GroupClassGenerator.java | |
+++ b/parboiled-java/src/main/java/org/parboiled/transform/GroupClassGenerator.java | |
@@ -17,6 +17,11 @@ | |
package org.parboiled.transform; | |
import static org.parboiled.common.Preconditions.*; | |
+ | |
+import java.util.function.BiConsumer; | |
+import java.util.function.IntSupplier; | |
+import java.util.function.Supplier; | |
+ | |
import org.objectweb.asm.ClassWriter; | |
import org.objectweb.asm.MethodVisitor; | |
import org.objectweb.asm.Type; | |
@@ -28,16 +33,45 @@ | |
abstract class GroupClassGenerator implements RuleMethodProcessor { | |
- private static final Object lock = new Object(); | |
+ private BiConsumer<String, Supplier<byte[]>> classInjector; | |
+ private IntSupplier classFileVersion; | |
private final boolean forceCodeBuilding; | |
protected ParserClassNode classNode; | |
protected RuleMethod method; | |
+ @Deprecated | |
protected GroupClassGenerator(boolean forceCodeBuilding) { | |
this.forceCodeBuilding = forceCodeBuilding; | |
+ Object lock = new Object(); | |
+ this.classInjector = (className, groupClassCodeGenerator) -> { | |
+ ClassLoader classLoader = classNode.getParentClass().getClassLoader(); | |
+ | |
+ Class<?> groupClass; | |
+ synchronized (lock) { | |
+ groupClass = findLoadedClass(className, classLoader); | |
+ if (groupClass == null) { | |
+ byte[] groupClassCode = groupClassCodeGenerator.get(); | |
+ | |
+ if (groupClass == null) { | |
+ loadClass(className, groupClassCode, classLoader); | |
+ } | |
+ } | |
+ } | |
+ }; | |
+ this.classFileVersion = () -> ASMSettings.JDK_VERSION; | |
} | |
+ protected GroupClassGenerator(boolean forceCodeBuilding, BiConsumer<String, Supplier<byte[]>> classInjector, IntSupplier classFileVersion) { | |
+ this(forceCodeBuilding); | |
+ if (classInjector != null) { | |
+ this.classInjector = classInjector; | |
+ } | |
+ if (classFileVersion != null) { | |
+ this.classFileVersion = classFileVersion; | |
+ } | |
+ } | |
+ | |
public void process(ParserClassNode classNode, RuleMethod method) { | |
this.classNode = checkArgNotNull(classNode, "classNode"); | |
this.method = checkArgNotNull(method, "method"); | |
@@ -54,18 +88,18 @@ | |
private void loadGroupClass(InstructionGroup group) { | |
createGroupClassType(group); | |
String className = group.getGroupClassType().getClassName(); | |
- ClassLoader classLoader = classNode.getParentClass().getClassLoader(); | |
- Class<?> groupClass; | |
- synchronized (lock) { | |
- groupClass = findLoadedClass(className, classLoader); | |
- if (groupClass == null || forceCodeBuilding) { | |
- byte[] groupClassCode = generateGroupClassCode(group); | |
+ Supplier<byte[]> groupClassCodeGenerator = () -> { | |
+ byte[] groupClassCode = group.getGroupClassCode(); | |
+ if (groupClassCode == null) { | |
+ groupClassCode = generateGroupClassCode(group); | |
group.setGroupClassCode(groupClassCode); | |
- if (groupClass == null) { | |
- loadClass(className, groupClassCode, classLoader); | |
- } | |
} | |
+ return groupClassCode; | |
+ }; | |
+ classInjector.accept(className, groupClassCodeGenerator); | |
+ if (forceCodeBuilding) { | |
+ groupClassCodeGenerator.get(); | |
} | |
} | |
@@ -87,7 +121,7 @@ | |
} | |
private void generateClassBasics(InstructionGroup group, ClassWriter cw) { | |
- cw.visit(ASMSettings.JDK_VERSION, ACC_PUBLIC + ACC_FINAL + ACC_SYNTHETIC, group.getGroupClassType().getInternalName(), null, | |
+ cw.visit(classFileVersion.getAsInt(), ACC_PUBLIC + ACC_FINAL + ACC_SYNTHETIC, group.getGroupClassType().getInternalName(), null, | |
getBaseType().getInternalName(), null); | |
cw.visitSource(classNode.sourceFile, null); | |
} | |
diff --git a/parboiled-java/src/main/java/org/parboiled/transform/ParserTransformer.java b/parboiled-java/src/main/java/org/parboiled/transform/ParserTransformer.java | |
index 16e9d52..03c6b82 100644 | |
--- a/parboiled-java/src/main/java/org/parboiled/transform/ParserTransformer.java | |
+++ b/parboiled-java/src/main/java/org/parboiled/transform/ParserTransformer.java | |
@@ -21,6 +21,10 @@ | |
import org.parboiled.common.ImmutableList; | |
import java.util.List; | |
+import java.util.function.BiConsumer; | |
+import java.util.function.BiFunction; | |
+import java.util.function.IntSupplier; | |
+import java.util.function.Supplier; | |
import static org.parboiled.transform.AsmUtils.*; | |
@@ -29,6 +33,7 @@ | |
private ParserTransformer() {} | |
@SuppressWarnings({"unchecked"}) | |
+ @Deprecated | |
public static synchronized <T> Class<? extends T> transformParser(Class<T> parserClass) throws Exception { | |
checkArgNotNull(parserClass, "parserClass"); | |
// first check whether we did not already create and load the extension of the given parser class | |
@@ -38,19 +43,30 @@ | |
return (Class<? extends T>) | |
(extendedClass != null ? extendedClass : extendParserClass(parserClass).getExtendedClass()); | |
} | |
+ | |
+ public static synchronized <T> Class<? extends T> transformParser(Class<T> parserClass, BiFunction<String, Supplier<byte[]>, Class<?>> classInjector, IntSupplier classFileVersion) throws Exception { | |
+ checkArgNotNull(parserClass, "parserClass"); | |
+ checkArgNotNull(classInjector, "classInjector"); | |
+ checkArgNotNull(classFileVersion, "classFileVersion"); | |
+ return extendParserClass(parserClass, classInjector, classFileVersion).getExtendedClass().asSubclass(parserClass); | |
+ } | |
+ @Deprecated | |
static ParserClassNode extendParserClass(Class<?> parserClass) throws Exception { | |
+ return extendParserClass(parserClass, null, null); | |
+ } | |
+ | |
+ private static ParserClassNode extendParserClass(Class<?> parserClass, BiFunction<String, Supplier<byte[]>, Class<?>> classInjector, IntSupplier classFileVersion) throws Exception { | |
ParserClassNode classNode = new ParserClassNode(parserClass); | |
- new ClassNodeInitializer().process(classNode); | |
- runMethodTransformers(classNode); | |
+ new ClassNodeInitializer(classFileVersion).process(classNode); | |
+ runMethodTransformers(classNode, classInjector::apply, classFileVersion); | |
new ConstructorGenerator().process(classNode); | |
- defineExtendedParserClass(classNode); | |
+ defineExtendedParserClass(classNode, classInjector); | |
return classNode; | |
} | |
- @SuppressWarnings({"unchecked"}) | |
- private static void runMethodTransformers(ParserClassNode classNode) throws Exception { | |
- List<RuleMethodProcessor> methodProcessors = createRuleMethodProcessors(); | |
+ private static void runMethodTransformers(ParserClassNode classNode, BiConsumer<String, Supplier<byte[]>> classInjector, IntSupplier classFileVersion) throws Exception { | |
+ List<RuleMethodProcessor> methodProcessors = createRuleMethodProcessors(classInjector, classFileVersion); | |
// iterate through all rule methods | |
// since the ruleMethods map on the classnode is a treemap we get the methods sorted by name which puts | |
@@ -72,7 +88,7 @@ | |
} | |
} | |
- static List<RuleMethodProcessor> createRuleMethodProcessors() { | |
+ private static List<RuleMethodProcessor> createRuleMethodProcessors(BiConsumer<String, Supplier<byte[]>> classInjector, IntSupplier classFileVersion) { | |
return ImmutableList.of( | |
new UnusedLabelsRemover(), | |
new ReturnInstructionUnifier(), | |
@@ -80,8 +96,8 @@ | |
new ImplicitActionsConverter(), | |
new InstructionGroupCreator(), | |
new InstructionGroupPreparer(), | |
- new ActionClassGenerator(false), | |
- new VarInitClassGenerator(false), | |
+ new ActionClassGenerator(false, classInjector, classFileVersion), | |
+ new VarInitClassGenerator(false, classInjector, classFileVersion), | |
new RuleMethodRewriter(), | |
new SuperCallRewriter(), | |
@@ -93,7 +109,7 @@ | |
); | |
} | |
- private static void defineExtendedParserClass(final ParserClassNode classNode) { | |
+ private static void defineExtendedParserClass(final ParserClassNode classNode, BiFunction<String, Supplier<byte[]>, Class<?>> classInjector) { | |
ClassWriter classWriter = new ClassWriter(ASMSettings.FRAMES) { | |
@Override | |
protected ClassLoader getClassLoader() { | |
@@ -102,10 +118,14 @@ | |
}; | |
classNode.accept(classWriter); | |
classNode.setClassCode(classWriter.toByteArray()); | |
- classNode.setExtendedClass(loadClass( | |
+ classNode.setExtendedClass(classInjector == null ? loadClass( | |
classNode.name.replace('/', '.'), | |
classNode.getClassCode(), | |
classNode.getParentClass().getClassLoader() | |
+ ) : | |
+ classInjector.apply( | |
+ classNode.name.replace('/', '.'), | |
+ () -> classNode.getClassCode() | |
)); | |
} | |
diff --git a/parboiled-java/src/main/java/org/parboiled/transform/VarInitClassGenerator.java b/parboiled-java/src/main/java/org/parboiled/transform/VarInitClassGenerator.java | |
index df537cf..e042e6c 100644 | |
--- a/parboiled-java/src/main/java/org/parboiled/transform/VarInitClassGenerator.java | |
+++ b/parboiled-java/src/main/java/org/parboiled/transform/VarInitClassGenerator.java | |
@@ -23,12 +23,21 @@ | |
import static org.objectweb.asm.Opcodes.*; | |
import static org.parboiled.transform.Types.*; | |
+import java.util.function.BiConsumer; | |
+import java.util.function.IntSupplier; | |
+import java.util.function.Supplier; | |
+ | |
class VarInitClassGenerator extends GroupClassGenerator { | |
+ @Deprecated | |
public VarInitClassGenerator(boolean forceCodeBuilding) { | |
super(forceCodeBuilding); | |
} | |
+ public VarInitClassGenerator(boolean forceCodeBuilding, BiConsumer<String, Supplier<byte[]>> classInjector, IntSupplier classFileVersion) { | |
+ super(forceCodeBuilding, classInjector, classFileVersion); | |
+ } | |
+ | |
public boolean appliesTo(ParserClassNode classNode, RuleMethod method) { | |
checkArgNotNull(method, "method"); | |
return method.containsVars(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment