Commit b29e8bbbc8d337bb12c14e81aa18ee2565b07830

Authored by Mumfrey
1 parent bbee82bb

adding injection order checks to EventInjectionTransformer to prevent listeners …

…being appended after event injection
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&lt;Event&gt;
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&lt;Event&gt;
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 }
... ...