mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-10-25 02:47:39 +00:00
Support arguments being coerced from strings
In this case, we use Lua's tostring(x) semantics (well, modulo metamethods), instead of Java's Object.toString(x) call. This ensures that values are formatted (mostly) consistently between Lua and Java methods. - Add IArguments.getStringCoerced, which uses Lua's tostring semantics. - Add a Coerced<T> wrapper type, which says to use the .getXCoerced methods. I'm not thrilled about this interface - there's definitely an argument for using annotations - but this is probably more consistent for now. - Convert existing methods to use this call. Closes #1445
This commit is contained in:
@@ -4,12 +4,12 @@
|
||||
|
||||
package dan200.computercraft.core.apis;
|
||||
|
||||
import dan200.computercraft.api.lua.Coerced;
|
||||
import dan200.computercraft.api.lua.IArguments;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.core.terminal.Palette;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.core.util.StringUtil;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
@@ -36,13 +36,12 @@ public abstract class TermMethods {
|
||||
* Unlike functions like {@code write} and {@code print}, this does not wrap the text - it simply copies the
|
||||
* text to the current terminal line.
|
||||
*
|
||||
* @param arguments The text to write.
|
||||
* @param textA The text to write.
|
||||
* @throws LuaException (hidden) If the terminal cannot be found.
|
||||
* @cc.param text The text to write.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final void write(IArguments arguments) throws LuaException {
|
||||
var text = StringUtil.toString(arguments.get(0));
|
||||
public final void write(Coerced<String> textA) throws LuaException {
|
||||
var text = textA.value();
|
||||
var terminal = getTerminal();
|
||||
synchronized (terminal) {
|
||||
terminal.write(text);
|
||||
@@ -79,7 +78,7 @@ public abstract class TermMethods {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the position of the cursor. {@link #write(IArguments) terminal writes} will begin from this position.
|
||||
* Set the position of the cursor. {@link #write(Coerced) terminal writes} will begin from this position.
|
||||
*
|
||||
* @param x The new x position of the cursor.
|
||||
* @param y The new y position of the cursor.
|
||||
@@ -236,7 +235,7 @@ public abstract class TermMethods {
|
||||
/**
|
||||
* Writes {@code text} to the terminal with the specific foreground and background colours.
|
||||
* <p>
|
||||
* As with {@link #write(IArguments)}, the text will be written at the current cursor location, with the cursor
|
||||
* As with {@link #write(Coerced)}, the text will be written at the current cursor location, with the cursor
|
||||
* moving to the end of the text.
|
||||
* <p>
|
||||
* {@code textColour} and {@code backgroundColour} must both be strings the same length as {@code text}. All
|
||||
|
||||
@@ -4,11 +4,10 @@
|
||||
|
||||
package dan200.computercraft.core.apis.handles;
|
||||
|
||||
import dan200.computercraft.api.lua.IArguments;
|
||||
import dan200.computercraft.api.lua.Coerced;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.core.filesystem.TrackingCloseable;
|
||||
import dan200.computercraft.core.util.StringUtil;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
@@ -34,14 +33,13 @@ public class EncodedWritableHandle extends HandleGeneric {
|
||||
/**
|
||||
* Write a string of characters to the file.
|
||||
*
|
||||
* @param args The value to write.
|
||||
* @param textA The text to write to the file.
|
||||
* @throws LuaException If the file has been closed.
|
||||
* @cc.param value The value to write to the file.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final void write(IArguments args) throws LuaException {
|
||||
public final void write(Coerced<String> textA) throws LuaException {
|
||||
checkOpen();
|
||||
var text = StringUtil.toString(args.get(0));
|
||||
var text = textA.value();
|
||||
try {
|
||||
writer.write(text, 0, text.length());
|
||||
} catch (IOException e) {
|
||||
@@ -52,14 +50,13 @@ public class EncodedWritableHandle extends HandleGeneric {
|
||||
/**
|
||||
* Write a string of characters to the file, following them with a new line character.
|
||||
*
|
||||
* @param args The value to write.
|
||||
* @param textA The text to write to the file.
|
||||
* @throws LuaException If the file has been closed.
|
||||
* @cc.param value The value to write to the file.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final void writeLine(IArguments args) throws LuaException {
|
||||
public final void writeLine(Coerced<String> textA) throws LuaException {
|
||||
checkOpen();
|
||||
var text = StringUtil.toString(args.get(0));
|
||||
var text = textA.value();
|
||||
try {
|
||||
writer.write(text, 0, text.length());
|
||||
writer.newLine();
|
||||
|
||||
@@ -8,7 +8,6 @@ import com.google.common.base.Objects;
|
||||
import dan200.computercraft.api.lua.*;
|
||||
import dan200.computercraft.core.apis.http.options.Options;
|
||||
import dan200.computercraft.core.metrics.Metrics;
|
||||
import dan200.computercraft.core.util.StringUtil;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
|
||||
@@ -75,10 +74,10 @@ public class WebsocketHandle implements Closeable {
|
||||
* @cc.changed 1.81.0 Added argument for binary mode.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final void send(Object message, Optional<Boolean> binary) throws LuaException {
|
||||
public final void send(Coerced<String> message, Optional<Boolean> binary) throws LuaException {
|
||||
checkOpen();
|
||||
|
||||
var text = StringUtil.toString(message);
|
||||
var text = message.value();
|
||||
if (options.websocketMessage != 0 && text.length() > options.websocketMessage) {
|
||||
throw new LuaException("Message is too large");
|
||||
}
|
||||
|
||||
@@ -9,10 +9,7 @@ import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.primitives.Primitives;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import dan200.computercraft.api.lua.IArguments;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.api.lua.MethodResult;
|
||||
import dan200.computercraft.api.lua.*;
|
||||
import dan200.computercraft.api.peripheral.PeripheralType;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
@@ -47,6 +44,8 @@ public final class Generator<T> {
|
||||
private static final String INTERNAL_ARGUMENTS = Type.getInternalName(IArguments.class);
|
||||
private static final String DESC_ARGUMENTS = Type.getDescriptor(IArguments.class);
|
||||
|
||||
private static final String INTERNAL_COERCED = Type.getInternalName(Coerced.class);
|
||||
|
||||
private final Class<T> base;
|
||||
private final List<Class<?>> context;
|
||||
|
||||
@@ -276,6 +275,21 @@ public final class Generator<T> {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (arg == Coerced.class) {
|
||||
var klass = Reflect.getRawType(method, TypeToken.of(genericArg).resolveType(Reflect.COERCED_IN).getType(), false);
|
||||
if (klass == null) return null;
|
||||
|
||||
if (klass == String.class) {
|
||||
mw.visitTypeInsn(NEW, INTERNAL_COERCED);
|
||||
mw.visitInsn(DUP);
|
||||
mw.visitVarInsn(ALOAD, 2 + context.size());
|
||||
Reflect.loadInt(mw, argIndex);
|
||||
mw.visitMethodInsn(INVOKEINTERFACE, INTERNAL_ARGUMENTS, "getStringCoerced", "(I)Ljava/lang/String;", true);
|
||||
mw.visitMethodInsn(INVOKESPECIAL, INTERNAL_COERCED, "<init>", "(Ljava/lang/Object;)V", false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (arg == Optional.class) {
|
||||
var klass = Reflect.getRawType(method, TypeToken.of(genericArg).resolveType(Reflect.OPTIONAL_IN).getType(), false);
|
||||
if (klass == null) return null;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
package dan200.computercraft.core.asm;
|
||||
|
||||
import dan200.computercraft.api.lua.Coerced;
|
||||
import dan200.computercraft.api.lua.LuaTable;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.slf4j.Logger;
|
||||
@@ -20,6 +21,7 @@ import static org.objectweb.asm.Opcodes.ICONST_0;
|
||||
final class Reflect {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Reflect.class);
|
||||
static final java.lang.reflect.Type OPTIONAL_IN = Optional.class.getTypeParameters()[0];
|
||||
static final java.lang.reflect.Type COERCED_IN = Coerced.class.getTypeParameters()[0];
|
||||
|
||||
private Reflect() {
|
||||
}
|
||||
|
||||
@@ -106,6 +106,13 @@ final class VarargArguments implements IArguments {
|
||||
return converted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStringCoerced(int index) {
|
||||
checkAccessible();
|
||||
// This doesn't run __tostring, which is _technically_ wrong, but avoids a lot of complexity.
|
||||
return varargs.arg(index + 1).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType(int index) {
|
||||
checkAccessible();
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
|
||||
package dan200.computercraft.core.util;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public final class StringUtil {
|
||||
private StringUtil() {
|
||||
}
|
||||
@@ -24,8 +22,4 @@ public final class StringUtil {
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static String toString(@Nullable Object value) {
|
||||
return value == null ? "" : value.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public class ObjectWrapper implements ILuaContext {
|
||||
return method.apply(object, this, new ObjectArguments(args)).getResult();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@SuppressWarnings({ "unchecked", "TypeParameterUnusedInFormals" })
|
||||
public <T> T callOf(String name, Object... args) throws LuaException {
|
||||
return (T) call(name, args)[0];
|
||||
}
|
||||
|
||||
@@ -137,6 +137,7 @@ public class GeneratorTest {
|
||||
|
||||
public static class IllegalThrows {
|
||||
@LuaFunction
|
||||
@SuppressWarnings("DoNotCallSuggester")
|
||||
public final void go() throws IOException {
|
||||
throw new IOException();
|
||||
}
|
||||
|
||||
@@ -173,11 +173,13 @@ public class MethodTest {
|
||||
|
||||
public static class PeripheralThrow implements IPeripheral {
|
||||
@LuaFunction
|
||||
@SuppressWarnings("DoNotCallSuggester")
|
||||
public final void thisThread() throws LuaException {
|
||||
throw new LuaException("!");
|
||||
}
|
||||
|
||||
@LuaFunction(mainThread = true)
|
||||
@SuppressWarnings("DoNotCallSuggester")
|
||||
public final void mainThread() throws LuaException {
|
||||
throw new LuaException("!");
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ package dan200.computercraft.core.filesystem;
|
||||
|
||||
import com.google.common.io.Files;
|
||||
import dan200.computercraft.api.filesystem.WritableMount;
|
||||
import dan200.computercraft.api.lua.Coerced;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.ObjectArguments;
|
||||
import dan200.computercraft.core.TestFiles;
|
||||
import dan200.computercraft.core.apis.handles.EncodedWritableHandle;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -45,7 +45,7 @@ public class FileSystemTest {
|
||||
{
|
||||
var writer = fs.openForWrite("out.txt", false, EncodedWritableHandle::openUtf8);
|
||||
var handle = new EncodedWritableHandle(writer.get(), writer);
|
||||
handle.write(new ObjectArguments("This is a long line"));
|
||||
handle.write(new Coerced<>("This is a long line"));
|
||||
handle.doClose();
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ public class FileSystemTest {
|
||||
{
|
||||
var writer = fs.openForWrite("out.txt", false, EncodedWritableHandle::openUtf8);
|
||||
var handle = new EncodedWritableHandle(writer.get(), writer);
|
||||
handle.write(new ObjectArguments("Tiny line"));
|
||||
handle.write(new Coerced<>("Tiny line"));
|
||||
handle.doClose();
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ public class FileSystemTest {
|
||||
|
||||
fs.unmount("disk");
|
||||
|
||||
var err = assertThrows(LuaException.class, () -> handle.write(new ObjectArguments("Tiny line")));
|
||||
var err = assertThrows(LuaException.class, () -> handle.write(new Coerced<>("Tiny line")));
|
||||
assertEquals("attempt to use a closed file", err.getMessage());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user