From 76ea4ab72eeb2c21a46cd724f48729a7083e32f1 Mon Sep 17 00:00:00 2001 From: Pesek Date: Mon, 11 Aug 2025 14:39:51 +0200 Subject: [PATCH 1/5] added support for the syntax info suppliers when converting to the legacy api, replaced reflection calls for creating instances of syntax infos with LambdaMetafactory --- .../ch/njol/skript/lang/ExpressionInfo.java | 22 ++++- .../ch/njol/skript/lang/SkriptEventInfo.java | 12 ++- .../ch/njol/skript/lang/SkriptParser.java | 97 +++++++++---------- .../njol/skript/lang/SyntaxElementInfo.java | 58 +++++++---- .../skript/lang/structure/StructureInfo.java | 35 +++++-- .../skript/registration/SyntaxInfoImpl.java | 17 ++-- .../skriptlang/skript/util/ClassUtils.java | 39 ++++++++ 7 files changed, 193 insertions(+), 87 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/ExpressionInfo.java b/src/main/java/ch/njol/skript/lang/ExpressionInfo.java index feeba7bd785..d081fbea99e 100644 --- a/src/main/java/ch/njol/skript/lang/ExpressionInfo.java +++ b/src/main/java/ch/njol/skript/lang/ExpressionInfo.java @@ -2,6 +2,8 @@ import org.jetbrains.annotations.Nullable; +import java.util.function.Supplier; + /** * Represents an expression's information, for use when creating new instances of expressions. */ @@ -10,12 +12,24 @@ public class ExpressionInfo, T> extends SyntaxElementInf public @Nullable ExpressionType expressionType; public Class returnType; - public ExpressionInfo(String[] patterns, Class returnType, Class expressionClass, String originClassPath) throws IllegalArgumentException { - this(patterns, returnType, expressionClass, originClassPath, null); + public ExpressionInfo(String[] patterns, Class returnType, Class expressionClass, String originClassPath) + throws IllegalArgumentException { + this(patterns, returnType, expressionClass, originClassPath, (Supplier) null); + } + + public ExpressionInfo(String[] patterns, Class returnType, Class expressionClass, String originClassPath, + @Nullable Supplier supplier) throws IllegalArgumentException { + this(patterns, returnType, expressionClass, originClassPath, null, supplier); + } + + public ExpressionInfo(String[] patterns, Class returnType, Class expressionClass, String originClassPath, + @Nullable ExpressionType expressionType) { + this(patterns, returnType, expressionClass, originClassPath, expressionType, null); } - public ExpressionInfo(String[] patterns, Class returnType, Class expressionClass, String originClassPath, @Nullable ExpressionType expressionType) throws IllegalArgumentException { - super(patterns, expressionClass, originClassPath); + public ExpressionInfo(String[] patterns, Class returnType, Class expressionClass, String originClassPath, + @Nullable ExpressionType expressionType, @Nullable Supplier supplier) throws IllegalArgumentException { + super(patterns, expressionClass, originClassPath, supplier); this.returnType = returnType; this.expressionType = expressionType; } diff --git a/src/main/java/ch/njol/skript/lang/SkriptEventInfo.java b/src/main/java/ch/njol/skript/lang/SkriptEventInfo.java index 71bd8e661e9..f2e155aad39 100644 --- a/src/main/java/ch/njol/skript/lang/SkriptEventInfo.java +++ b/src/main/java/ch/njol/skript/lang/SkriptEventInfo.java @@ -20,6 +20,7 @@ import java.util.Collection; import java.util.List; import java.util.Locale; +import java.util.function.Supplier; public sealed class SkriptEventInfo extends StructureInfo permits ModernSkriptEventInfo { @@ -32,15 +33,22 @@ public sealed class SkriptEventInfo extends StructureInfo private final String id; + public SkriptEventInfo(String name, String[] patterns, Class eventClass, String originClassPath, + Class[] events) { + this(name, patterns, eventClass, originClassPath, events, null); + } + /** * @param name Capitalised name of the event without leading "On" which is added automatically (Start the name with an asterisk to prevent this). * @param patterns The Skript patterns to use for this event * @param eventClass The SkriptEvent's class * @param originClassPath The class path for the origin of this event. * @param events The Bukkit-Events this SkriptEvent listens to + * @param supplier supplier ofr the syntax info instance */ - public SkriptEventInfo(String name, String[] patterns, Class eventClass, String originClassPath, Class[] events) { - super(patterns, eventClass, originClassPath); + public SkriptEventInfo(String name, String[] patterns, Class eventClass, String originClassPath, + Class[] events, @Nullable Supplier supplier) { + super(patterns, eventClass, originClassPath, supplier); for (int i = 0; i < events.length; i++) { for (int j = i + 1; j < events.length; j++) { if (events[i].isAssignableFrom(events[j]) || events[j].isAssignableFrom(events[i])) { diff --git a/src/main/java/ch/njol/skript/lang/SkriptParser.java b/src/main/java/ch/njol/skript/lang/SkriptParser.java index a6eb399dfc8..bb79cc3ef61 100644 --- a/src/main/java/ch/njol/skript/lang/SkriptParser.java +++ b/src/main/java/ch/njol/skript/lang/SkriptParser.java @@ -188,64 +188,61 @@ public boolean hasTag(String tag) { SyntaxElementInfo info = source.next(); patternsLoop: for (int patternIndex = 0; patternIndex < info.patterns.length; patternIndex++) { log.clear(); + String pattern = info.patterns[patternIndex]; + assert pattern != null; + ParseResult parseResult; try { - String pattern = info.patterns[patternIndex]; - assert pattern != null; - ParseResult parseResult; + parseResult = parse_i(pattern); + } catch (MalformedPatternException e) { + String message = "pattern compiling exception, element class: " + info.getElementClass().getName(); try { - parseResult = parse_i(pattern); - } catch (MalformedPatternException e) { - String message = "pattern compiling exception, element class: " + info.getElementClass().getName(); - try { - JavaPlugin providingPlugin = JavaPlugin.getProvidingPlugin(info.getElementClass()); - message += " (provided by " + providingPlugin.getName() + ")"; - } catch (IllegalArgumentException | IllegalStateException ignored) {} - throw new RuntimeException(message, e); + JavaPlugin providingPlugin = JavaPlugin.getProvidingPlugin(info.getElementClass()); + message += " (provided by " + providingPlugin.getName() + ")"; + } catch (IllegalArgumentException | IllegalStateException ignored) {} + throw new RuntimeException(message, e); - } - if (parseResult != null) { - assert parseResult.source != null; // parse results from parse_i have a source - List types = null; - for (int i = 0; i < parseResult.exprs.length; i++) { - if (parseResult.exprs[i] == null) { - if (types == null) - types = parseResult.source.getElements(TypePatternElement.class);; - ExprInfo exprInfo = types.get(i).getExprInfo(); - if (!exprInfo.isOptional) { - DefaultExpression expr = getDefaultExpression(exprInfo, info.patterns[patternIndex]); - if (!expr.init()) - continue patternsLoop; - parseResult.exprs[i] = expr; - } - } - } - T element = info.getElementClass().newInstance(); - - if (element instanceof EventRestrictedSyntax eventRestrictedSyntax) { - Class[] supportedEvents = eventRestrictedSyntax.supportedEvents(); - if (!getParser().isCurrentEvent(supportedEvents)) { - Iterator iterator = Arrays.stream(supportedEvents) - .map(it -> "the " + it.getSimpleName() - .replaceAll("([A-Z])", " $1") - .toLowerCase() - .trim()) - .iterator(); - - String events = StringUtils.join(iterator, ", ", " or "); - - Skript.error("'" + parseResult.expr + "' can only be used in " + events); - continue; + } + if (parseResult != null) { + assert parseResult.source != null; // parse results from parse_i have a source + List types = null; + for (int i = 0; i < parseResult.exprs.length; i++) { + if (parseResult.exprs[i] == null) { + if (types == null) + types = parseResult.source.getElements(TypePatternElement.class);; + ExprInfo exprInfo = types.get(i).getExprInfo(); + if (!exprInfo.isOptional) { + DefaultExpression expr = getDefaultExpression(exprInfo, info.patterns[patternIndex]); + if (!expr.init()) + continue patternsLoop; + parseResult.exprs[i] = expr; } } + } + + T element = info.instance(); - boolean success = element.init(parseResult.exprs, patternIndex, getParser().getHasDelayBefore(), parseResult); - if (success) { - log.printLog(); - return element; + if (element instanceof EventRestrictedSyntax eventRestrictedSyntax) { + Class[] supportedEvents = eventRestrictedSyntax.supportedEvents(); + if (!getParser().isCurrentEvent(supportedEvents)) { + Iterator iterator = Arrays.stream(supportedEvents) + .map(it -> "the " + it.getSimpleName() + .replaceAll("([A-Z])", " $1") + .toLowerCase() + .trim()) + .iterator(); + + String events = StringUtils.join(iterator, ", ", " or "); + + Skript.error("'" + parseResult.expr + "' can only be used in " + events); + continue; } } - } catch (InstantiationException | IllegalAccessException e) { - assert false; + + boolean success = element.init(parseResult.exprs, patternIndex, getParser().getHasDelayBefore(), parseResult); + if (success) { + log.printLog(); + return element; + } } } } diff --git a/src/main/java/ch/njol/skript/lang/SyntaxElementInfo.java b/src/main/java/ch/njol/skript/lang/SyntaxElementInfo.java index 2724451d23d..a4057ac4eba 100644 --- a/src/main/java/ch/njol/skript/lang/SyntaxElementInfo.java +++ b/src/main/java/ch/njol/skript/lang/SyntaxElementInfo.java @@ -3,14 +3,18 @@ import org.bukkit.event.Event; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Nullable; import org.skriptlang.skript.bukkit.registration.BukkitSyntaxInfos; +import org.skriptlang.skript.lang.structure.Structure; import org.skriptlang.skript.registration.SyntaxInfo; import org.skriptlang.skript.lang.structure.StructureInfo; import ch.njol.skript.SkriptAPIException; +import org.skriptlang.skript.util.ClassUtils; import java.lang.reflect.Modifier; import java.util.Arrays; +import java.util.function.Supplier; /** * @param the syntax element this info is for @@ -21,22 +25,20 @@ public class SyntaxElementInfo { public final Class elementClass; public final String[] patterns; public final String originClassPath; + private Supplier instanceSupplier; + + public SyntaxElementInfo(String[] patterns, Class elementClass, String originClassPath) { + this(patterns, elementClass, originClassPath, null); + } - public SyntaxElementInfo(String[] patterns, Class elementClass, String originClassPath) throws IllegalArgumentException { + public SyntaxElementInfo(String[] patterns, Class elementClass, String originClassPath, + @Nullable Supplier instanceSupplier) throws IllegalArgumentException { if (Modifier.isAbstract(elementClass.getModifiers())) throw new SkriptAPIException("Class " + elementClass.getName() + " is abstract"); - this.patterns = patterns; this.elementClass = elementClass; this.originClassPath = originClassPath; - try { - elementClass.getConstructor(); - } catch (final NoSuchMethodException e) { - // throwing an Exception throws an (empty) ExceptionInInitializerError instead, thus an Error is used - throw new Error(elementClass + " does not have a public nullary constructor", e); - } catch (final SecurityException e) { - throw new IllegalStateException("Skript cannot run properly because a security manager is blocking it!"); - } + this.instanceSupplier = instanceSupplier; } /** @@ -63,11 +65,28 @@ public String getOriginClassPath() { return originClassPath; } + /** + * Creates new instance of the syntax element of this syntax element info. + * + * @return new syntax element instance + */ + public E instance() { + if (instanceSupplier == null) { + try { + instanceSupplier = ClassUtils.instanceSupplier(getElementClass()); + } catch (Throwable throwable) { + throw new RuntimeException(throwable); + } + } + return instanceSupplier.get(); + } + @Contract("_ -> new") @ApiStatus.Experimental @SuppressWarnings("unchecked") public static , E extends SyntaxElement> I fromModern(SyntaxInfo info) { - if (info instanceof BukkitSyntaxInfos.Event event) { + if (info instanceof BukkitSyntaxInfos.Event) { + BukkitSyntaxInfos.Event event = (BukkitSyntaxInfos.Event) info; // We must first go back to the raw input String rawName = event.name().startsWith("On ") ? event.name().substring(3) @@ -75,7 +94,8 @@ public static , E extends SyntaxElement> I fromMo SkriptEventInfo eventInfo = new SkriptEventInfo<>( rawName, event.patterns().toArray(new String[0]), event.type(), event.origin().name(), - (Class[]) event.events().toArray(new Class[0])); + (Class[]) event.events().toArray(new Class[0]), + event::instance); String since = event.since(); if (since != null) eventInfo.since(since); @@ -87,24 +107,24 @@ public static , E extends SyntaxElement> I fromMo .examples(event.examples().toArray(new String[0])) .keywords(event.keywords().toArray(new String[0])) .requiredPlugins(event.requiredPlugins().toArray(new String[0])); - return (I) eventInfo; - } else if (info instanceof SyntaxInfo.Structure structure) { + } else if (info instanceof SyntaxInfo.Structure) { + SyntaxInfo.Structure structure = (SyntaxInfo.Structure) info; return (I) new StructureInfo<>(structure.patterns().toArray(new String[0]), structure.type(), - structure.origin().name(), structure.entryValidator(), structure.nodeType()); + structure.origin().name(), structure.entryValidator(), structure.nodeType(), structure::instance); } else if (info instanceof SyntaxInfo.Expression expression) { return (I) fromModernExpression(expression); } - - return (I) new SyntaxElementInfo<>(info.patterns().toArray(new String[0]), info.type(), info.origin().name()); + return (I) new SyntaxElementInfo<>(info.patterns().toArray(new String[0]), (Class) info.type(), + info.origin().name(), info::instance); } - + @Contract("_ -> new") @ApiStatus.Experimental private static , R> ExpressionInfo fromModernExpression(SyntaxInfo.Expression info) { return new ExpressionInfo<>( info.patterns().toArray(new String[0]), info.returnType(), - info.type(), info.origin().name(), ExpressionType.fromModern(info.priority()) + info.type(), info.origin().name(), ExpressionType.fromModern(info.priority()), info::instance ); } diff --git a/src/main/java/org/skriptlang/skript/lang/structure/StructureInfo.java b/src/main/java/org/skriptlang/skript/lang/structure/StructureInfo.java index 21c7e354d16..dcf7cf04477 100644 --- a/src/main/java/org/skriptlang/skript/lang/structure/StructureInfo.java +++ b/src/main/java/org/skriptlang/skript/lang/structure/StructureInfo.java @@ -6,6 +6,8 @@ import org.skriptlang.skript.lang.entry.EntryValidator; import org.skriptlang.skript.registration.SyntaxInfo; +import java.util.function.Supplier; + /** * Special {@link SyntaxElementInfo} for {@link Structure}s that may contain information such as the {@link EntryValidator}. */ @@ -23,21 +25,42 @@ public class StructureInfo extends SyntaxElementInfo { public final SyntaxInfo.Structure.NodeType nodeType; public StructureInfo(String[] patterns, Class c, String originClassPath) throws IllegalArgumentException { - this(patterns, c, originClassPath, false); + this(patterns, c, originClassPath, (Supplier) null); + } + + public StructureInfo(String[] patterns, Class c, String originClassPath, + @Nullable Supplier supplier) throws IllegalArgumentException { + this(patterns, c, originClassPath, false, supplier); + } + + public StructureInfo(String[] patterns, Class elementClass, String originClassPath, boolean simple) + throws IllegalArgumentException { + this(patterns, elementClass, originClassPath, simple, null); } - public StructureInfo(String[] patterns, Class elementClass, String originClassPath, boolean simple) throws IllegalArgumentException { - this(patterns, elementClass, originClassPath, null, simple ? SyntaxInfo.Structure.NodeType.SIMPLE : SyntaxInfo.Structure.NodeType.SECTION); + public StructureInfo(String[] patterns, Class elementClass, String originClassPath, boolean simple, + @Nullable Supplier supplier) throws IllegalArgumentException { + this(patterns, elementClass, originClassPath, null, + simple ? SyntaxInfo.Structure.NodeType.SIMPLE : SyntaxInfo.Structure.NodeType.SECTION, supplier); } - public StructureInfo(String[] patterns, Class elementClass, String originClassPath, @Nullable EntryValidator entryValidator) throws IllegalArgumentException { + public StructureInfo(String[] patterns, Class elementClass, String originClassPath, + @Nullable EntryValidator entryValidator) throws IllegalArgumentException { this(patterns, elementClass, originClassPath, entryValidator, SyntaxInfo.Structure.NodeType.SECTION); } @ApiStatus.Experimental public StructureInfo(String[] patterns, Class elementClass, String originClassPath, - @Nullable EntryValidator entryValidator, SyntaxInfo.Structure.NodeType nodeType) throws IllegalArgumentException { - super(patterns, elementClass, originClassPath); + @Nullable EntryValidator entryValidator, SyntaxInfo.Structure.NodeType nodeType) + throws IllegalArgumentException { + this(patterns, elementClass, originClassPath, entryValidator, nodeType, null); + } + + @ApiStatus.Experimental + public StructureInfo(String[] patterns, Class elementClass, String originClassPath, + @Nullable EntryValidator entryValidator, SyntaxInfo.Structure.NodeType nodeType, + @Nullable Supplier supplier) { + super(patterns, elementClass, originClassPath, supplier); this.entryValidator = entryValidator; this.nodeType = nodeType; this.simple = nodeType.canBeSimple(); diff --git a/src/main/java/org/skriptlang/skript/registration/SyntaxInfoImpl.java b/src/main/java/org/skriptlang/skript/registration/SyntaxInfoImpl.java index 82c436f48e0..09e3e8ed75e 100644 --- a/src/main/java/org/skriptlang/skript/registration/SyntaxInfoImpl.java +++ b/src/main/java/org/skriptlang/skript/registration/SyntaxInfoImpl.java @@ -20,7 +20,8 @@ class SyntaxInfoImpl implements SyntaxInfo { private final SyntaxOrigin origin; private final Class type; - private final @Nullable Supplier supplier; + private @Nullable Supplier supplier; + private final boolean providedSupplier; private final Collection patterns; private final Priority priority; @@ -37,6 +38,7 @@ protected SyntaxInfoImpl( this.origin = origin; this.type = type; this.supplier = supplier; + providedSupplier = supplier != null; this.patterns = ImmutableList.copyOf(patterns); this.priority = priority; } @@ -45,7 +47,7 @@ protected SyntaxInfoImpl( public Builder, T> toBuilder() { var builder = new BuilderImpl<>(type); builder.origin(origin); - if (supplier != null) { + if (providedSupplier) { builder.supplier(supplier); } builder.addPatterns(patterns); @@ -65,11 +67,14 @@ public Class type() { @Override public T instance() { - try { - return supplier == null ? type.getDeclaredConstructor().newInstance() : supplier.get(); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); + if (supplier == null) { + try { + supplier = ClassUtils.instanceSupplier(type); + } catch (Throwable throwable) { + throw new RuntimeException(throwable); + } } + return supplier.get(); } @Override diff --git a/src/main/java/org/skriptlang/skript/util/ClassUtils.java b/src/main/java/org/skriptlang/skript/util/ClassUtils.java index 9315f9b14b3..657373ef5f2 100644 --- a/src/main/java/org/skriptlang/skript/util/ClassUtils.java +++ b/src/main/java/org/skriptlang/skript/util/ClassUtils.java @@ -1,6 +1,13 @@ package org.skriptlang.skript.util; +import com.google.common.base.Preconditions; + +import java.lang.invoke.*; +import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; /** * Utilities for interacting with classes. @@ -16,4 +23,36 @@ public static boolean isNormalClass(Class clazz) { && !clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers()); } + private static final Map, Supplier> instanceSuppliers = new ConcurrentHashMap<>(); + + /** + * Creates supplier for given class if its nullary constructor exists. + * + * @param type class to create the supplier for + * @return supplier for the instances of given class, using its nullary constructor + * @param type + */ + @SuppressWarnings("unchecked") + public static Supplier instanceSupplier(Class type) throws Throwable { + Supplier cached = instanceSuppliers.get(type); + if (cached != null) + return (Supplier) cached; + Preconditions.checkArgument( + !Modifier.isAbstract(type.getModifiers()) && !Modifier.isInterface(type.getModifiers()), + "You can not create instance supplier for abstract classes"); + Constructor nullaryConstructor = type.getDeclaredConstructor(); + MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(type, MethodHandles.lookup()); + MethodHandle methodHandle = lookup.unreflectConstructor(nullaryConstructor); + CallSite callSite = LambdaMetafactory.metafactory(lookup, + "get", + MethodType.methodType(Supplier.class), + MethodType.methodType(Object.class), + methodHandle, + methodHandle.type() + ); + Supplier created = (Supplier) callSite.getTarget().invokeExact(); + instanceSuppliers.put(type, created); + return created; + } + } From bbc9957673c2ea120a218545b71f34a11c5fbc28 Mon Sep 17 00:00:00 2001 From: Pesek Date: Mon, 11 Aug 2025 15:05:39 +0200 Subject: [PATCH 2/5] resolved conflicts --- .../njol/skript/lang/SyntaxElementInfo.java | 57 +++++++++++-------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/SyntaxElementInfo.java b/src/main/java/ch/njol/skript/lang/SyntaxElementInfo.java index 20724b653e5..c1d7096129b 100644 --- a/src/main/java/ch/njol/skript/lang/SyntaxElementInfo.java +++ b/src/main/java/ch/njol/skript/lang/SyntaxElementInfo.java @@ -3,6 +3,7 @@ import org.bukkit.event.Event; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; import org.skriptlang.skript.bukkit.registration.BukkitSyntaxInfos; import org.skriptlang.skript.registration.SyntaxInfo; @@ -10,13 +11,14 @@ import ch.njol.skript.SkriptAPIException; import org.skriptlang.skript.registration.SyntaxOrigin; +import org.skriptlang.skript.util.ClassUtils; import org.skriptlang.skript.util.Priority; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.function.Supplier; /** * @param the syntax element this info is for @@ -27,22 +29,20 @@ public class SyntaxElementInfo implements SyntaxInfo public final Class elementClass; public final String[] patterns; public final String originClassPath; + private Supplier instanceSupplier; - public SyntaxElementInfo(String[] patterns, Class elementClass, String originClassPath) throws IllegalArgumentException { + public SyntaxElementInfo(String[] patterns, Class elementClass, String originClassPath) { + this(patterns, elementClass, originClassPath, null); + } + + public SyntaxElementInfo(String[] patterns, Class elementClass, String originClassPath, + @Nullable Supplier instanceSupplier) throws IllegalArgumentException { if (Modifier.isAbstract(elementClass.getModifiers())) throw new SkriptAPIException("Class " + elementClass.getName() + " is abstract"); - this.patterns = patterns; this.elementClass = elementClass; this.originClassPath = originClassPath; - try { - elementClass.getConstructor(); - } catch (final NoSuchMethodException e) { - // throwing an Exception throws an (empty) ExceptionInInitializerError instead, thus an Error is used - throw new Error(elementClass + " does not have a public nullary constructor", e); - } catch (final SecurityException e) { - throw new IllegalStateException("Skript cannot run properly because a security manager is blocking it!"); - } + this.instanceSupplier = instanceSupplier; } /** @@ -76,7 +76,8 @@ public String getOriginClassPath() { public static , E extends SyntaxElement> I fromModern(SyntaxInfo info) { if (info instanceof SyntaxElementInfo oldInfo) { return (I) oldInfo; - } else if (info instanceof BukkitSyntaxInfos.Event event) { + } else if (info instanceof BukkitSyntaxInfos.Event) { + BukkitSyntaxInfos.Event event = (BukkitSyntaxInfos.Event) info; // We must first go back to the raw input String rawName = event.name().startsWith("On ") ? event.name().substring(3) @@ -84,7 +85,7 @@ public static , E extends SyntaxElement> I fromMo SkriptEventInfo eventInfo = new SkriptEventInfo<>( rawName, event.patterns().toArray(new String[0]), event.type(), event.origin().name(), - (Class[]) event.events().toArray(new Class[0])); + (Class[]) event.events().toArray(new Class[0]), event::instance); String documentationId = event.documentationId(); if (documentationId != null) eventInfo.documentationID(documentationId); @@ -94,24 +95,28 @@ public static , E extends SyntaxElement> I fromMo .examples(event.examples().toArray(new String[0])) .keywords(event.keywords().toArray(new String[0])) .requiredPlugins(event.requiredPlugins().toArray(new String[0])); - return (I) eventInfo; - } else if (info instanceof SyntaxInfo.Structure structure) { + } else if (info instanceof SyntaxInfo.Structure) { + var structure = (Structure) info; return (I) new StructureInfo<>(structure.patterns().toArray(new String[0]), structure.type(), - structure.origin().name(), structure.entryValidator(), structure.nodeType()); + structure.origin().name(), structure.entryValidator(), structure.nodeType(), + structure::instance); } else if (info instanceof SyntaxInfo.Expression expression) { return (I) fromModernExpression(expression); } - return (I) new SyntaxElementInfo<>(info.patterns().toArray(new String[0]), info.type(), info.origin().name()); + return (I) new SyntaxElementInfo<>(info.patterns().toArray(new String[0]), (Class) info.type(), info.origin().name(), + info::instance); } - + @Contract("_ -> new") @ApiStatus.Experimental - private static , R> ExpressionInfo fromModernExpression(SyntaxInfo.Expression info) { + private static , R> ExpressionInfo fromModernExpression( + SyntaxInfo.Expression info) { return new ExpressionInfo<>( info.patterns().toArray(new String[0]), info.returnType(), - info.type(), info.origin().name(), ExpressionType.fromModern(info.priority()) + info.type(), info.origin().name(), ExpressionType.fromModern(info.priority()), + info::instance ); } @@ -139,12 +144,14 @@ public Class type() { @Override @ApiStatus.Internal public E instance() { - try { - return type().getDeclaredConstructor().newInstance(); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException | - NoSuchMethodException e) { - throw new RuntimeException(e); + if (instanceSupplier == null) { + try { + instanceSupplier = ClassUtils.instanceSupplier(getElementClass()); + } catch (Throwable throwable) { + throw new RuntimeException(throwable); + } } + return instanceSupplier.get(); } @Override From 04b10626e9619e77c652ef84f0ffc4f67e571445 Mon Sep 17 00:00:00 2001 From: Pesek <42549665+Pesekjak@users.noreply.github.com> Date: Mon, 11 Aug 2025 15:18:16 +0200 Subject: [PATCH 3/5] Update src/main/java/ch/njol/skript/lang/SkriptEventInfo.java Co-authored-by: Efnilite <35348263+Efnilite@users.noreply.github.com> --- src/main/java/ch/njol/skript/lang/SkriptEventInfo.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/ch/njol/skript/lang/SkriptEventInfo.java b/src/main/java/ch/njol/skript/lang/SkriptEventInfo.java index d6806b807df..b4e3182d2a2 100644 --- a/src/main/java/ch/njol/skript/lang/SkriptEventInfo.java +++ b/src/main/java/ch/njol/skript/lang/SkriptEventInfo.java @@ -49,7 +49,7 @@ public SkriptEventInfo(String name, String[] patterns, Class eventClass, Stri * @param eventClass The SkriptEvent's class * @param originClassPath The class path for the origin of this event. * @param events The Bukkit-Events this SkriptEvent listens to - * @param supplier supplier ofr the syntax info instance + * @param supplier supplier of the syntax info instance */ public SkriptEventInfo(String name, String[] patterns, Class eventClass, String originClassPath, Class[] events, @Nullable Supplier supplier) { From 39461d7b48a7121f15ace76b94cd882f6c959867 Mon Sep 17 00:00:00 2001 From: Pesek <42549665+Pesekjak@users.noreply.github.com> Date: Mon, 11 Aug 2025 15:18:24 +0200 Subject: [PATCH 4/5] Update src/main/java/org/skriptlang/skript/util/ClassUtils.java Co-authored-by: Efnilite <35348263+Efnilite@users.noreply.github.com> --- src/main/java/org/skriptlang/skript/util/ClassUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/skriptlang/skript/util/ClassUtils.java b/src/main/java/org/skriptlang/skript/util/ClassUtils.java index 657373ef5f2..fec9760f2ec 100644 --- a/src/main/java/org/skriptlang/skript/util/ClassUtils.java +++ b/src/main/java/org/skriptlang/skript/util/ClassUtils.java @@ -39,7 +39,7 @@ public static Supplier instanceSupplier(Class type) throws Throwable { return (Supplier) cached; Preconditions.checkArgument( !Modifier.isAbstract(type.getModifiers()) && !Modifier.isInterface(type.getModifiers()), - "You can not create instance supplier for abstract classes"); + "You cannot create instance suppliers for abstract classes"); Constructor nullaryConstructor = type.getDeclaredConstructor(); MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(type, MethodHandles.lookup()); MethodHandle methodHandle = lookup.unreflectConstructor(nullaryConstructor); From 38ecbd881adf4ea0bb89a4ee1b2abe800b5e5694 Mon Sep 17 00:00:00 2001 From: Pesek <42549665+Pesekjak@users.noreply.github.com> Date: Mon, 11 Aug 2025 15:18:33 +0200 Subject: [PATCH 5/5] Update src/main/java/org/skriptlang/skript/util/ClassUtils.java Co-authored-by: Efnilite <35348263+Efnilite@users.noreply.github.com> --- src/main/java/org/skriptlang/skript/util/ClassUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/skriptlang/skript/util/ClassUtils.java b/src/main/java/org/skriptlang/skript/util/ClassUtils.java index fec9760f2ec..adec15bb532 100644 --- a/src/main/java/org/skriptlang/skript/util/ClassUtils.java +++ b/src/main/java/org/skriptlang/skript/util/ClassUtils.java @@ -26,7 +26,7 @@ public static boolean isNormalClass(Class clazz) { private static final Map, Supplier> instanceSuppliers = new ConcurrentHashMap<>(); /** - * Creates supplier for given class if its nullary constructor exists. + * Creates a supplier for the given class if its nullary constructor exists. * * @param type class to create the supplier for * @return supplier for the instances of given class, using its nullary constructor