mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-04-15 15:23:13 +00:00
Clean up how we enumerate Lua/peripheral methods
- Move several interfaces out of `d00.computercraft.core.asm` into a new `aethods` package. It may make sense to expose this to the public API in a future commit (possibly part of #1462). - Add a new MethodSupplier<T> interface, which provides methods to iterate over all methods exported by an object (either directly, or including those from ObjectSources). This interface's concrete implementation (asm.MethodSupplierImpl), uses Generators and IntCaches as before - we can now make that all package-private though, which is nice! - Make the LuaMethod and PeripheralMethod MethodSupplier local to the ComputerContext. This currently has no effect (the underlying Generator is still global), but eventually we'll make GenericMethods non-global, which unlocks the door for #1382. - Update everything to use this new interface. This is mostly pretty sensible, but is a little uglier on the MC side (especially in generic peripherals), as we need to access the global ServerContext.
This commit is contained in:
parent
a29a516a3f
commit
591a7eca23
@ -14,6 +14,8 @@ import dan200.computercraft.core.computer.mainthread.MainThread;
|
||||
import dan200.computercraft.core.computer.mainthread.MainThreadConfig;
|
||||
import dan200.computercraft.core.lua.CobaltLuaMachine;
|
||||
import dan200.computercraft.core.lua.ILuaMachine;
|
||||
import dan200.computercraft.core.methods.MethodSupplier;
|
||||
import dan200.computercraft.core.methods.PeripheralMethod;
|
||||
import dan200.computercraft.impl.AbstractComputerCraftAPI;
|
||||
import dan200.computercraft.impl.ApiFactories;
|
||||
import dan200.computercraft.shared.CommonHooks;
|
||||
@ -134,6 +136,16 @@ public final class ServerContext {
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link MethodSupplier} used to find methods on peripherals.
|
||||
*
|
||||
* @return The {@link PeripheralMethod} method supplier.
|
||||
* @see ComputerContext#peripheralMethods()
|
||||
*/
|
||||
public MethodSupplier<PeripheralMethod> peripheralMethods() {
|
||||
return context.peripheralMethods();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tick all components of this server context. This should <em>NOT</em> be called outside of {@link CommonHooks}.
|
||||
*/
|
||||
|
@ -7,7 +7,7 @@ package dan200.computercraft.shared.computer.upload;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.core.apis.handles.BinaryReadableHandle;
|
||||
import dan200.computercraft.core.apis.handles.ByteBufferChannel;
|
||||
import dan200.computercraft.core.asm.ObjectSource;
|
||||
import dan200.computercraft.core.methods.ObjectSource;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Collections;
|
||||
|
@ -6,9 +6,12 @@ package dan200.computercraft.shared.peripheral.generic;
|
||||
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.peripheral.PeripheralType;
|
||||
import dan200.computercraft.core.asm.NamedMethod;
|
||||
import dan200.computercraft.core.asm.PeripheralMethod;
|
||||
import dan200.computercraft.core.methods.MethodSupplier;
|
||||
import dan200.computercraft.core.methods.NamedMethod;
|
||||
import dan200.computercraft.core.methods.PeripheralMethod;
|
||||
import dan200.computercraft.shared.computer.core.ServerContext;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@ -25,37 +28,36 @@ import java.util.Set;
|
||||
* See the platform-specific peripheral providers for the usage of this.
|
||||
*/
|
||||
final class GenericPeripheralBuilder {
|
||||
private final MethodSupplier<PeripheralMethod> peripheralMethods;
|
||||
|
||||
private @Nullable String name;
|
||||
private final Set<String> additionalTypes = new HashSet<>(0);
|
||||
private final ArrayList<SaturatedMethod> methods = new ArrayList<>(0);
|
||||
private final ArrayList<SaturatedMethod> methods = new ArrayList<>();
|
||||
|
||||
GenericPeripheralBuilder(MinecraftServer server) {
|
||||
peripheralMethods = ServerContext.get(server).peripheralMethods();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
IPeripheral toPeripheral(BlockEntity tile, Direction side) {
|
||||
IPeripheral toPeripheral(BlockEntity blockEntity, Direction side) {
|
||||
if (methods.isEmpty()) return null;
|
||||
|
||||
methods.trimToSize();
|
||||
return new GenericPeripheral(tile, side, name, additionalTypes, methods);
|
||||
return new GenericPeripheral(blockEntity, side, name, additionalTypes, methods);
|
||||
}
|
||||
|
||||
boolean addMethods(Object target) {
|
||||
var methods = PeripheralMethod.GENERATOR.getMethods(target.getClass());
|
||||
if (methods.isEmpty()) return false;
|
||||
|
||||
var saturatedMethods = this.methods;
|
||||
saturatedMethods.ensureCapacity(saturatedMethods.size() + methods.size());
|
||||
for (var method : methods) {
|
||||
saturatedMethods.add(new SaturatedMethod(target, method.name(), method.method()));
|
||||
return peripheralMethods.forEachSelfMethod(target, (name, method, info) -> {
|
||||
methods.add(new SaturatedMethod(target, name, method));
|
||||
|
||||
// If we have a peripheral type, use it. Always pick the smallest one, so it's consistent (assuming mods
|
||||
// don't change).
|
||||
var type = method.genericType();
|
||||
var type = info == null ? null : info.genericType();
|
||||
if (type != null && type.getPrimaryType() != null) {
|
||||
var name = type.getPrimaryType();
|
||||
if (this.name == null || this.name.compareTo(name) > 0) this.name = name;
|
||||
var primaryType = type.getPrimaryType();
|
||||
if (this.name == null || this.name.compareTo(primaryType) > 0) this.name = primaryType;
|
||||
}
|
||||
if (type != null) additionalTypes.addAll(type.getAdditionalTypes());
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.MethodResult;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.core.asm.PeripheralMethod;
|
||||
import dan200.computercraft.core.methods.PeripheralMethod;
|
||||
|
||||
/**
|
||||
* A {@link PeripheralMethod} along with the method's target.
|
||||
|
@ -16,10 +16,12 @@ import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.peripheral.NotAttachedException;
|
||||
import dan200.computercraft.api.peripheral.WorkMonitor;
|
||||
import dan200.computercraft.core.apis.PeripheralAPI;
|
||||
import dan200.computercraft.core.asm.PeripheralMethod;
|
||||
import dan200.computercraft.core.methods.PeripheralMethod;
|
||||
import dan200.computercraft.core.util.LuaUtil;
|
||||
import dan200.computercraft.shared.computer.core.ServerContext;
|
||||
import dan200.computercraft.shared.peripheral.modem.ModemPeripheral;
|
||||
import dan200.computercraft.shared.peripheral.modem.ModemState;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -284,7 +286,8 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements Wi
|
||||
|
||||
private void attachPeripheralImpl(IComputerAccess computer, ConcurrentMap<String, RemotePeripheralWrapper> peripherals, String periphName, IPeripheral peripheral) {
|
||||
if (!peripherals.containsKey(periphName) && !periphName.equals(getLocalPeripheral().getConnectedName())) {
|
||||
var wrapper = new RemotePeripheralWrapper(modem, peripheral, computer, periphName);
|
||||
var methods = ServerContext.get(((ServerLevel) getLevel()).getServer()).peripheralMethods().getSelfMethods(peripheral);
|
||||
var wrapper = new RemotePeripheralWrapper(modem, peripheral, computer, periphName, methods);
|
||||
peripherals.put(periphName, wrapper);
|
||||
wrapper.attach();
|
||||
}
|
||||
@ -314,7 +317,7 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements Wi
|
||||
private volatile boolean attached;
|
||||
private final Set<String> mounts = new HashSet<>();
|
||||
|
||||
RemotePeripheralWrapper(WiredModemElement element, IPeripheral peripheral, IComputerAccess computer, String name) {
|
||||
RemotePeripheralWrapper(WiredModemElement element, IPeripheral peripheral, IComputerAccess computer, String name, Map<String, PeripheralMethod> methods) {
|
||||
this.element = element;
|
||||
this.peripheral = peripheral;
|
||||
this.computer = computer;
|
||||
@ -322,7 +325,7 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements Wi
|
||||
|
||||
type = Objects.requireNonNull(peripheral.getType(), "Peripheral type cannot be null");
|
||||
additionalTypes = peripheral.getAdditionalTypes();
|
||||
methodMap = PeripheralAPI.getMethods(peripheral);
|
||||
methodMap = methods;
|
||||
}
|
||||
|
||||
public void attach() {
|
||||
|
@ -5,12 +5,18 @@
|
||||
package dan200.computercraft.core;
|
||||
|
||||
import dan200.computercraft.api.lua.ILuaAPIFactory;
|
||||
import dan200.computercraft.core.asm.LuaMethodSupplier;
|
||||
import dan200.computercraft.core.asm.PeripheralMethodSupplier;
|
||||
import dan200.computercraft.core.computer.ComputerThread;
|
||||
import dan200.computercraft.core.computer.GlobalEnvironment;
|
||||
import dan200.computercraft.core.computer.mainthread.MainThreadScheduler;
|
||||
import dan200.computercraft.core.computer.mainthread.NoWorkMainThreadScheduler;
|
||||
import dan200.computercraft.core.lua.CobaltLuaMachine;
|
||||
import dan200.computercraft.core.lua.ILuaMachine;
|
||||
import dan200.computercraft.core.lua.MachineEnvironment;
|
||||
import dan200.computercraft.core.methods.LuaMethod;
|
||||
import dan200.computercraft.core.methods.MethodSupplier;
|
||||
import dan200.computercraft.core.methods.PeripheralMethod;
|
||||
|
||||
import javax.annotation.CheckReturnValue;
|
||||
import javax.annotation.Nullable;
|
||||
@ -28,17 +34,22 @@ public final class ComputerContext {
|
||||
private final MainThreadScheduler mainThreadScheduler;
|
||||
private final ILuaMachine.Factory luaFactory;
|
||||
private final List<ILuaAPIFactory> apiFactories;
|
||||
private final MethodSupplier<LuaMethod> luaMethods;
|
||||
private final MethodSupplier<PeripheralMethod> peripheralMethods;
|
||||
|
||||
ComputerContext(
|
||||
GlobalEnvironment globalEnvironment, ComputerThread computerScheduler,
|
||||
MainThreadScheduler mainThreadScheduler, ILuaMachine.Factory luaFactory,
|
||||
List<ILuaAPIFactory> apiFactories
|
||||
List<ILuaAPIFactory> apiFactories, MethodSupplier<LuaMethod> luaMethods,
|
||||
MethodSupplier<PeripheralMethod> peripheralMethods
|
||||
) {
|
||||
this.globalEnvironment = globalEnvironment;
|
||||
this.computerScheduler = computerScheduler;
|
||||
this.mainThreadScheduler = mainThreadScheduler;
|
||||
this.luaFactory = luaFactory;
|
||||
this.apiFactories = apiFactories;
|
||||
this.luaMethods = luaMethods;
|
||||
this.peripheralMethods = peripheralMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -87,6 +98,25 @@ public final class ComputerContext {
|
||||
return apiFactories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link MethodSupplier} used to find methods on Lua values.
|
||||
*
|
||||
* @return The {@link LuaMethod} method supplier.
|
||||
* @see MachineEnvironment#luaMethods()
|
||||
*/
|
||||
public MethodSupplier<LuaMethod> luaMethods() {
|
||||
return luaMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link MethodSupplier} used to find methods on peripherals.
|
||||
*
|
||||
* @return The {@link PeripheralMethod} method supplier.
|
||||
*/
|
||||
public MethodSupplier<PeripheralMethod> peripheralMethods() {
|
||||
return peripheralMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the current {@link ComputerContext}, disposing of any resources inside.
|
||||
*
|
||||
@ -206,7 +236,9 @@ public final class ComputerContext {
|
||||
new ComputerThread(threads),
|
||||
mainThreadScheduler == null ? new NoWorkMainThreadScheduler() : mainThreadScheduler,
|
||||
luaFactory == null ? CobaltLuaMachine::new : luaFactory,
|
||||
apiFactories == null ? List.of() : apiFactories
|
||||
apiFactories == null ? List.of() : apiFactories,
|
||||
LuaMethodSupplier.create(),
|
||||
PeripheralMethodSupplier.create()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -10,9 +10,9 @@ import dan200.computercraft.core.computer.ComputerEnvironment;
|
||||
import dan200.computercraft.core.computer.ComputerSide;
|
||||
import dan200.computercraft.core.computer.GlobalEnvironment;
|
||||
import dan200.computercraft.core.filesystem.FileSystem;
|
||||
import dan200.computercraft.core.metrics.OperationTimer;
|
||||
import dan200.computercraft.core.metrics.Metric;
|
||||
import dan200.computercraft.core.metrics.MetricsObserver;
|
||||
import dan200.computercraft.core.metrics.OperationTimer;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
@ -7,13 +7,12 @@ package dan200.computercraft.core.apis;
|
||||
import dan200.computercraft.api.filesystem.Mount;
|
||||
import dan200.computercraft.api.filesystem.WritableMount;
|
||||
import dan200.computercraft.api.lua.*;
|
||||
import dan200.computercraft.api.peripheral.IDynamicPeripheral;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.peripheral.NotAttachedException;
|
||||
import dan200.computercraft.api.peripheral.WorkMonitor;
|
||||
import dan200.computercraft.core.asm.LuaMethod;
|
||||
import dan200.computercraft.core.asm.PeripheralMethod;
|
||||
import dan200.computercraft.core.computer.ComputerSide;
|
||||
import dan200.computercraft.core.methods.MethodSupplier;
|
||||
import dan200.computercraft.core.methods.PeripheralMethod;
|
||||
import dan200.computercraft.core.metrics.Metrics;
|
||||
import dan200.computercraft.core.util.LuaUtil;
|
||||
|
||||
@ -44,7 +43,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
type = Objects.requireNonNull(peripheral.getType(), "Peripheral type cannot be null");
|
||||
additionalTypes = peripheral.getAdditionalTypes();
|
||||
|
||||
methodMap = PeripheralAPI.getMethods(peripheral);
|
||||
methodMap = peripheralMethods.getSelfMethods(peripheral);
|
||||
}
|
||||
|
||||
public IPeripheral getPeripheral() {
|
||||
@ -172,11 +171,13 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
}
|
||||
|
||||
private final IAPIEnvironment environment;
|
||||
private final MethodSupplier<PeripheralMethod> peripheralMethods;
|
||||
private final PeripheralWrapper[] peripherals = new PeripheralWrapper[6];
|
||||
private boolean running;
|
||||
|
||||
public PeripheralAPI(IAPIEnvironment environment) {
|
||||
public PeripheralAPI(IAPIEnvironment environment, MethodSupplier<PeripheralMethod> peripheralMethods) {
|
||||
this.environment = environment;
|
||||
this.peripheralMethods = peripheralMethods;
|
||||
this.environment.setPeripheralChangeListener(this);
|
||||
running = false;
|
||||
}
|
||||
@ -315,21 +316,4 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String, PeripheralMethod> getMethods(IPeripheral peripheral) {
|
||||
var dynamicMethods = peripheral instanceof IDynamicPeripheral
|
||||
? Objects.requireNonNull(((IDynamicPeripheral) peripheral).getMethodNames(), "Peripheral methods cannot be null")
|
||||
: LuaMethod.EMPTY_METHODS;
|
||||
|
||||
var methods = PeripheralMethod.GENERATOR.getMethods(peripheral.getClass());
|
||||
|
||||
Map<String, PeripheralMethod> methodMap = new HashMap<>(methods.size() + dynamicMethods.length);
|
||||
for (var i = 0; i < dynamicMethods.length; i++) {
|
||||
methodMap.put(dynamicMethods[i], PeripheralMethod.DYNAMIC.get(i));
|
||||
}
|
||||
for (var method : methods) {
|
||||
methodMap.put(method.name(), method.method());
|
||||
}
|
||||
return methodMap;
|
||||
}
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ public final class TableHelper {
|
||||
|
||||
public static Optional<Double> optRealField(Map<?, ?> table, String key) throws LuaException {
|
||||
var value = table.get(key);
|
||||
if(value == null) {
|
||||
if (value == null) {
|
||||
return Optional.empty();
|
||||
} else {
|
||||
return Optional.of(getRealField(table, key));
|
||||
|
@ -10,7 +10,7 @@ import dan200.computercraft.core.apis.HTTPAPI;
|
||||
import dan200.computercraft.core.apis.handles.BinaryReadableHandle;
|
||||
import dan200.computercraft.core.apis.handles.EncodedReadableHandle;
|
||||
import dan200.computercraft.core.apis.handles.HandleGeneric;
|
||||
import dan200.computercraft.core.asm.ObjectSource;
|
||||
import dan200.computercraft.core.methods.ObjectSource;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
@ -11,6 +11,7 @@ import com.google.common.primitives.Primitives;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import dan200.computercraft.api.lua.*;
|
||||
import dan200.computercraft.api.peripheral.PeripheralType;
|
||||
import dan200.computercraft.core.methods.NamedMethod;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Type;
|
||||
@ -30,7 +31,7 @@ import java.util.function.Function;
|
||||
|
||||
import static org.objectweb.asm.Opcodes.*;
|
||||
|
||||
public final class Generator<T> {
|
||||
final class Generator<T> {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Generator.class);
|
||||
|
||||
private static final AtomicInteger METHOD_ID = new AtomicInteger();
|
||||
|
@ -7,7 +7,7 @@ package dan200.computercraft.core.asm;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.IntFunction;
|
||||
|
||||
public final class IntCache<T> {
|
||||
final class IntCache<T> {
|
||||
private final IntFunction<T> factory;
|
||||
private volatile Object[] cache = new Object[16];
|
||||
|
||||
|
@ -1,23 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.core.asm;
|
||||
|
||||
import dan200.computercraft.api.lua.*;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
public interface LuaMethod {
|
||||
Generator<LuaMethod> GENERATOR = new Generator<>(LuaMethod.class, Collections.singletonList(ILuaContext.class),
|
||||
m -> (target, context, args) -> context.executeMainThreadTask(() -> ResultHelpers.checkNormalResult(m.apply(target, context, args.escapes())))
|
||||
);
|
||||
|
||||
IntCache<LuaMethod> DYNAMIC = new IntCache<>(
|
||||
method -> (instance, context, args) -> ((IDynamicLuaObject) instance).callMethod(context, method, args)
|
||||
);
|
||||
|
||||
String[] EMPTY_METHODS = new String[0];
|
||||
|
||||
MethodResult apply(Object target, ILuaContext context, IArguments args) throws LuaException;
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.core.asm;
|
||||
|
||||
import dan200.computercraft.api.lua.IDynamicLuaObject;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.core.methods.LuaMethod;
|
||||
import dan200.computercraft.core.methods.MethodSupplier;
|
||||
import org.jetbrains.annotations.VisibleForTesting;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class LuaMethodSupplier {
|
||||
@VisibleForTesting
|
||||
static final Generator<LuaMethod> GENERATOR = new Generator<>(LuaMethod.class, List.of(ILuaContext.class),
|
||||
m -> (target, context, args) -> context.executeMainThreadTask(() -> ResultHelpers.checkNormalResult(m.apply(target, context, args.escapes())))
|
||||
);
|
||||
private static final IntCache<LuaMethod> DYNAMIC = new IntCache<>(
|
||||
method -> (instance, context, args) -> ((IDynamicLuaObject) instance).callMethod(context, method, args)
|
||||
);
|
||||
|
||||
private LuaMethodSupplier() {
|
||||
}
|
||||
|
||||
public static MethodSupplier<LuaMethod> create() {
|
||||
return new MethodSupplierImpl<>(GENERATOR, DYNAMIC, x -> x instanceof IDynamicLuaObject dynamic
|
||||
? Objects.requireNonNull(dynamic.getMethodNames(), "Dynamic methods cannot be null")
|
||||
: null
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.core.asm;
|
||||
|
||||
import dan200.computercraft.core.methods.MethodSupplier;
|
||||
import dan200.computercraft.core.methods.ObjectSource;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
final class MethodSupplierImpl<T> implements MethodSupplier<T> {
|
||||
private final Generator<T> generator;
|
||||
private final IntCache<T> dynamic;
|
||||
private final Function<Object, String[]> dynamicMethods;
|
||||
|
||||
MethodSupplierImpl(Generator<T> generator, IntCache<T> dynamic, Function<Object, String[]> dynamicMethods) {
|
||||
this.generator = generator;
|
||||
this.dynamic = dynamic;
|
||||
this.dynamicMethods = dynamicMethods;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean forEachSelfMethod(Object object, UntargetedConsumer<T> consumer) {
|
||||
var methods = generator.getMethods(object.getClass());
|
||||
for (var method : methods) consumer.accept(method.name(), method.method(), method);
|
||||
|
||||
var dynamicMethods = this.dynamicMethods.apply(object);
|
||||
if (dynamicMethods != null) {
|
||||
for (var i = 0; i < dynamicMethods.length; i++) consumer.accept(dynamicMethods[i], dynamic.get(i), null);
|
||||
}
|
||||
|
||||
return !methods.isEmpty() || dynamicMethods != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean forEachMethod(Object object, TargetedConsumer<T> consumer) {
|
||||
var methods = generator.getMethods(object.getClass());
|
||||
for (var method : methods) consumer.accept(object, method.name(), method.method(), method);
|
||||
|
||||
var hasMethods = !methods.isEmpty();
|
||||
|
||||
if (object instanceof ObjectSource source) {
|
||||
for (var extra : source.getExtra()) {
|
||||
var extraMethods = generator.getMethods(extra.getClass());
|
||||
if (!extraMethods.isEmpty()) hasMethods = true;
|
||||
for (var method : extraMethods) consumer.accept(object, method.name(), method.method(), method);
|
||||
}
|
||||
}
|
||||
|
||||
var dynamicMethods = this.dynamicMethods.apply(object);
|
||||
if (dynamicMethods != null) {
|
||||
hasMethods = true;
|
||||
for (var i = 0; i < dynamicMethods.length; i++) {
|
||||
consumer.accept(object, dynamicMethods[i], dynamic.get(i), null);
|
||||
}
|
||||
}
|
||||
|
||||
return hasMethods;
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.core.asm;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
* A Lua object which exposes additional methods.
|
||||
* <p>
|
||||
* This can be used to merge multiple objects together into one. Ideally this'd be part of the API, but I'm not entirely
|
||||
* happy with the interface - something I'd like to think about first.
|
||||
*/
|
||||
public interface ObjectSource {
|
||||
Iterable<Object> getExtra();
|
||||
|
||||
static <T> void allMethods(Generator<T> generator, Object object, BiConsumer<Object, NamedMethod<T>> accept) {
|
||||
for (var method : generator.getMethods(object.getClass())) accept.accept(object, method);
|
||||
|
||||
if (object instanceof ObjectSource source) {
|
||||
for (var extra : source.getExtra()) {
|
||||
for (var method : generator.getMethods(extra.getClass())) accept.accept(extra, method);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.core.asm;
|
||||
|
||||
import dan200.computercraft.api.lua.IArguments;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.MethodResult;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.peripheral.IDynamicPeripheral;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public interface PeripheralMethod {
|
||||
Generator<PeripheralMethod> GENERATOR = new Generator<>(PeripheralMethod.class, Arrays.asList(ILuaContext.class, IComputerAccess.class),
|
||||
m -> (target, context, computer, args) -> context.executeMainThreadTask(() -> ResultHelpers.checkNormalResult(m.apply(target, context, computer, args.escapes())))
|
||||
);
|
||||
|
||||
IntCache<PeripheralMethod> DYNAMIC = new IntCache<>(
|
||||
method -> (instance, context, computer, args) -> ((IDynamicPeripheral) instance).callMethod(computer, context, method, args)
|
||||
);
|
||||
|
||||
MethodResult apply(Object target, ILuaContext context, IComputerAccess computer, IArguments args) throws LuaException;
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.core.asm;
|
||||
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.peripheral.IDynamicPeripheral;
|
||||
import dan200.computercraft.core.methods.MethodSupplier;
|
||||
import dan200.computercraft.core.methods.PeripheralMethod;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class PeripheralMethodSupplier {
|
||||
private static final Generator<PeripheralMethod> GENERATOR = new Generator<>(PeripheralMethod.class, List.of(ILuaContext.class, IComputerAccess.class),
|
||||
m -> (target, context, computer, args) -> context.executeMainThreadTask(() -> ResultHelpers.checkNormalResult(m.apply(target, context, computer, args.escapes())))
|
||||
);
|
||||
private static final IntCache<PeripheralMethod> DYNAMIC = new IntCache<>(
|
||||
method -> (instance, context, computer, args) -> ((IDynamicPeripheral) instance).callMethod(computer, context, method, args)
|
||||
);
|
||||
|
||||
public static MethodSupplier<PeripheralMethod> create() {
|
||||
return new MethodSupplierImpl<>(GENERATOR, DYNAMIC, x -> x instanceof IDynamicPeripheral dynamic
|
||||
? Objects.requireNonNull(dynamic.getMethodNames(), "Dynamic methods cannot be null")
|
||||
: null
|
||||
);
|
||||
}
|
||||
}
|
@ -15,6 +15,8 @@ import dan200.computercraft.core.filesystem.FileSystemException;
|
||||
import dan200.computercraft.core.lua.ILuaMachine;
|
||||
import dan200.computercraft.core.lua.MachineEnvironment;
|
||||
import dan200.computercraft.core.lua.MachineException;
|
||||
import dan200.computercraft.core.methods.LuaMethod;
|
||||
import dan200.computercraft.core.methods.MethodSupplier;
|
||||
import dan200.computercraft.core.metrics.Metrics;
|
||||
import dan200.computercraft.core.metrics.MetricsObserver;
|
||||
import dan200.computercraft.core.util.Colour;
|
||||
@ -61,6 +63,7 @@ final class ComputerExecutor {
|
||||
private final MetricsObserver metrics;
|
||||
private final List<ILuaAPI> apis = new ArrayList<>();
|
||||
private final ComputerThread scheduler;
|
||||
private final MethodSupplier<LuaMethod> luaMethods;
|
||||
final TimeoutState timeout;
|
||||
|
||||
private @Nullable FileSystem fileSystem;
|
||||
@ -168,6 +171,7 @@ final class ComputerExecutor {
|
||||
metrics = computerEnvironment.getMetrics();
|
||||
luaFactory = context.luaFactory();
|
||||
scheduler = context.computerScheduler();
|
||||
luaMethods = context.luaMethods();
|
||||
timeout = new TimeoutState(scheduler);
|
||||
|
||||
var environment = computer.getEnvironment();
|
||||
@ -176,7 +180,7 @@ final class ComputerExecutor {
|
||||
apis.add(new TermAPI(environment));
|
||||
apis.add(new RedstoneAPI(environment));
|
||||
apis.add(new FSAPI(environment));
|
||||
apis.add(new PeripheralAPI(environment));
|
||||
apis.add(new PeripheralAPI(environment, context.peripheralMethods()));
|
||||
apis.add(new OSAPI(environment));
|
||||
if (CoreConfig.httpEnabled) apis.add(new HTTPAPI(environment));
|
||||
|
||||
@ -382,6 +386,7 @@ final class ComputerExecutor {
|
||||
return luaFactory.create(new MachineEnvironment(
|
||||
new LuaContext(computer), metrics, timeout,
|
||||
() -> apis.stream().map(api -> api instanceof ApiWrapper wrapper ? wrapper.getDelegate() : api).iterator(),
|
||||
luaMethods,
|
||||
computer.getGlobalEnvironment().getHostString()
|
||||
), bios);
|
||||
} catch (IOException e) {
|
||||
|
@ -8,7 +8,7 @@ import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.MethodResult;
|
||||
import dan200.computercraft.core.Logging;
|
||||
import dan200.computercraft.core.asm.LuaMethod;
|
||||
import dan200.computercraft.core.methods.LuaMethod;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.squiddev.cobalt.LuaError;
|
||||
|
@ -10,9 +10,9 @@ import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.ILuaFunction;
|
||||
import dan200.computercraft.core.CoreConfig;
|
||||
import dan200.computercraft.core.Logging;
|
||||
import dan200.computercraft.core.asm.LuaMethod;
|
||||
import dan200.computercraft.core.asm.ObjectSource;
|
||||
import dan200.computercraft.core.computer.TimeoutState;
|
||||
import dan200.computercraft.core.methods.LuaMethod;
|
||||
import dan200.computercraft.core.methods.MethodSupplier;
|
||||
import dan200.computercraft.core.util.Nullability;
|
||||
import dan200.computercraft.core.util.SanitisedError;
|
||||
import org.slf4j.Logger;
|
||||
@ -39,6 +39,7 @@ public class CobaltLuaMachine implements ILuaMachine {
|
||||
private final TimeoutState timeout;
|
||||
private final Runnable timeoutListener = this::updateTimeout;
|
||||
private final ILuaContext context;
|
||||
private final MethodSupplier<LuaMethod> luaMethods;
|
||||
|
||||
private final LuaState state;
|
||||
private final LuaThread mainRoutine;
|
||||
@ -51,6 +52,7 @@ public class CobaltLuaMachine implements ILuaMachine {
|
||||
public CobaltLuaMachine(MachineEnvironment environment, InputStream bios) throws MachineException, IOException {
|
||||
timeout = environment.timeout();
|
||||
context = environment.context();
|
||||
luaMethods = environment.luaMethods();
|
||||
|
||||
// Create an environment to run in
|
||||
var state = this.state = LuaState.builder()
|
||||
@ -161,28 +163,13 @@ public class CobaltLuaMachine implements ILuaMachine {
|
||||
|
||||
@Nullable
|
||||
private LuaTable wrapLuaObject(Object object) {
|
||||
var dynamicMethods = object instanceof IDynamicLuaObject dynamic
|
||||
? Objects.requireNonNull(dynamic.getMethodNames(), "Methods cannot be null")
|
||||
: LuaMethod.EMPTY_METHODS;
|
||||
|
||||
var table = new LuaTable();
|
||||
for (var i = 0; i < dynamicMethods.length; i++) {
|
||||
var method = dynamicMethods[i];
|
||||
table.rawset(method, new ResultInterpreterFunction(this, LuaMethod.DYNAMIC.get(i), object, context, method));
|
||||
}
|
||||
var found = luaMethods.forEachMethod(object, (target, name, method, info) ->
|
||||
table.rawset(name, info != null && info.nonYielding()
|
||||
? new BasicFunction(this, method, target, context, name)
|
||||
: new ResultInterpreterFunction(this, method, target, context, name)));
|
||||
|
||||
ObjectSource.allMethods(LuaMethod.GENERATOR, object, (instance, method) ->
|
||||
table.rawset(method.name(), method.nonYielding()
|
||||
? new BasicFunction(this, method.method(), instance, context, method.name())
|
||||
: new ResultInterpreterFunction(this, method.method(), instance, context, method.name())));
|
||||
|
||||
try {
|
||||
if (table.next(Constants.NIL).first().isNil()) return null;
|
||||
} catch (LuaError ignored) {
|
||||
// next should never throw on nil.
|
||||
}
|
||||
|
||||
return table;
|
||||
return found ? table : null;
|
||||
}
|
||||
|
||||
private LuaValue toValue(@Nullable Object object, @Nullable IdentityHashMap<Object, LuaValue> values) {
|
||||
|
@ -8,6 +8,8 @@ import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.core.computer.GlobalEnvironment;
|
||||
import dan200.computercraft.core.computer.TimeoutState;
|
||||
import dan200.computercraft.core.methods.LuaMethod;
|
||||
import dan200.computercraft.core.methods.MethodSupplier;
|
||||
import dan200.computercraft.core.metrics.MetricsObserver;
|
||||
|
||||
/**
|
||||
@ -18,6 +20,7 @@ import dan200.computercraft.core.metrics.MetricsObserver;
|
||||
* @param timeout The current timeout state. This should be used by the machine to interrupt its execution.
|
||||
* @param apis APIs to inject into the global environment. Each API should be converted into a Lua object
|
||||
* (following the same rules as any other value), and then set to all names in {@link ILuaAPI#getNames()}.
|
||||
* @param luaMethods A {@link MethodSupplier} to find methods on returned values.
|
||||
* @param hostString A {@linkplain GlobalEnvironment#getHostString() host string} to identify the current environment.
|
||||
* @see ILuaMachine.Factory
|
||||
*/
|
||||
@ -26,6 +29,7 @@ public record MachineEnvironment(
|
||||
MetricsObserver metrics,
|
||||
TimeoutState timeout,
|
||||
Iterable<ILuaAPI> apis,
|
||||
MethodSupplier<LuaMethod> luaMethods,
|
||||
String hostString
|
||||
) {
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.MethodResult;
|
||||
import dan200.computercraft.core.Logging;
|
||||
import dan200.computercraft.core.asm.LuaMethod;
|
||||
import dan200.computercraft.core.methods.LuaMethod;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.squiddev.cobalt.*;
|
||||
|
@ -0,0 +1,31 @@
|
||||
// SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.core.methods;
|
||||
|
||||
import dan200.computercraft.api.lua.*;
|
||||
|
||||
/**
|
||||
* A basic Lua function (i.e. one not associated with a peripheral) on some object (such as a {@link IDynamicLuaObject}
|
||||
* or {@link ILuaAPI}.
|
||||
* <p>
|
||||
* This interface is not typically implemented yourself, but instead generated from a {@link LuaFunction}-annotated
|
||||
* method.
|
||||
*
|
||||
* @see NamedMethod
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface LuaMethod {
|
||||
/**
|
||||
* Apply this method.
|
||||
*
|
||||
* @param target The object instance that this method targets.
|
||||
* @param context The Lua context for this function call.
|
||||
* @param args Arguments to this function.
|
||||
* @return The return call of this function.
|
||||
* @throws LuaException Thrown by the underlying method call.
|
||||
* @see IDynamicLuaObject#callMethod(ILuaContext, int, IArguments)
|
||||
*/
|
||||
MethodResult apply(Object target, ILuaContext context, IArguments args) throws LuaException;
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.core.methods;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Finds methods available on an object and yields them.
|
||||
*
|
||||
* @param <T> The type of method, such as {@link LuaMethod} or {@link PeripheralMethod}.
|
||||
*/
|
||||
public interface MethodSupplier<T> {
|
||||
/**
|
||||
* Iterate over methods available on an object, ignoring {@link ObjectSource}s.
|
||||
*
|
||||
* @param object The object to find methods for.
|
||||
* @param consumer The consumer to call for each method.
|
||||
* @return Whether any methods were found.
|
||||
*/
|
||||
boolean forEachSelfMethod(Object object, UntargetedConsumer<T> consumer);
|
||||
|
||||
/**
|
||||
* Generate a map of all methods targeting the current object, ignoring {@link ObjectSource}s.
|
||||
*
|
||||
* @param object The object to find methods for.
|
||||
* @return A map of all methods on the object.
|
||||
*/
|
||||
default Map<String, T> getSelfMethods(Object object) {
|
||||
var map = new HashMap<String, T>();
|
||||
forEachSelfMethod(object, (n, m, i) -> map.put(n, m));
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over all methods on an object, including {@link ObjectSource}s.
|
||||
*
|
||||
* @param object The object to find methods for.
|
||||
* @param consumer The consumer to call for each method.
|
||||
* @return Whether any methods were found.
|
||||
*/
|
||||
boolean forEachMethod(Object object, TargetedConsumer<T> consumer);
|
||||
|
||||
/**
|
||||
* A function which is called for each method on an object.
|
||||
*
|
||||
* @param <T> The type of method, such as {@link LuaMethod} or {@link PeripheralMethod}.
|
||||
* @see #forEachSelfMethod(Object, UntargetedConsumer)
|
||||
*/
|
||||
@FunctionalInterface
|
||||
interface UntargetedConsumer<T> {
|
||||
/**
|
||||
* Consume a method on an object.
|
||||
*
|
||||
* @param name The name of this method.
|
||||
* @param method The actual method definition.
|
||||
* @param info Additional information about the method, such as whether it will yield. May be {@code null}.
|
||||
*/
|
||||
void accept(String name, T method, @Nullable NamedMethod<T> info);
|
||||
}
|
||||
|
||||
/**
|
||||
* A function which is called for each method on an object and possibly nested objects.
|
||||
*
|
||||
* @param <T> The type of method, such as {@link LuaMethod} or {@link PeripheralMethod}.
|
||||
* @see #forEachMethod(Object, TargetedConsumer)
|
||||
*/
|
||||
@FunctionalInterface
|
||||
interface TargetedConsumer<T> {
|
||||
/**
|
||||
* Consume a method on an object.
|
||||
*
|
||||
* @param object The object this method targets, should be passed to the method's {@code apply(...)} function.
|
||||
* @param name The name of this method.
|
||||
* @param method The actual method definition.
|
||||
* @param info Additional information about the method, such as whether it will yield. May be {@code null}.
|
||||
*/
|
||||
void accept(Object object, String name, T method, @Nullable NamedMethod<T> info);
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.core.asm;
|
||||
package dan200.computercraft.core.methods;
|
||||
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.api.lua.MethodResult;
|
@ -0,0 +1,15 @@
|
||||
// SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.core.methods;
|
||||
|
||||
/**
|
||||
* A Lua object which exposes additional methods.
|
||||
* <p>
|
||||
* This can be used to merge multiple objects together into one. Ideally this'd be part of the API, but I'm not entirely
|
||||
* happy with the interface - something I'd like to think about first.
|
||||
*/
|
||||
public interface ObjectSource {
|
||||
Iterable<Object> getExtra();
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
// SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.core.methods;
|
||||
|
||||
import dan200.computercraft.api.lua.*;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.peripheral.IDynamicPeripheral;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
|
||||
/**
|
||||
* A Lua function associated with some peripheral.
|
||||
* <p>
|
||||
* This interface is not typically implemented yourself, but instead generated from a {@link LuaFunction}-annotated
|
||||
* method.
|
||||
*
|
||||
* @see NamedMethod
|
||||
* @see IPeripheral
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface PeripheralMethod {
|
||||
/**
|
||||
* Apply this method.
|
||||
*
|
||||
* @param target The object instance that this method targets.
|
||||
* @param context The Lua context for this function call.
|
||||
* @param computer The interface to the computer that is making the call.
|
||||
* @param args Arguments to this function.
|
||||
* @return The return call of this function.
|
||||
* @throws LuaException Thrown by the underlying method call.
|
||||
* @see IDynamicPeripheral#callMethod(IComputerAccess, ILuaContext, int, IArguments)
|
||||
*/
|
||||
MethodResult apply(Object target, ILuaContext context, IComputerAccess computer, IArguments args) throws LuaException;
|
||||
}
|
@ -4,32 +4,25 @@
|
||||
|
||||
package dan200.computercraft.core.apis;
|
||||
|
||||
import dan200.computercraft.api.lua.*;
|
||||
import dan200.computercraft.core.asm.LuaMethod;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaTask;
|
||||
import dan200.computercraft.api.lua.ObjectArguments;
|
||||
import dan200.computercraft.core.asm.LuaMethodSupplier;
|
||||
import dan200.computercraft.core.methods.LuaMethod;
|
||||
import dan200.computercraft.core.methods.MethodSupplier;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ObjectWrapper implements ILuaContext {
|
||||
private static final MethodSupplier<LuaMethod> LUA_METHODS = LuaMethodSupplier.create();
|
||||
|
||||
private final Object object;
|
||||
private final Map<String, LuaMethod> methodMap;
|
||||
|
||||
public ObjectWrapper(Object object) {
|
||||
this.object = object;
|
||||
var dynamicMethods = object instanceof IDynamicLuaObject dynamic
|
||||
? Objects.requireNonNull(dynamic.getMethodNames(), "Methods cannot be null")
|
||||
: LuaMethod.EMPTY_METHODS;
|
||||
|
||||
var methods = LuaMethod.GENERATOR.getMethods(object.getClass());
|
||||
|
||||
var methodMap = this.methodMap = new HashMap<>(methods.size() + dynamicMethods.length);
|
||||
for (var i = 0; i < dynamicMethods.length; i++) {
|
||||
methodMap.put(dynamicMethods[i], LuaMethod.DYNAMIC.get(i));
|
||||
}
|
||||
for (var method : methods) {
|
||||
methodMap.put(method.name(), method.method());
|
||||
}
|
||||
methodMap = LUA_METHODS.getSelfMethods(object);
|
||||
}
|
||||
|
||||
public Object[] call(String name, Object... args) throws LuaException {
|
||||
|
@ -7,6 +7,8 @@ package dan200.computercraft.core.asm;
|
||||
import dan200.computercraft.api.lua.*;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.core.computer.ComputerSide;
|
||||
import dan200.computercraft.core.methods.LuaMethod;
|
||||
import dan200.computercraft.core.methods.NamedMethod;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@ -21,9 +23,11 @@ import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
public class GeneratorTest {
|
||||
private static final Generator<LuaMethod> GENERATOR = LuaMethodSupplier.GENERATOR;
|
||||
|
||||
@Test
|
||||
public void testBasic() {
|
||||
var methods = LuaMethod.GENERATOR.getMethods(Basic.class);
|
||||
var methods = GENERATOR.getMethods(Basic.class);
|
||||
assertThat(methods, contains(
|
||||
allOf(
|
||||
named("go"),
|
||||
@ -34,48 +38,48 @@ public class GeneratorTest {
|
||||
|
||||
@Test
|
||||
public void testIdentical() {
|
||||
var methods = LuaMethod.GENERATOR.getMethods(Basic.class);
|
||||
var methods2 = LuaMethod.GENERATOR.getMethods(Basic.class);
|
||||
var methods = GENERATOR.getMethods(Basic.class);
|
||||
var methods2 = GENERATOR.getMethods(Basic.class);
|
||||
assertThat(methods, sameInstance(methods2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIdenticalMethods() {
|
||||
var methods = LuaMethod.GENERATOR.getMethods(Basic.class);
|
||||
var methods2 = LuaMethod.GENERATOR.getMethods(Basic2.class);
|
||||
var methods = GENERATOR.getMethods(Basic.class);
|
||||
var methods2 = GENERATOR.getMethods(Basic2.class);
|
||||
assertThat(methods, contains(named("go")));
|
||||
assertThat(methods.get(0).method(), sameInstance(methods2.get(0).method()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyClass() {
|
||||
assertThat(LuaMethod.GENERATOR.getMethods(Empty.class), is(empty()));
|
||||
assertThat(GENERATOR.getMethods(Empty.class), is(empty()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonPublicClass() {
|
||||
assertThat(LuaMethod.GENERATOR.getMethods(NonPublic.class), is(empty()));
|
||||
assertThat(GENERATOR.getMethods(NonPublic.class), is(empty()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonInstance() {
|
||||
assertThat(LuaMethod.GENERATOR.getMethods(NonInstance.class), is(empty()));
|
||||
assertThat(GENERATOR.getMethods(NonInstance.class), is(empty()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIllegalThrows() {
|
||||
assertThat(LuaMethod.GENERATOR.getMethods(IllegalThrows.class), is(empty()));
|
||||
assertThat(GENERATOR.getMethods(IllegalThrows.class), is(empty()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomNames() {
|
||||
var methods = LuaMethod.GENERATOR.getMethods(CustomNames.class);
|
||||
var methods = GENERATOR.getMethods(CustomNames.class);
|
||||
assertThat(methods, contains(named("go1"), named("go2")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testArgKinds() {
|
||||
var methods = LuaMethod.GENERATOR.getMethods(ArgKinds.class);
|
||||
var methods = GENERATOR.getMethods(ArgKinds.class);
|
||||
assertThat(methods, containsInAnyOrder(
|
||||
named("objectArg"), named("intArg"), named("optIntArg"),
|
||||
named("context"), named("arguments")
|
||||
@ -84,7 +88,7 @@ public class GeneratorTest {
|
||||
|
||||
@Test
|
||||
public void testEnum() throws LuaException {
|
||||
var methods = LuaMethod.GENERATOR.getMethods(EnumMethods.class);
|
||||
var methods = GENERATOR.getMethods(EnumMethods.class);
|
||||
assertThat(methods, containsInAnyOrder(named("getEnum"), named("optEnum")));
|
||||
|
||||
assertThat(apply(methods, new EnumMethods(), "getEnum", "front"), one(is("FRONT")));
|
||||
@ -95,7 +99,7 @@ public class GeneratorTest {
|
||||
|
||||
@Test
|
||||
public void testMainThread() throws LuaException {
|
||||
var methods = LuaMethod.GENERATOR.getMethods(MainThread.class);
|
||||
var methods = GENERATOR.getMethods(MainThread.class);
|
||||
assertThat(methods, contains(allOf(
|
||||
named("go"),
|
||||
contramap(is(false), "non-yielding", NamedMethod::nonYielding)
|
||||
@ -107,7 +111,7 @@ public class GeneratorTest {
|
||||
|
||||
@Test
|
||||
public void testUnsafe() {
|
||||
var methods = LuaMethod.GENERATOR.getMethods(Unsafe.class);
|
||||
var methods = GENERATOR.getMethods(Unsafe.class);
|
||||
assertThat(methods, contains(named("withUnsafe")));
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ import dan200.computercraft.api.peripheral.IDynamicPeripheral;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.core.computer.ComputerBootstrap;
|
||||
import dan200.computercraft.core.computer.ComputerSide;
|
||||
import dan200.computercraft.core.methods.ObjectSource;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
@ -11,11 +11,15 @@ import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
public class GenericPeripheralProvider {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(GenericPeripheralProvider.class);
|
||||
|
||||
interface Lookup<T> {
|
||||
@Nullable
|
||||
T find(Level world, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, Direction context);
|
||||
@ -29,7 +33,13 @@ public class GenericPeripheralProvider {
|
||||
public static IPeripheral getPeripheral(Level level, BlockPos pos, Direction side, @Nullable BlockEntity blockEntity) {
|
||||
if (blockEntity == null) return null;
|
||||
|
||||
var builder = new GenericPeripheralBuilder();
|
||||
var server = level.getServer();
|
||||
if (server == null) {
|
||||
LOG.warn("Fetching peripherals on a non-server level {}.", level, new IllegalStateException("Fetching peripherals on a non-server level."));
|
||||
return null;
|
||||
}
|
||||
|
||||
var builder = new GenericPeripheralBuilder(server);
|
||||
builder.addMethods(blockEntity);
|
||||
|
||||
for (var lookup : lookups) {
|
||||
|
@ -11,12 +11,16 @@ import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.common.util.NonNullConsumer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Objects;
|
||||
|
||||
public class GenericPeripheralProvider {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(GenericPeripheralProvider.class);
|
||||
|
||||
private static final ArrayList<Capability<?>> capabilities = new ArrayList<>();
|
||||
|
||||
public static synchronized void addCapability(Capability<?> capability) {
|
||||
@ -29,7 +33,13 @@ public class GenericPeripheralProvider {
|
||||
var blockEntity = level.getBlockEntity(pos);
|
||||
if (blockEntity == null) return null;
|
||||
|
||||
var builder = new GenericPeripheralBuilder();
|
||||
var server = level.getServer();
|
||||
if (server == null) {
|
||||
LOG.warn("Fetching peripherals on a non-server level {}.", level, new IllegalStateException("Fetching peripherals on a non-server level."));
|
||||
return null;
|
||||
}
|
||||
|
||||
var builder = new GenericPeripheralBuilder(server);
|
||||
builder.addMethods(blockEntity);
|
||||
|
||||
for (var capability : capabilities) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user