Skip to content

Commit e7f57af

Browse files
committed
Improve the bridge compiler capabilities.
1 parent 292835e commit e7f57af

File tree

10 files changed

+214
-26
lines changed

10 files changed

+214
-26
lines changed

pom.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>org.byteskript</groupId>
88
<artifactId>byteskript</artifactId>
9-
<version>1.0.27</version>
9+
<version>1.0.28</version>
1010
<name>ByteSkript</name>
1111
<description>A compiled JVM implementation of the Skript language.</description>
1212

@@ -68,7 +68,7 @@
6868
<dependency>
6969
<groupId>mx.kenzie</groupId>
7070
<artifactId>foundation</artifactId>
71-
<version>1.1.6</version>
71+
<version>1.1.7</version>
7272
<scope>compile</scope>
7373
</dependency>
7474
<dependency>
@@ -104,7 +104,7 @@
104104
<dependency>
105105
<groupId>org.jetbrains</groupId>
106106
<artifactId>annotations</artifactId>
107-
<version>22.0.0</version>
107+
<version>23.0.0</version>
108108
<scope>provided</scope>
109109
</dependency>
110110
<dependency>

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ public Class<?> createClass()
6262
final Class<?> parameter = parameters[i];
6363
visitor.visitVarInsn(20 + this.instructionOffset(argument), i);
6464
this.boxAtomic(visitor, parameter);
65+
this.conform(visitor, parameter);
6566
visitor.visitTypeInsn(192, Type.getInternalName(this.getUnboxingType(parameter)));
6667
this.unbox(visitor, parameter);
6768
}
@@ -100,6 +101,12 @@ protected void boxAtomic(MethodVisitor visitor, Class<?> parameter) {
100101
visitor.visitMethodInsn(184, Type.getInternalName(AtomicVariable.class), "unwrap", "(Ljava/lang/Object;)Ljava/lang/Object;", false);
101102
}
102103

