Commit b29e8bbbc8d337bb12c14e81aa18ee2565b07830
1 parent
bbee82bb
adding injection order checks to EventInjectionTransformer to prevent listeners …
…being appended after event injection
Showing
4 changed files
with
155 additions
and
51 deletions
java/common/com/mumfrey/liteloader/transformers/event/Event.java
... | ... | @@ -93,6 +93,8 @@ public class Event implements Comparable<Event> |
93 | 93 | |
94 | 94 | protected String eventInfoClass; |
95 | 95 | |
96 | + protected Set<MethodInfo> pendingInjections; | |
97 | + | |
96 | 98 | Event(String name, boolean cancellable, int priority) |
97 | 99 | { |
98 | 100 | this.name = name.toLowerCase(); |
... | ... | @@ -226,6 +228,38 @@ public class Event implements Comparable<Event> |
226 | 228 | this.method = null; |
227 | 229 | } |
228 | 230 | |
231 | + void addPendingInjection(MethodInfo targetMethod) | |
232 | + { | |
233 | + if (this.pendingInjections == null) | |
234 | + { | |
235 | + this.pendingInjections = new HashSet<MethodInfo>(); | |
236 | + } | |
237 | + | |
238 | + this.pendingInjections.add(targetMethod); | |
239 | + } | |
240 | + | |
241 | + void notifyInjected(String method, String desc, String className) | |
242 | + { | |
243 | + MethodInfo thisInjection = null; | |
244 | + | |
245 | + if (this.pendingInjections != null) | |
246 | + { | |
247 | + for (MethodInfo pendingInjection : this.pendingInjections) | |
248 | + { | |
249 | + if (pendingInjection.matches(method, desc, className)) | |
250 | + { | |
251 | + thisInjection = pendingInjection; | |
252 | + break; | |
253 | + } | |
254 | + } | |
255 | + } | |
256 | + | |
257 | + if (thisInjection != null) | |
258 | + { | |
259 | + this.pendingInjections.remove(thisInjection); | |
260 | + } | |
261 | + } | |
262 | + | |
229 | 263 | /** |
230 | 264 | * Pre-flight check |
231 | 265 | * |
... | ... | @@ -371,6 +405,11 @@ public class Event implements Comparable<Event> |
371 | 405 | throw new IllegalArgumentException("Descriptor is not allowed for listener methods"); |
372 | 406 | } |
373 | 407 | |
408 | + if (this.pendingInjections != null && this.pendingInjections.size() == 0) | |
409 | + { | |
410 | + throw new EventAlreadyInjectedException("The event " + this.name + " was already injected and has 0 pending injections, addListener() is not allowed at this point"); | |
411 | + } | |
412 | + | |
374 | 413 | this.listeners.add(listener); |
375 | 414 | |
376 | 415 | return this; | ... | ... |
java/common/com/mumfrey/liteloader/transformers/event/EventAlreadyInjectedException.java
0 → 100644
1 | +package com.mumfrey.liteloader.transformers.event; | |
2 | + | |
3 | +public class EventAlreadyInjectedException extends RuntimeException | |
4 | +{ | |
5 | + private static final long serialVersionUID = 1L; | |
6 | + | |
7 | + public EventAlreadyInjectedException(String message) | |
8 | + { | |
9 | + super(message); | |
10 | + } | |
11 | + | |
12 | + public EventAlreadyInjectedException(Throwable cause) | |
13 | + { | |
14 | + super(cause); | |
15 | + } | |
16 | + | |
17 | + public EventAlreadyInjectedException(String message, Throwable cause) | |
18 | + { | |
19 | + super(message, cause); | |
20 | + } | |
21 | +} | ... | ... |
java/common/com/mumfrey/liteloader/transformers/event/EventInjectionTransformer.java
... | ... | @@ -77,7 +77,14 @@ public abstract class EventInjectionTransformer extends ClassTransformer |
77 | 77 | EventInjectionTransformer.master = this; |
78 | 78 | } |
79 | 79 | |
80 | - this.addEvents(); | |
80 | + try | |
81 | + { | |
82 | + this.addEvents(); | |
83 | + } | |
84 | + catch (Exception ex) | |
85 | + { | |
86 | + ex.printStackTrace(); | |
87 | + } | |
81 | 88 | } |
82 | 89 | |
83 | 90 | /** |
... | ... | @@ -120,6 +127,8 @@ public abstract class EventInjectionTransformer extends ClassTransformer |
120 | 127 | this.addEvent(event, targetMethod.owner, targetMethod.sigSrg, injectionPoint); |
121 | 128 | this.addEvent(event, targetMethod.ownerObf, targetMethod.sigObf, injectionPoint); |
122 | 129 | |
130 | + event.addPendingInjection(targetMethod); | |
131 | + | |
123 | 132 | return event; |
124 | 133 | } |
125 | 134 | |
... | ... | @@ -215,65 +224,100 @@ public abstract class EventInjectionTransformer extends ClassTransformer |
215 | 224 | Map<Event, InjectionPoint> methodInjections = mappings.get(signature); |
216 | 225 | if (methodInjections != null) |
217 | 226 | { |
218 | - ReadOnlyInsnList insns = new ReadOnlyInsnList(method.instructions); | |
219 | - Map<AbstractInsnNode, Set<Event>> injectionPoints = new LinkedHashMap<AbstractInsnNode, Set<Event>>(); | |
220 | - Collection<AbstractInsnNode> nodes = new ArrayList<AbstractInsnNode>(32); | |
221 | - for (Entry<Event, InjectionPoint> eventEntry : methodInjections.entrySet()) | |
222 | - { | |
223 | - Event event = eventEntry.getKey(); | |
224 | - event.attach(method); | |
225 | - InjectionPoint injectionPoint = eventEntry.getValue(); | |
226 | - nodes.clear(); | |
227 | - if (injectionPoint.find(method.desc, insns, nodes, event)) | |
228 | - { | |
229 | - for (AbstractInsnNode node : nodes) | |
230 | - { | |
231 | - Set<Event> nodeEvents = injectionPoints.get(node); | |
232 | - if (nodeEvents == null) | |
233 | - { | |
234 | - nodeEvents = new TreeSet<Event>(); | |
235 | - injectionPoints.put(node, nodeEvents); | |
236 | - } | |
237 | - | |
238 | - nodeEvents.add(event); | |
239 | - } | |
240 | - } | |
241 | - } | |
242 | - | |
243 | - for (Entry<AbstractInsnNode, Set<Event>> injectionPoint : injectionPoints.entrySet()) | |
244 | - { | |
245 | - AbstractInsnNode insn = injectionPoint.getKey(); | |
246 | - Set<Event> events = injectionPoint.getValue(); | |
227 | + this.injectIntoMethod(classNode, signature, method, methodInjections); | |
228 | + } | |
229 | + } | |
230 | + | |
231 | + if (true || this.runValidator) | |
232 | + { | |
233 | + ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); | |
234 | + classNode.accept(new CheckClassAdapter(writer)); | |
235 | + } | |
236 | + | |
237 | + return this.writeClass(classNode); | |
238 | + } | |
247 | 239 | |
248 | - // Injection is cancellable if ANY of the events on this insn are cancellable | |
249 | - boolean cancellable = false; | |
250 | - for (Event event : events) | |
251 | - cancellable |= event.isCancellable(); | |
252 | - | |
253 | - Event head = events.iterator().next(); | |
254 | - MethodNode handler = head.inject(insn, cancellable, this.globalEventID); | |
240 | + /** | |
241 | + * @param classNode | |
242 | + * @param signature | |
243 | + * @param method | |
244 | + * @param methodInjections | |
245 | + */ | |
246 | + void injectIntoMethod(ClassNode classNode, String signature, MethodNode method, Map<Event, InjectionPoint> methodInjections) | |
247 | + { | |
248 | + Map<AbstractInsnNode, Set<Event>> injectionPoints = this.findInjectionPoints(classNode, method, methodInjections); | |
249 | + | |
250 | + for (Entry<AbstractInsnNode, Set<Event>> injectionPoint : injectionPoints.entrySet()) | |
251 | + { | |
252 | + this.injectEventsAt(classNode, method, injectionPoint.getKey(), injectionPoint.getValue()); | |
253 | + } | |
255 | 254 | |
256 | - LiteLoaderLogger.info("Injecting event %s with %d handlers in method %s in class %s", head.getName(), events.size(), method.name, classNode.name.replace('/', '.')); | |
257 | - | |
258 | - for (Event event : events) | |
259 | - event.addToHandler(handler); | |
260 | - | |
261 | - this.globalEventID++; | |
262 | - } | |
255 | + for (Event event : methodInjections.keySet()) | |
256 | + { | |
257 | + event.notifyInjected(method.name, method.desc, classNode.name); | |
258 | + event.detach(); | |
259 | + } | |
260 | + } | |
263 | 261 | |
264 | - for (Event event : methodInjections.keySet()) | |
262 | + /** | |
263 | + * @param classNode | |
264 | + * @param method | |
265 | + * @param methodInjections | |
266 | + * @return | |
267 | + */ | |
268 | + private Map<AbstractInsnNode, Set<Event>> findInjectionPoints(ClassNode classNode, MethodNode method, Map<Event, InjectionPoint> methodInjections) | |
269 | + { | |
270 | + ReadOnlyInsnList insns = new ReadOnlyInsnList(method.instructions); | |
271 | + Collection<AbstractInsnNode> nodes = new ArrayList<AbstractInsnNode>(32); | |
272 | + Map<AbstractInsnNode, Set<Event>> injectionPoints = new LinkedHashMap<AbstractInsnNode, Set<Event>>(); | |
273 | + for (Entry<Event, InjectionPoint> eventEntry : methodInjections.entrySet()) | |
274 | + { | |
275 | + Event event = eventEntry.getKey(); | |
276 | + event.attach(method); | |
277 | + InjectionPoint injectionPoint = eventEntry.getValue(); | |
278 | + nodes.clear(); | |
279 | + if (injectionPoint.find(method.desc, insns, nodes, event)) | |
280 | + { | |
281 | + for (AbstractInsnNode node : nodes) | |
265 | 282 | { |
266 | - event.detach(); | |
283 | + Set<Event> nodeEvents = injectionPoints.get(node); | |
284 | + if (nodeEvents == null) | |
285 | + { | |
286 | + nodeEvents = new TreeSet<Event>(); | |
287 | + injectionPoints.put(node, nodeEvents); | |
288 | + } | |
289 | + | |
290 | + nodeEvents.add(event); | |
267 | 291 | } |
268 | 292 | } |
269 | 293 | } |
270 | 294 | |
271 | - if (true || this.runValidator) | |
295 | + return injectionPoints; | |
296 | + } | |
297 | + | |
298 | + /** | |
299 | + * @param classNode | |
300 | + * @param method | |
301 | + * @param injectionPoint | |
302 | + * @param events | |
303 | + */ | |
304 | + private void injectEventsAt(ClassNode classNode, MethodNode method, AbstractInsnNode injectionPoint, Set<Event> events) | |
305 | + { | |
306 | + // Injection is cancellable if ANY of the events on this insn are cancellable | |
307 | + boolean cancellable = false; | |
308 | + for (Event event : events) | |
309 | + cancellable |= event.isCancellable(); | |
310 | + | |
311 | + Event head = events.iterator().next(); | |
312 | + MethodNode handler = head.inject(injectionPoint, cancellable, this.globalEventID); | |
313 | + | |
314 | + LiteLoaderLogger.info("Injecting event %s with %d handlers in method %s in class %s", head.getName(), events.size(), method.name, classNode.name.replace('/', '.')); | |
315 | + | |
316 | + for (Event event : events) | |
272 | 317 | { |
273 | - ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); | |
274 | - classNode.accept(new CheckClassAdapter(writer)); | |
318 | + event.addToHandler(handler); | |
275 | 319 | } |
276 | 320 | |
277 | - return this.writeClass(classNode); | |
321 | + this.globalEventID++; | |
278 | 322 | } |
279 | 323 | } |
280 | 324 | \ No newline at end of file | ... | ... |
java/common/com/mumfrey/liteloader/transformers/event/MethodInfo.java
... | ... | @@ -290,7 +290,7 @@ public class MethodInfo |
290 | 290 | |
291 | 291 | public boolean matches(String method, String desc, String className) |
292 | 292 | { |
293 | - if ((className == null || this.owner.equals(className)) && (this.name.equals(method) || this.nameSrg.equals(method))) | |
293 | + if ((className == null || this.ownerRef.equals(className)) && (this.name.equals(method) || this.nameSrg.equals(method))) | |
294 | 294 | { |
295 | 295 | return this.desc == null || this.desc.equals(desc); |
296 | 296 | } | ... | ... |