Skip to content

Commit 22af389

Browse files
authored
Merge pull request #164 from stevenschlansker/bb-classloader-access-2.13
Blackbird: backport #138 to 2.13
2 parents 6430368 + d94d8fb commit 22af389

File tree

6 files changed

+499
-6
lines changed

6 files changed

+499
-6
lines changed

blackbird/GenBlackbirdAccess.java

+276
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
import java.util.ArrayList;
2+
import java.util.Collections;
3+
import java.util.EnumSet;
4+
import java.util.List;
5+
import java.util.Set;
6+
import java.util.stream.Collectors;
7+
8+
import javax.tools.Diagnostic;
9+
import javax.tools.DiagnosticListener;
10+
import java.io.ByteArrayInputStream;
11+
import java.io.ByteArrayOutputStream;
12+
import java.io.IOException;
13+
import java.io.InputStream;
14+
import java.io.OutputStream;
15+
import java.net.URI;
16+
import java.util.HashMap;
17+
import java.util.Iterator;
18+
import java.util.Map;
19+
import java.util.NoSuchElementException;
20+
import java.util.Set;
21+
import javax.tools.FileObject;
22+
import javax.tools.JavaFileManager;
23+
import javax.tools.JavaFileObject;
24+
import javax.tools.JavaFileObject.Kind;
25+
import javax.tools.SimpleJavaFileObject;
26+
import javax.tools.StandardJavaFileManager;
27+
import javax.tools.StandardLocation;
28+
29+
/**
30+
* The following code produces the class file stored in the data arrays in CrossLoaderAccess.
31+
* Loading the Java compiler is extremely expensive so we inline the byte data instead.
32+
*
33+
* This code is not compiled or shipped with Blackbird, just here for maintainer reference.
34+
*/
35+
class GrantAccess {
36+
private static MethodHandles.Lookup grantAccessSlow(final MethodHandles.Lookup lookup, final Package pkg) throws IOException, ReflectiveOperationException {
37+
final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
38+
final BlackbirdDiagnosticListener<JavaFileObject> diagnostics = new BlackbirdDiagnosticListener<>();
39+
final StandardJavaFileManager delegateFileManager = compiler.getStandardFileManager(diagnostics, Locale.ROOT, StandardCharsets.UTF_8);
40+
final String myPackage = pkg.getName();
41+
final BlackbirdFileManager fileManager = new BlackbirdFileManager(delegateFileManager, myPackage);
42+
final JavaFileObject file = new BlackbirdFileManager.BlackbirdInputJavaFileObject("BlackbirdAccess", "package " + myPackage + "; public class BlackbirdAccess { public static final java.lang.invoke.MethodHandles.Lookup LOOKUP = java.lang.invoke.MethodHandles.lookup(); }");
43+
compiler
44+
.getTask(null, fileManager, diagnostics, Arrays.asList("--release", "8"), null, Arrays.asList(file))
45+
.call();
46+
diagnostics.assertSuccess();
47+
final FileObject accessClassFile = fileManager.getFileForInput(StandardLocation.CLASS_PATH, myPackage, "BlackbirdAccess");
48+
final byte[] accessClassBytes = accessClassFile.openInputStream().readAllBytes();
49+
final Class<?> accessClass = lookup.defineClass(accessClassBytes);
50+
return (MethodHandles.Lookup) accessClass.getField("LOOKUP").get(null);
51+
}
52+
}
53+
54+
class BlackbirdDiagnosticListener<S> implements DiagnosticListener<S> {
55+
private static final Set<Diagnostic.Kind> FAILURES;
56+
57+
static {
58+
FAILURES = Collections.unmodifiableSet(
59+
EnumSet.of(Diagnostic.Kind.ERROR, Diagnostic.Kind.WARNING, Diagnostic.Kind.MANDATORY_WARNING));
60+
}
61+
62+
private final List<Diagnostic<? extends S>> failures = new ArrayList<>();
63+
64+
@Override
65+
public void report(final Diagnostic<? extends S> diagnostic) {
66+
if (FAILURES.contains(diagnostic.getKind())) {
67+
failures.add(diagnostic);
68+
}
69+
}
70+
71+
public void assertSuccess() {
72+
if (!failures.isEmpty()) {
73+
throw new RuntimeException("Compilation failed:\n"
74+
+ failures.stream()
75+
.map(Object::toString)
76+
.collect(Collectors.joining("\n")));
77+
}
78+
}
79+
}
80+
81+
82+
class BlackbirdFileManager implements JavaFileManager {
83+
private final StandardJavaFileManager delegate;
84+
private final Map<String, BlackbirdOutputJavaFileObject> createdFiles = new HashMap<>();
85+
private final String myPackage;
86+
87+
public BlackbirdFileManager(final StandardJavaFileManager delegate, final String myPackage) {
88+
this.delegate = delegate;
89+
this.myPackage = myPackage;
90+
}
91+
92+
@Override
93+
public ClassLoader getClassLoader(final Location location) {
94+
return delegate.getClassLoader(location);
95+
}
96+
97+
@Override
98+
public Iterable<JavaFileObject> list(final Location location, final String packageName, final Set<Kind> kinds, final boolean recurse) throws IOException {
99+
final Iterable<JavaFileObject> stdList = delegate.list(location, packageName, kinds, recurse);
100+
if (StandardLocation.CLASS_PATH.equals(location)) {
101+
return () -> new Iterator<JavaFileObject>() {
102+
boolean stdDone;
103+
Iterator<? extends JavaFileObject> iter;
104+
105+
@Override
106+
public boolean hasNext() {
107+
if (iter == null) {
108+
iter = stdList.iterator();
109+
}
110+
if (iter.hasNext()) {
111+
return true;
112+
}
113+
if (stdDone) {
114+
return false;
115+
}
116+
stdDone = true;
117+
iter = createdFiles.values().iterator();
118+
return iter.hasNext();
119+
}
120+
121+
@Override
122+
public JavaFileObject next() {
123+
if (!hasNext()) {
124+
throw new NoSuchElementException();
125+
}
126+
return iter.next();
127+
}
128+
};
129+
}
130+
return stdList;
131+
}
132+
133+
@Override
134+
public String inferBinaryName(final Location location, final JavaFileObject file) {
135+
if (file instanceof BlackbirdOutputJavaFileObject) {
136+
return file.getName();
137+
}
138+
return delegate.inferBinaryName(location, file);
139+
}
140+
141+
@Override
142+
public boolean handleOption(final String current, final Iterator<String> remaining) {
143+
return delegate.handleOption(current, remaining);
144+
}
145+
146+
@Override
147+
public boolean hasLocation(final Location location) {
148+
return delegate.hasLocation(location);
149+
}
150+
151+
@Override
152+
public JavaFileObject getJavaFileForInput(final Location location, final String className, final Kind kind) throws IOException {
153+
return delegate.getJavaFileForInput(location, className, kind);
154+
}
155+
156+
@Override
157+
public JavaFileObject getJavaFileForOutput(final Location location, final String className, final Kind kind, final FileObject sibling) throws IOException {
158+
final BlackbirdOutputJavaFileObject out = new BlackbirdOutputJavaFileObject(className, kind);
159+
createdFiles.put(className, out);
160+
return out;
161+
}
162+
163+
@Override
164+
public FileObject getFileForInput(final Location location, final String packageName, final String relativeName) throws IOException {
165+
if (StandardLocation.CLASS_PATH.equals(location) && myPackage.equals(packageName)) {
166+
final BlackbirdOutputJavaFileObject file = createdFiles.get(packageName + "." + relativeName);
167+
if (file != null) {
168+
return file;
169+
}
170+
}
171+
return delegate.getFileForInput(location, packageName, relativeName);
172+
}
173+
174+
@Override
175+
public FileObject getFileForOutput(final Location location, final String packageName, final String relativeName, final FileObject sibling) throws IOException {
176+
throw new UnsupportedOperationException();
177+
}
178+
179+
@Override
180+
public void flush() throws IOException {}
181+
182+
@Override
183+
public void close() throws IOException {}
184+
185+
@Override
186+
public int isSupportedOption(final String option) {
187+
return delegate.isSupportedOption(option);
188+
}
189+
190+
@Override
191+
public boolean isSameFile(final FileObject a, final FileObject b) {
192+
return delegate.isSameFile(a, b);
193+
}
194+
195+
@Override
196+
public Location getLocationForModule(final Location location, final String moduleName) throws IOException {
197+
return delegate.getLocationForModule(location, moduleName);
198+
}
199+
200+
@Override
201+
public Location getLocationForModule(final Location location, final JavaFileObject fo) throws IOException {
202+
return delegate.getLocationForModule(location, fo);
203+
}
204+
205+
@Override
206+
public String inferModuleName(final Location location) throws IOException {
207+
return delegate.inferModuleName(location);
208+
}
209+
210+
@Override
211+
public Iterable<Set<Location>> listLocationsForModules(final Location location) throws IOException {
212+
return delegate.listLocationsForModules(location);
213+
}
214+
215+
@Override
216+
public boolean contains(final Location location, final FileObject file) throws IOException {
217+
return delegate.contains(location, file);
218+
}
219+
220+
public static class BlackbirdJavaFileObject extends SimpleJavaFileObject {
221+
222+
protected BlackbirdJavaFileObject(final String name, final Kind kind) {
223+
super(URI.create("string:///" + name.replace('.', '/')
224+
+ kind.extension), kind);
225+
}
226+
}
227+
228+
public static class BlackbirdInputJavaFileObject extends BlackbirdJavaFileObject {
229+
private final String contents;
230+
231+
protected BlackbirdInputJavaFileObject(final String name, final String contents) {
232+
super(name, Kind.SOURCE);
233+
this.contents = contents;
234+
}
235+
236+
@Override
237+
public CharSequence getCharContent(final boolean ignoreEncodingErrors) throws IOException {
238+
return contents;
239+
}
240+
}
241+
242+
public static class BlackbirdOutputJavaFileObject extends BlackbirdJavaFileObject {
243+
private final String name;
244+
private ByteArrayOutputStream baos = new ByteArrayOutputStream();
245+
private byte[] bytes;
246+
247+
protected BlackbirdOutputJavaFileObject(final String name, final Kind kind) {
248+
super(name, kind);
249+
this.name = name;
250+
}
251+
252+
@Override
253+
public String getName() {
254+
return name;
255+
}
256+
257+
public byte[] getBytes() {
258+
if (bytes == null) {
259+
bytes = baos.toByteArray();
260+
baos = null;
261+
}
262+
return bytes;
263+
}
264+
265+
@Override
266+
public OutputStream openOutputStream() throws IOException {
267+
return baos;
268+
}
269+
270+
@Override
271+
public InputStream openInputStream() throws IOException {
272+
return new ByteArrayInputStream(getBytes());
273+
}
274+
}
275+
}
276+

blackbird/src/main/java/com/fasterxml/jackson/module/blackbird/BlackbirdModule.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ public BlackbirdModule(Supplier<MethodHandles.Lookup> lookup) {
3535
@Override
3636
public void setupModule(SetupContext context)
3737
{
38-
context.addBeanDeserializerModifier(new BBDeserializerModifier(_lookups));
39-
context.addBeanSerializerModifier(new BBSerializerModifier(_lookups));
38+
CrossLoaderAccess openSesame = new CrossLoaderAccess();
39+
context.addBeanDeserializerModifier(new BBDeserializerModifier(_lookups, openSesame));
40+
context.addBeanSerializerModifier(new BBSerializerModifier(_lookups, openSesame));
4041
}
4142

4243
@Override

0 commit comments

Comments
 (0)