104+
protected void conform(MethodVisitor visitor, Class<?> parameter) {
105+
if (parameter == Object.class || parameter == AtomicVariable.class) return;
106+
visitor.visitLdcInsn(Type.getObjectType(Type.getInternalName(this.getUnboxingType(parameter))));
107+
visitor.visitMethodInsn(184, "org/byteskript/skript/runtime/internal/ExtractedSyntaxCalls", "convert", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", false);
108+
}
109+
103110
protected Class<?> getUnboxingType(Class<?> primitive) {
104111
if (!primitive.isPrimitive()) return primitive;
105112
if (primitive == boolean.class) return Boolean.class;

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,7 @@
7575
import org.byteskript.skript.runtime.type.DataList;
7676
import org.byteskript.skript.runtime.type.DataMap;
7777

78-
import java.io.IOException;
79-
import java.io.InputStream;
78+
import java.io.*;
8079
import java.net.URL;
8180
import java.security.CodeSource;
8281
import java.util.ArrayList;
@@ -129,7 +128,10 @@ private SkriptLangSpec() {
129128
registerConverter(String.class, Long.class, Long::valueOf);
130129
registerConverter(String.class, Number.class, Double::valueOf);
131130
registerConverter(String.class, Error.class, Error::new);
131+
registerConverter(String.class, File.class, File::new);
132132
registerConverter(String.class, Class.class, Skript::findAnyClass);
133+
registerConverter(File.class, OutputStream.class, FileOutputStream::new);
134+
registerConverter(File.class, InputStream.class, FileInputStream::new);
133135
registerConverter(Object.class, String.class, Object::toString);
134136
registerSyntax(CompileState.ROOT,
135137
new TypeMember(),
@@ -145,6 +147,7 @@ private SkriptLangSpec() {
145147
registerSyntax(CompileState.MEMBER_BODY,
146148
new Verify(),
147149
new Trigger(),
150+
new Parameters(),
148151
new ReturnType(),
149152
new SyntaxEntry(),
150153
new EffectEntry(),
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Copyright (c) 2021 ByteSkript org (Moderocky)
3+
* View the full licence information and permissions:
4+
* https://github.com/Moderocky/ByteSkript/blob/master/LICENSE
5+
*/
6+
7+
package org.byteskript.skript.lang.syntax.entry;
8+
9+
import mx.kenzie.foundation.MethodBuilder;
10+
import mx.kenzie.foundation.Type;
11+
import mx.kenzie.foundation.compiler.State;
12+
import org.byteskript.skript.api.note.Documentation;
13+
import org.byteskript.skript.api.syntax.SimpleEntry;
14+
import org.byteskript.skript.compiler.*;
15+
import org.byteskript.skript.error.ScriptCompileError;
16+
import org.byteskript.skript.lang.element.StandardElements;
17+
18+
@Documentation(
19+
name = "Function Parameter Types",
20+
description = """
21+
Specify explicit parameter types of a function.
22+
This can be used for overriding functions in types.
23+
This can also be used for automatic type conformity.
24+
""",
25+
examples = {
26+
"""
27+
function my_func (a, b):
28+
parameters: string, number
29+
return: String
30+
trigger:
31+
return "hello " + {a}
32+
"""
33+
}
34+
)
35+
public class Parameters extends SimpleEntry {
36+
37+
public Parameters() {
38+
super(SkriptLangSpec.LIBRARY, StandardElements.METADATA, "parameters: %Types%");
39+
}
40+
41+
@Override
42+
public void compile(Context context, Pattern.Match match) {
43+
final String[] names = match.<String>meta().split(",");
44+
final MethodBuilder builder = context.getMethod();
45+
final Type[] types = builder.getErasure().parameterTypes();
46+
for (int i = 0; i < names.length && i < types.length; i++) {
47+
final String name = names[i].trim();
48+
if (name.isEmpty()) {
49+
context.getError().addHint(this, "All values need to be provided in a `x, y, z` comma-separated list.");
50+
throw new ScriptCompileError(context.lineNumber(), "All values in " + match.meta() + " need to be provided in a `x, y, z` comma-separated list.");
51+
} else {
52+
final Type type = context.findType(name);
53+
if (type != null) builder.setParameter(i, type);
54+
else builder.setParameter(i, new Type(name.replace('.', '/')));
55+
}
56+
}
57+
context.setState(CompileState.MEMBER_BODY);
58+
}
59+
60+
@Override
61+
public Pattern.Match match(String thing, Context context) {
62+
if (!thing.startsWith("parameters: ")) return null;
63+
final Pattern.Match match = super.match(thing, context);
64+
if (match == null) return null;
65+
final String name = match.groups()[0].trim();
66+
if (name.contains("\"")) {
67+
context.getError().addHint(this, "Types should not be written inside quotation marks.");
68+
return null;
69+
}
70+
return new Pattern.Match(Pattern.fakeMatcher(thing), name);
71+
}
72+
73+
@Override
74+
public boolean allowAsInputFor(Type type) {
75+
return false;
76+
}
77+
78+
@Override
79+
public boolean allowedIn(State state, Context context) {
80+
return super.allowedIn(state, context) && context.hasFlag(AreaFlag.IN_FUNCTION);
81+
}
82+
83+
84+
}

src/main/java/org/byteskript/skript/lang/syntax/flow/error/CatchSection.java

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,19 @@ public boolean allowedIn(State state, Context context) {
6363
&& context.getMethod() != null;
6464
}
6565

66+
public void compileTogether(Context context, Pattern.Match match, TryCatchTree tree) throws Throwable {
67+
final ElementTree holder = context.getCompileCurrent().nested()[0];
68+
tree.branch(context);
69+
store:
70+
{
71+
holder.type = StandardHandlers.SET;
72+
holder.compile = true;
73+
holder.preCompile(context);
74+
holder.compile(context);
75+
}
76+
context.setState(CompileState.CODE_BODY);
77+
}
78+
6679
@Override
6780
public void onSectionExit(Context context, SectionMeta meta) {
6881
final ProgrammaticSplitTree current;
@@ -87,17 +100,4 @@ public void preCompileInline(Context context, Pattern.Match match) throws Throwa
87100
this.preCompile(context, match);
88101
}
89102

90-
public void compileTogether(Context context, Pattern.Match match, TryCatchTree tree) throws Throwable {
91-
final ElementTree holder = context.getCompileCurrent().nested()[0];
92-
tree.branch(context);
93-
store:
94-
{
95-
holder.type = StandardHandlers.SET;
96-
holder.compile = true;
97-
holder.preCompile(context);
98-
holder.compile(context);
99-
}
100-
context.setState(CompileState.CODE_BODY);
101-
}
102-
103103
}

src/main/java/org/byteskript/skript/runtime/Skript.java

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -298,12 +298,18 @@ public static <To> To convert(Object from, Class<To> to) {
298298
*/
299299
@SuppressWarnings("unchecked")
300300
public static <From, To> To convert(From from, Class<To> to, boolean fail) {
301-
if (to.isInstance(from)) return to.cast(from);
302-
final Skript instance = findInstance();
303-
final Converter<From, To> converter = (Converter<From, To>) instance.getConverter(from.getClass(), to);
304-
if (converter != null) return converter.convert(from);
305-
if (fail) throw new ScriptRuntimeError("Unable convert '" + from + "' to type " + to.getSimpleName() + ".");
306-
else return null;
301+
try {
302+
if (to.isInstance(from)) return to.cast(from);
303+
final Skript instance = findInstance();
304+
final Converter<From, To> converter = (Converter<From, To>) instance.getConverter(from.getClass(), to);
305+
if (converter != null) return converter.convert(from);
306+
if (fail) throw new ScriptRuntimeError("Unable convert '" + from + "' to type " + to.getSimpleName() + ".");
307+
else return null;
308+
} catch (Throwable ex) {
309+
if (fail)
310+
throw new ScriptRuntimeError("Unable convert '" + from + "' to type " + to.getSimpleName() + ".", ex);
311+
else return null;
312+
}
307313
}
308314

309315
@SuppressWarnings("unchecked")
@@ -322,6 +328,11 @@ public <From, To> void registerConverter(Class<From> from, Class<To> to, Convert
322328
this.converters.put(data, converter);
323329
}
324330

331+
public <From, To> void unregisterConverter(Class<From> from, Class<To> to) {
332+
final Converter.Data data = new Converter.Data(from, to);
333+
this.converters.remove(data);
334+
}
335+
325336
@Description("""
326337
Gets the parent class-loader attached to this Skript runtime.
327338
This is used to search available libraries and scripts for classes.

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,11 @@ public static Object convert(Object from, Object object) {
4343
if (to.isAssignableFrom(from.getClass())) return to.cast(from);
4444
final Converter converter = findInstance().getConverter(from.getClass(), to);
4545
if (converter == null) return from;
46-
return converter.convert(from);
46+
try {
47+
return converter.convert(from);
48+
} catch (Throwable ex) {
49+
throw new ScriptRuntimeError("Error while converting '" + from + "' to a " + to.getSimpleName(), ex);
50+
}
4751
}
4852

4953
public static void handleTestError(Throwable throwable) {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
@FunctionalInterface
1010
public interface Converter<From, To> {
1111

12-
To convert(From from);
12+
To convert(From from) throws Throwable;
1313

1414
record Data(Class<?> from, Class<?> to) {}
1515

src/main/java/unsafe.java

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import mx.kenzie.mirror.MethodAccessor;
2+
import org.byteskript.skript.runtime.Skript;
3+
import org.byteskript.skript.runtime.UnsafeAccessor;
4+
import org.byteskript.skript.runtime.type.DataList;
5+
6+
import java.util.Arrays;
7+
8+
/**
9+
* This is the Java implementation of the 'unsafe' namespace functions.
10+
* Some implementations may re-implement this in Skript itself.
11+
* <p>
12+
* The unusual casing is to match Skript's code-style.
13+
*/
14+
public class unsafe extends UnsafeAccessor {
15+
16+
public static DataList get_threads() {
17+
final DataList list = new DataList();
18+
final int count = get_thread_count();
19+
final Thread[] threads = new Thread[count];
20+
Skript.THREAD_GROUP.enumerate(threads);
21+
list.addAll(Arrays.asList(threads));
22+
return list;
23+
}
24+
25+
public static int get_thread_count() {
26+
return Skript.THREAD_GROUP.activeCount();
27+
}
28+
29+
public static void await_load(Class<?> type) {
30+
UNSAFE.ensureClassInitialized(type);
31+
}
32+
33+
public static void register_converter(Class<Object> from, Class<Object> to, MethodAccessor<?> function) {
34+
final Skript skript = get_runtime();
35+
skript.registerConverter(from, to, function::invoke);
36+
}
37+
38+
public static Skript get_runtime() {
39+
final Skript skript = Skript.localInstance();
40+
if (skript != null) return skript;
41+
return Skript.currentInstance();
42+
}
43+
44+
public static void unregister_converter(Class<Object> from, Class<Object> to) {
45+
final Skript skript = get_runtime();
46+
skript.unregisterConverter(from, to);
47+
}
48+
49+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
2+
function blob (a, b, c):
3+
parameters: string
4+
trigger:
5+
assert {a} is a string
6+
return {c}
7+
8+
function blob (a, b):
9+
parameters: string, integer
10+
trigger:
11+
assert {a} is a string
12+
assert {b} is a number
13+
return {a} + " " + {b}
14+
15+
16+
function blob (a):
17+
parameters: string, number
18+
trigger:
19+
assert {a} is a string
20+
return {a}
21+
22+
function test:
23+
trigger:
24+
assert blob("hello", 2) is "hello 2"
25+
assert blob("hello", "2") is "hello 2"
26+
assert blob(2, 1) is "2 1"
27+
assert blob(2, "1") is "2 1"
28+
assert blob(1, 1, 2) is 2
29+
assert blob(1) is "1"
30+
return true

0 commit comments

Comments
 (0)