Skip to content

Commit 7ceceb8

Browse files
committed
Merge branch 'pr/1006'
2 parents be5f69c + 5a85a92 commit 7ceceb8

16 files changed

Lines changed: 1223 additions & 97 deletions

File tree

recaf-core/src/main/java/software/coley/recaf/services/plugin/CdiClassAllocator.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
import jakarta.enterprise.inject.spi.*;
77
import jakarta.inject.Inject;
88

9-
import java.util.IdentityHashMap;
109
import java.util.Map;
10+
import java.util.WeakHashMap;
11+
import java.util.concurrent.locks.Lock;
12+
import java.util.concurrent.locks.ReentrantLock;
1113

1214
/**
1315
* Allocator instance that ties into the CDI container.
@@ -16,7 +18,8 @@
1618
*/
1719
@ApplicationScoped
1820
public class CdiClassAllocator implements ClassAllocator {
19-
private final Map<Class<?>, Bean<?>> classBeanMap = new IdentityHashMap<>();
21+
private final Map<Class<?>, Bean<?>> classBeanMap = new WeakHashMap<>();
22+
private final Lock lock = new ReentrantLock();
2023
private final BeanManager beanManager;
2124

2225
@Inject
@@ -28,20 +31,27 @@ public CdiClassAllocator(@Nonnull BeanManager beanManager) {
2831
@Override
2932
@SuppressWarnings("unchecked")
3033
public <T> T instance(@Nonnull Class<T> cls) throws AllocationException {
34+
lock.lock();
3135
try {
3236
// Create bean
3337
Bean<T> bean = (Bean<T>) classBeanMap.computeIfAbsent(cls, c -> {
34-
AnnotatedType<T> annotatedClass = beanManager.createAnnotatedType(cls);
38+
// TODO something here is bugged
39+
// bean.create(creationalContext).getClass().getClassLoader() == cls.getClassLoader()
40+
// - Evaluates to false
41+
// - In AnnotatedTypeIdentifier#forBackedAnnotatedType the ClassLoader is not considered, just the class's name
42+
AnnotatedType<T> annotatedClass = beanManager.createAnnotatedType((Class<T>) c);
3543
BeanAttributes<T> attributes = beanManager.createBeanAttributes(annotatedClass);
3644
InjectionTargetFactory<T> factory = beanManager.getInjectionTargetFactory(annotatedClass);
37-
return beanManager.createBean(attributes, cls, factory);
45+
return beanManager.createBean(attributes, (Class<T>) c, factory);
3846
});
3947
CreationalContext<T> creationalContext = beanManager.createCreationalContext(bean);
4048

4149
// Allocate instance of bean
4250
return bean.create(creationalContext);
4351
} catch (Throwable t) {
4452
throw new AllocationException(cls, t);
53+
} finally {
54+
lock.unlock();
4555
}
4656
}
4757
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package software.coley.recaf.services.script;
2+
3+
import software.coley.recaf.util.CancelSignal;
4+
5+
/**
6+
* Cancellation singleton.
7+
* <p>
8+
* Injected into generated scripts.
9+
* <b>Do not use in normal code</b>.
10+
*
11+
* @author xDark
12+
* @see InsertCancelSignalVisitor
13+
* @see GenerateResult#requestStop()
14+
*/
15+
public final class CancellationSingleton {
16+
private static volatile boolean shouldStop;
17+
18+
private CancellationSingleton() {}
19+
20+
/**
21+
* Schedules cancellation of the script.
22+
*/
23+
public static void stop() {
24+
shouldStop = true;
25+
}
26+
27+
/**
28+
* Clears any prior cancellation request.
29+
*/
30+
public static void reset() {
31+
shouldStop = false;
32+
}
33+
34+
/**
35+
* Polls for cancellation.
36+
*
37+
* @throws CancelSignal
38+
* If cancellation has been requested.
39+
*/
40+
public static void poll() {
41+
if (shouldStop)
42+
throw CancelSignal.get();
43+
}
44+
}

recaf-core/src/main/java/software/coley/recaf/services/script/GenerateResult.java

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import jakarta.annotation.Nullable;
55
import software.coley.recaf.services.compile.CompilerDiagnostic;
66

7+
import java.lang.reflect.InvocationTargetException;
78
import java.util.List;
89

910
/**
@@ -23,4 +24,42 @@ public record GenerateResult(@Nullable Class<?> cls, @Nonnull List<CompilerDiagn
2324
public boolean wasSuccess() {
2425
return cls != null;
2526
}
26-
}
27+
28+
/**
29+
* Attempts to stop the script. If the generation failed, this method will do nothing.
30+
* <p>
31+
* Cancellation is scoped to the generated class loader that owns {@link #cls()}.
32+
* Callers that wish to stop running scripts must use {@link ScriptEngine#run(GenerateResult)}
33+
* and track the returned instance for stopping.
34+
*
35+
* @throws IllegalStateException
36+
* If something went wrong.
37+
*/
38+
public void requestStop() {
39+
invokeCancellationSingleton("stop");
40+
}
41+
42+
/**
43+
* Clears any prior request to stop the script. If the generation failed, this method will do nothing.
44+
*
45+
* @throws IllegalStateException
46+
* If something went wrong.
47+
*/
48+
public void resetStop() {
49+
invokeCancellationSingleton("reset");
50+
}
51+
52+
private void invokeCancellationSingleton(@Nonnull String methodName) {
53+
Class<?> cls = this.cls;
54+
if (cls == null)
55+
return;
56+
try {
57+
Class<?> cancellationSingleton = cls.getClassLoader().loadClass(CancellationSingleton.class.getName());
58+
cancellationSingleton.getDeclaredMethod(methodName).invoke(null);
59+
} catch (InvocationTargetException ex) {
60+
throw new IllegalStateException(ex.getTargetException());
61+
} catch (Exception ex) {
62+
throw new IllegalStateException(ex);
63+
}
64+
}
65+
}

0 commit comments

Comments
 (0)