Skip to content

Instantly share code, notes, and snippets.

@TuomasKiviaho
Created June 18, 2020 08:01
Show Gist options
  • Save TuomasKiviaho/3d6dae166e85c4aaba94afb0318b4e4d to your computer and use it in GitHub Desktop.
Save TuomasKiviaho/3d6dae166e85c4aaba94afb0318b4e4d to your computer and use it in GitHub Desktop.
sirthias/parboiled: JDK11 reporting illegal reflective access #128
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