Skip to content

Commit 51ad482

Browse files
committed
Support for variable arguments.
1 parent 0882718 commit 51ad482

File tree

6 files changed

+100
-2
lines changed

6 files changed

+100
-2
lines changed

src/main/java/org/byteskript/skript/compiler/BridgeCompiler.java

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,78 @@ public BridgeCompiler(MethodHandles.Lookup lookup, String owner, MethodType sour
4343
}
4444

4545
public Class<?> createClass()
46+
throws IllegalAccessException {
47+
final Class<?>[] arguments = source.parameterArray();
48+
final Class<?>[] parameters = target.getParameterTypes();
49+
final Class<?> expected = source.returnType();
50+
final Class<?> result = target.getReturnType();
51+
if (arguments.length != parameters.length && !target.isVarArgs())
52+
throw new ScriptRuntimeError("Function argument count did not match target parameter count.");
53+
final ClassWriter writer = new ClassWriter(0);
54+
writer.visit(Skript.JAVA_VERSION, 0x0001 | 0x1000, location, null, "java/lang/Object", null);
55+
final MethodVisitor visitor;
56+
final Type[] types = new Type[arguments.length];
57+
for (int i = 0; i < arguments.length; i++) types[i] = Type.getType(arguments[i]);
58+
visitor = writer.visitMethod(0x0001 | 0x0008 | 0x0040 | 0x1000, "bridge", Type.getMethodDescriptor(Type.getType(expected), types), null, null);
59+
visitor.visitCode();
60+
final int length;
61+
if (target.isVarArgs()) length = parameters.length - 1;
62+
else length = arguments.length;
63+
this.extractSimpleArguments(length, arguments, parameters, visitor);
64+
this.extractVarArguments(arguments, parameters, visitor, length);
65+
this.invoke(visitor);
66+
if (result == void.class) {
67+
visitor.visitInsn(1);
68+
visitor.visitInsn(176);
69+
} else {
70+
this.box(visitor, result);
71+
visitor.visitTypeInsn(192, Type.getInternalName(this.getWrapperType(expected)));
72+
visitor.visitInsn(171 + this.instructionOffset(expected));
73+
}
74+
final int stack;
75+
if (target.isVarArgs())
76+
stack = Math.max(parameters.length + 1 + this.wideIndexOffset(parameters, result), 1) + 4;
77+
else stack = Math.max(parameters.length + 1 + this.wideIndexOffset(parameters, result), 1);
78+
visitor.visitMaxs(stack, arguments.length);
79+
visitor.visitEnd();
80+
writer.visitEnd();
81+
this.generated = lookup.defineClass(writer.toByteArray());
82+
return generated;
83+
}
84+
85+
private void extractVarArguments(Class<?>[] arguments, Class<?>[] parameters, MethodVisitor visitor, int length) {
86+
if (!target.isVarArgs()) return;
87+
final int remaining = arguments.length - length;
88+
final Class<?> array = parameters[parameters.length - 1];
89+
final Class<?> parameter = array.getComponentType();
90+
visitor.visitIntInsn(16, remaining);
91+
visitor.visitTypeInsn(189, Type.getInternalName(parameter));
92+
for (int i = 0; i < remaining; i++) {
93+
visitor.visitInsn(89);
94+
visitor.visitIntInsn(16, i);
95+
final Class<?> argument = arguments[i];
96+
visitor.visitVarInsn(20 + this.instructionOffset(argument), i);
97+
this.boxAtomic(visitor, parameter);
98+
this.conform(visitor, parameter);
99+
visitor.visitTypeInsn(192, Type.getInternalName(this.getUnboxingType(parameter)));
100+
this.unbox(visitor, parameter);
101+
visitor.visitInsn(83);
102+
}
103+
}
104+
105+
private void extractSimpleArguments(int length, Class<?>[] arguments, Class<?>[] parameters, MethodVisitor visitor) {
106+
for (int i = 0; i < length; i++) { // assume no fat arguments ?
107+
final Class<?> argument = arguments[i];
108+
final Class<?> parameter = parameters[i];
109+
visitor.visitVarInsn(20 + this.instructionOffset(argument), i);
110+
this.boxAtomic(visitor, parameter);
111+
this.conform(visitor, parameter);
112+
visitor.visitTypeInsn(192, Type.getInternalName(this.getUnboxingType(parameter)));
113+
this.unbox(visitor, parameter);
114+
}
115+
}
116+
117+
public Class<?> createClass0()
46118
throws IllegalAccessException {
47119
final Class<?>[] arguments = source.parameterArray();
48120
final Class<?>[] parameters = target.getParameterTypes();

src/main/java/org/byteskript/skript/compiler/SkriptLangSpec.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ private SkriptLangSpec() {
133133
registerConverter(String.class, Error.class, Error::new);
134134
registerConverter(String.class, File.class, File::new);
135135
registerConverter(String.class, Class.class, Skript::findAnyClass);
136+
registerConverter(String.class, String[].class, s -> new String[] {s});
137+
registerConverter(Object.class, Object[].class, o -> new Object[] {o});
136138
registerConverter(Object[].class, DataList.class, DataList::of);
137139
registerConverter(Collection.class, Object[].class, Collection::toArray);
138140
registerConverter(File.class, OutputStream.class, FileOutputStream::new);

src/main/java/org/byteskript/skript/runtime/internal/Metafactory.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
import java.lang.invoke.*;
1818
import java.lang.reflect.Method;
1919
import java.lang.reflect.Modifier;
20+
import java.util.ArrayList;
2021
import java.util.Arrays;
22+
import java.util.List;
2123

2224
@Description("""
2325
The meta-factory handles building call-sites at runtime.
@@ -44,6 +46,7 @@ public static CallSite createBridge(MethodHandles.Lookup caller, String name, Me
4446
}
4547

4648
private static Method findTarget(MethodHandles.Lookup caller, String name, Class<?> owner, Class<?>... parameters) {
49+
final List<Method> varArgs = new ArrayList<>();
4750
{
4851
final Method[] methods = owner.getMethods();
4952
for (final Method method : methods) {
@@ -53,6 +56,7 @@ private static Method findTarget(MethodHandles.Lookup caller, String name, Class
5356
for (final Method method : methods) {
5457
if (!Modifier.isStatic(method.getModifiers())) continue; // can only hit statics for now
5558
if (!method.getName().equals(name)) continue;
59+
if (method.isVarArgs()) varArgs.add(method);
5660
if (parameters.length == method.getParameterCount()) return method;
5761
}
5862
}
@@ -65,9 +69,19 @@ private static Method findTarget(MethodHandles.Lookup caller, String name, Class
6569
for (final Method method : methods) {
6670
if (!Modifier.isStatic(method.getModifiers())) continue; // can only hit statics for now
6771
if (!method.getName().equals(name)) continue;
72+
if (method.isVarArgs()) varArgs.add(method);
6873
if (parameters.length == method.getParameterCount()) return method;
6974
}
7075
}
76+
for (final Method method : varArgs) {
77+
final int check = method.getParameterCount() - 1;
78+
if (parameters.length < check) continue;
79+
if (Arrays.equals(method.getParameterTypes(), 0, check, parameters, 0, check)) return method;
80+
}
81+
for (final Method method : varArgs) {
82+
if (parameters.length < method.getParameterCount() - 1) continue;
83+
return method;
84+
}
7185
throw new ScriptRuntimeError("Unable to find function '" + name + Arrays.toString(parameters).replace('[', '(')
7286
.replace(']', ')') + "' from " + owner.getSimpleName());
7387
}

src/main/java/org/byteskript/skript/runtime/type/OperatorFunction.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@ public boolean reversible() {
2929
};
3030
}
3131

32+
Object union(A first, B second) throws Throwable;
33+
3234
default Object union2(B first, A second) throws Throwable {
3335
return this.union(second, first);
3436
}
3537

36-
Object union(A first, B second) throws Throwable;
37-
3838
default boolean reversible() {
3939
return true;
4040
}

src/test/java/org/byteskript/skript/test/SyntaxTest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ public static void main(String[] args) throws Throwable { // test only
2929
.getResourceAsStream("tests/bracket.bsk"), "skript.bracket");
3030
}
3131

32+
public static boolean varArgs(String first, String... second) {
33+
return first.equals("hello") && second.length == 3;
34+
}
35+
3236
@Test
3337
public void all() throws Throwable {
3438
final URI uri = SyntaxTest.class.getClassLoader().getResource("tests").toURI();

src/test/resources/tests/functionexpression.bsk

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,10 @@ function test:
1616
set {@var} to 5
1717
assert box({@var}, 2) is 3
1818
assert {@var} is 2
19+
set {result} to varArgs("hello") from org/byteskript/skript/test/SyntaxTest
20+
assert {result} is false
21+
set {result} to varArgs("hello", "there") from org/byteskript/skript/test/SyntaxTest
22+
assert {result} is false
23+
set {result} to varArgs("hello", "there", "general", "kenobi") from org/byteskript/skript/test/SyntaxTest
24+
assert {result} is true
1925
return true

0 commit comments

Comments
 (0)