Skip to content

Commit ff649d5

Browse files
arjantijmsmanovotn
authored andcommitted
WELD-2828 Ensure a proxy target JPMS module can read Weld JPMS modules
1 parent dae56f0 commit ff649d5

File tree

2 files changed

+85
-2
lines changed

2 files changed

+85
-2
lines changed

impl/src/main/java/org/jboss/weld/bean/proxy/DummyClassFactoryImpl.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@
1313
* See {@link WeldDefaultProxyServices#defineClass(Class, String, byte[], int, int)} for details on how we define
1414
* classes.
1515
*/
16-
class DummyClassFactoryImpl implements ClassFactory {
16+
public class DummyClassFactoryImpl implements ClassFactory {
1717

1818
private DummyClassFactoryImpl() {
1919
}
2020

2121
// final so that there's only one instance that's being referenced from anywhere
22-
static final DummyClassFactoryImpl INSTANCE = new DummyClassFactoryImpl();
22+
public static final DummyClassFactoryImpl INSTANCE = new DummyClassFactoryImpl();
2323

2424
@Override
2525
public Class<?> defineClass(ClassLoader loader, String name, byte[] b, int off, int len, ProtectionDomain protectionDomain)

impl/src/main/java/org/jboss/weld/bean/proxy/util/WeldDefaultProxyServices.java

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,28 @@
1717

1818
package org.jboss.weld.bean.proxy.util;
1919

20+
import static org.jboss.classfilewriter.AccessFlag.FINAL;
21+
import static org.jboss.classfilewriter.AccessFlag.PUBLIC;
22+
import static org.jboss.classfilewriter.AccessFlag.STATIC;
23+
import static org.jboss.classfilewriter.AccessFlag.SUPER;
24+
import static org.jboss.classfilewriter.AccessFlag.SYNTHETIC;
25+
import static org.jboss.classfilewriter.util.DescriptorUtils.makeDescriptor;
26+
import static org.jboss.weld.util.bytecode.BytecodeUtils.VOID_CLASS_DESCRIPTOR;
27+
28+
import java.lang.invoke.MethodHandle;
2029
import java.lang.invoke.MethodHandles;
30+
import java.lang.invoke.MethodType;
2131
import java.security.ProtectionDomain;
2232
import java.util.concurrent.ConcurrentHashMap;
2333
import java.util.concurrent.ConcurrentMap;
2434

35+
import org.jboss.classfilewriter.AccessFlag;
36+
import org.jboss.classfilewriter.ClassFile;
37+
import org.jboss.classfilewriter.code.CodeAttribute;
38+
import org.jboss.weld.bean.proxy.DummyClassFactoryImpl;
2539
import org.jboss.weld.bean.proxy.ProxyFactory;
2640
import org.jboss.weld.logging.BeanLogger;
41+
import org.jboss.weld.proxy.WeldClientProxy;
2742
import org.jboss.weld.serialization.spi.ProxyServices;
2843

2944
/**
@@ -135,6 +150,8 @@ private WeldProxyDeclaringCL returnWeldCL(ClassLoader loader) {
135150
private Class<?> defineWithMethodLookup(String classToDefineName, byte[] classBytes, Class<?> originalClass,
136151
ClassLoader loader) {
137152
Module thisModule = WeldDefaultProxyServices.class.getModule();
153+
Module apiModule = WeldClientProxy.class.getModule();
154+
138155
try {
139156
Class<?> lookupBaseClass;
140157
try {
@@ -143,19 +160,85 @@ private Class<?> defineWithMethodLookup(String classToDefineName, byte[] classBy
143160
} catch (Exception e) {
144161
lookupBaseClass = originalClass;
145162
}
163+
164+
// Ensure we can read the other module, and the other module can read us
165+
146166
Module lookupClassModule = lookupBaseClass.getModule();
147167
if (!thisModule.canRead(lookupClassModule)) {
148168
// we need to read the other module in order to have privateLookup access
149169
// see javadoc for MethodHandles.privateLookupIn()
150170
thisModule.addReads(lookupClassModule);
151171
}
172+
173+
try {
174+
// the other module needs to read us, since the proxy we are
175+
// about to generate inside that module uses our classes
176+
177+
MethodHandle ensureReadsMethod = null;
178+
if (!lookupClassModule.canRead(thisModule)) {
179+
ensureReadsMethod = generateEnsureReadsMethod(lookupBaseClass);
180+
ensureReadsMethod.invoke(thisModule);
181+
}
182+
183+
if (!lookupClassModule.canRead(apiModule)) {
184+
if (ensureReadsMethod == null) {
185+
ensureReadsMethod = generateEnsureReadsMethod(lookupBaseClass);
186+
}
187+
ensureReadsMethod.invoke(apiModule);
188+
}
189+
} catch (Throwable t) {
190+
throw new RuntimeException(t);
191+
}
192+
152193
MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(lookupBaseClass, MethodHandles.lookup());
153194
return lookup.defineClass(classBytes);
154195
} catch (IllegalAccessException e) {
155196
throw new RuntimeException(e);
156197
}
157198
}
158199

200+
private MethodHandle generateEnsureReadsMethod(Class<?> lookupBaseClass)
201+
throws IllegalAccessException, NoSuchMethodException {
202+
MethodHandles.Lookup privateLookup = MethodHandles.privateLookupIn(lookupBaseClass, MethodHandles.lookup());
203+
204+
return privateLookup.findStatic(
205+
// Define a hidden helper class in the *target* module/package
206+
privateLookup.defineClass(generateReadsHelperBytes(lookupBaseClass)),
207+
"ensureReads",
208+
MethodType.methodType(void.class, Module.class));
209+
}
210+
211+
private byte[] generateReadsHelperBytes(Class<?> lookupBaseClass) {
212+
// Put helper in the same package as the base class so the private Lookup can define it
213+
214+
// Create class header
215+
ClassFile ensureReadsClassFile = new ClassFile(
216+
(lookupBaseClass.getPackage() == null ? "" : lookupBaseClass.getPackage().getName() + ".") + "Weld$ReadsHelper",
217+
AccessFlag.of(PUBLIC, FINAL, SUPER, SYNTHETIC),
218+
Object.class.getName(),
219+
ProxyFactory.class.getClassLoader(),
220+
DummyClassFactoryImpl.INSTANCE);
221+
222+
// Create method header for "public static void ensureReads(Module other)"
223+
CodeAttribute code = ensureReadsClassFile.addMethod(
224+
AccessFlag.of(PUBLIC, STATIC),
225+
"ensureReads",
226+
VOID_CLASS_DESCRIPTOR,
227+
makeDescriptor(Module.class))
228+
.getCodeAttribute();
229+
230+
// Create code for the method body "MethodHandles.lookup().lookupClass().getModule().addReads(other);"
231+
code.invokestatic("java/lang/invoke/MethodHandles", "lookup", "()Ljava/lang/invoke/MethodHandles$Lookup;");
232+
code.invokevirtual("java/lang/invoke/MethodHandles$Lookup", "lookupClass", "()Ljava/lang/Class;");
233+
code.invokevirtual("java/lang/Class", "getModule", "()Ljava/lang/Module;");
234+
code.aload(0); // parameter: Module other
235+
code.invokevirtual("java/lang/Module", "addReads", "(Ljava/lang/Module;)Ljava/lang/Module;");
236+
code.pop();
237+
code.returnInstruction();
238+
239+
return ensureReadsClassFile.toBytecode();
240+
}
241+
159242
/**
160243
* A class loader that should only be used to load Weld-prefixed proxies meaning those that have non-existent packages.
161244
* This is a workaround for JMPS approach ({@code MethodHandles.Lookup}) not being able to fulfil this scenario.

0 commit comments

Comments
 (0)