1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-06-25 22:53:22 +00:00

Switch the core library to be non-null by default

See comments in c8c128d335 for further
details. This requires /relatively/ few changes - mostly cases we were
missing @Nullable annotations.
This commit is contained in:
Jonathan Coates 2022-11-06 11:55:26 +00:00
parent c8c128d335
commit c82f37d3bf
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
88 changed files with 465 additions and 626 deletions

View File

@ -6,6 +6,7 @@ import java.nio.charset.StandardCharsets
plugins {
`java-library`
idea
jacoco
checkstyle
id("com.diffplug.spotless")
@ -139,3 +140,12 @@ spotless {
ktlint().editorConfigOverride(ktlintConfig)
}
}
idea.module {
excludeDirs.addAll(project.files("run", "out", "logs").files)
// Force Gradle to write to inherit the output directory from the parent, instead of writing to out/xxx/classes.
// This is required for Loom, and we patch Forge's run configurations to work there.
// TODO: Submit a patch to Forge to support ProjectRootManager.
inheritOutputDirs = true
}

View File

@ -18,6 +18,7 @@ dependencies {
compileOnly(project(":mc-stubs"))
compileOnlyApi(libs.jsr305)
compileOnlyApi(libs.checkerFramework)
compileOnlyApi(libs.jetbrainsAnnotations)
"docApi"(project(":"))
}

View File

@ -5,6 +5,8 @@
*/
package dan200.computercraft.api.lua;
import org.jetbrains.annotations.Contract;
import javax.annotation.Nullable;
import java.nio.ByteBuffer;
import java.util.Map;
@ -387,7 +389,9 @@ default boolean optBoolean(int index, boolean def) throws LuaException {
* @return The argument's value, or {@code def} if none was provided.
* @throws LuaException If the value is not a string.
*/
default String optString(int index, String def) throws LuaException {
@Nullable
@Contract("_, !null -> !null")
default String optString(int index, @Nullable String def) throws LuaException {
return optString(index).orElse(def);
}
@ -399,7 +403,9 @@ default String optString(int index, String def) throws LuaException {
* @return The argument's value, or {@code def} if none was provided.
* @throws LuaException If the value is not a table.
*/
default Map<?, ?> optTable(int index, Map<Object, Object> def) throws LuaException {
@Nullable
@Contract("_, !null -> !null")
default Map<?, ?> optTable(int index, @Nullable Map<Object, Object> def) throws LuaException {
return optTable(index).orElse(def);
}
}

View File

@ -86,6 +86,7 @@ default String mountWritable(String desiredLocation, IWritableMount mount) {
* @see #unmount(String)
* @see IMount
*/
@Nullable
String mountWritable(String desiredLocation, IWritableMount mount, String driveName);
/**

View File

@ -5,6 +5,7 @@ plugins {
id("cc-tweaked.kotlin-convention")
id("cc-tweaked.java-convention")
id("cc-tweaked.publishing")
id("cc-tweaked.errorprone")
id("cc-tweaked")
}

View File

@ -7,7 +7,6 @@
import dan200.computercraft.api.lua.ILuaAPIFactory;
import javax.annotation.Nonnull;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
@ -20,7 +19,7 @@ private ApiFactories() {
private static final Collection<ILuaAPIFactory> factories = new LinkedHashSet<>();
private static final Collection<ILuaAPIFactory> factoriesView = Collections.unmodifiableCollection(factories);
public static synchronized void register(@Nonnull ILuaAPIFactory factory) {
public static synchronized void register(ILuaAPIFactory factory) {
Objects.requireNonNull(factory, "provider cannot be null");
factories.add(factory);
}

View File

@ -13,7 +13,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
@ -40,8 +40,9 @@ public void unmountAll() {
mounts.clear();
}
@Nullable
@Override
public synchronized String mount(@Nonnull String desiredLoc, @Nonnull IMount mount, @Nonnull String driveName) {
public synchronized String mount(String desiredLoc, IMount mount, String driveName) {
Objects.requireNonNull(desiredLoc, "desiredLocation cannot be null");
Objects.requireNonNull(mount, "mount cannot be null");
Objects.requireNonNull(driveName, "driveName cannot be null");
@ -49,14 +50,14 @@ public synchronized String mount(@Nonnull String desiredLoc, @Nonnull IMount mou
// Mount the location
String location;
var fileSystem = environment.getFileSystem();
if (fileSystem == null) throw new IllegalStateException("File system has not been created");
synchronized (fileSystem) {
location = findFreeLocation(desiredLoc);
if (location != null) {
try {
fileSystem.mount(driveName, location, mount);
} catch (FileSystemException ignored) {
} catch (FileSystemException e) {
throw new IllegalArgumentException(e.getMessage());
}
}
}
@ -65,8 +66,9 @@ public synchronized String mount(@Nonnull String desiredLoc, @Nonnull IMount mou
return location;
}
@Nullable
@Override
public synchronized String mountWritable(@Nonnull String desiredLoc, @Nonnull IWritableMount mount, @Nonnull String driveName) {
public synchronized String mountWritable(String desiredLoc, IWritableMount mount, String driveName) {
Objects.requireNonNull(desiredLoc, "desiredLocation cannot be null");
Objects.requireNonNull(mount, "mount cannot be null");
Objects.requireNonNull(driveName, "driveName cannot be null");
@ -74,14 +76,14 @@ public synchronized String mountWritable(@Nonnull String desiredLoc, @Nonnull IW
// Mount the location
String location;
var fileSystem = environment.getFileSystem();
if (fileSystem == null) throw new IllegalStateException("File system has not been created");
synchronized (fileSystem) {
location = findFreeLocation(desiredLoc);
if (location != null) {
try {
fileSystem.mountWritable(driveName, location, mount);
} catch (FileSystemException ignored) {
} catch (FileSystemException e) {
throw new IllegalArgumentException(e.getMessage());
}
}
}
@ -91,7 +93,7 @@ public synchronized String mountWritable(@Nonnull String desiredLoc, @Nonnull IW
}
@Override
public void unmount(String location) {
public void unmount(@Nullable String location) {
if (location == null) return;
if (!mounts.contains(location)) throw new IllegalStateException("You didn't mount this location");
@ -105,17 +107,17 @@ public int getID() {
}
@Override
public void queueEvent(@Nonnull String event, Object... arguments) {
public void queueEvent(String event, @Nullable Object... arguments) {
Objects.requireNonNull(event, "event cannot be null");
environment.queueEvent(event, arguments);
}
@Nonnull
@Override
public IWorkMonitor getMainThreadMonitor() {
return environment.getMainThreadMonitor();
}
@Nullable
private String findFreeLocation(String desiredLoc) {
try {
var fileSystem = environment.getFileSystem();

View File

@ -17,6 +17,7 @@
import dan200.computercraft.core.filesystem.FileSystemException;
import dan200.computercraft.core.metrics.Metrics;
import javax.annotation.Nullable;
import java.nio.file.attribute.FileTime;
import java.util.HashMap;
import java.util.Map;
@ -58,7 +59,7 @@
*/
public class FSAPI implements ILuaAPI {
private final IAPIEnvironment environment;
private FileSystem fileSystem = null;
private @Nullable FileSystem fileSystem = null;
public FSAPI(IAPIEnvironment env) {
environment = env;
@ -79,6 +80,12 @@ public void shutdown() {
fileSystem = null;
}
private FileSystem getFileSystem() {
var filesystem = fileSystem;
if (filesystem == null) throw new IllegalStateException("File system is not mounted");
return filesystem;
}
/**
* Returns a list of files in a directory.
*
@ -97,7 +104,7 @@ public void shutdown() {
public final String[] list(String path) throws LuaException {
environment.observe(Metrics.FS_OPS);
try {
return fileSystem.list(path);
return getFileSystem().list(path);
} catch (FileSystemException e) {
throw new LuaException(e.getMessage());
}
@ -178,7 +185,7 @@ public final String getDir(String path) {
@LuaFunction
public final long getSize(String path) throws LuaException {
try {
return fileSystem.getSize(path);
return getFileSystem().getSize(path);
} catch (FileSystemException e) {
throw new LuaException(e.getMessage());
}
@ -193,7 +200,7 @@ public final long getSize(String path) throws LuaException {
@LuaFunction
public final boolean exists(String path) {
try {
return fileSystem.exists(path);
return getFileSystem().exists(path);
} catch (FileSystemException e) {
return false;
}
@ -208,7 +215,7 @@ public final boolean exists(String path) {
@LuaFunction
public final boolean isDir(String path) {
try {
return fileSystem.isDir(path);
return getFileSystem().isDir(path);
} catch (FileSystemException e) {
return false;
}
@ -223,7 +230,7 @@ public final boolean isDir(String path) {
@LuaFunction
public final boolean isReadOnly(String path) {
try {
return fileSystem.isReadOnly(path);
return getFileSystem().isReadOnly(path);
} catch (FileSystemException e) {
return false;
}
@ -239,7 +246,7 @@ public final boolean isReadOnly(String path) {
public final void makeDir(String path) throws LuaException {
try {
environment.observe(Metrics.FS_OPS);
fileSystem.makeDir(path);
getFileSystem().makeDir(path);
} catch (FileSystemException e) {
throw new LuaException(e.getMessage());
}
@ -258,7 +265,7 @@ public final void makeDir(String path) throws LuaException {
public final void move(String path, String dest) throws LuaException {
try {
environment.observe(Metrics.FS_OPS);
fileSystem.move(path, dest);
getFileSystem().move(path, dest);
} catch (FileSystemException e) {
throw new LuaException(e.getMessage());
}
@ -277,7 +284,7 @@ public final void move(String path, String dest) throws LuaException {
public final void copy(String path, String dest) throws LuaException {
try {
environment.observe(Metrics.FS_OPS);
fileSystem.copy(path, dest);
getFileSystem().copy(path, dest);
} catch (FileSystemException e) {
throw new LuaException(e.getMessage());
}
@ -296,7 +303,7 @@ public final void copy(String path, String dest) throws LuaException {
public final void delete(String path) throws LuaException {
try {
environment.observe(Metrics.FS_OPS);
fileSystem.delete(path);
getFileSystem().delete(path);
} catch (FileSystemException e) {
throw new LuaException(e.getMessage());
}
@ -363,32 +370,32 @@ public final Object[] open(String path, String mode) throws LuaException {
switch (mode) {
case "r" -> {
// Open the file for reading, then create a wrapper around the reader
var reader = fileSystem.openForRead(path, EncodedReadableHandle::openUtf8);
var reader = getFileSystem().openForRead(path, EncodedReadableHandle::openUtf8);
return new Object[]{ new EncodedReadableHandle(reader.get(), reader) };
}
case "w" -> {
// Open the file for writing, then create a wrapper around the writer
var writer = fileSystem.openForWrite(path, false, EncodedWritableHandle::openUtf8);
var writer = getFileSystem().openForWrite(path, false, EncodedWritableHandle::openUtf8);
return new Object[]{ new EncodedWritableHandle(writer.get(), writer) };
}
case "a" -> {
// Open the file for appending, then create a wrapper around the writer
var writer = fileSystem.openForWrite(path, true, EncodedWritableHandle::openUtf8);
var writer = getFileSystem().openForWrite(path, true, EncodedWritableHandle::openUtf8);
return new Object[]{ new EncodedWritableHandle(writer.get(), writer) };
}
case "rb" -> {
// Open the file for binary reading, then create a wrapper around the reader
var reader = fileSystem.openForRead(path, Function.identity());
var reader = getFileSystem().openForRead(path, Function.identity());
return new Object[]{ BinaryReadableHandle.of(reader.get(), reader) };
}
case "wb" -> {
// Open the file for binary writing, then create a wrapper around the writer
var writer = fileSystem.openForWrite(path, false, Function.identity());
var writer = getFileSystem().openForWrite(path, false, Function.identity());
return new Object[]{ BinaryWritableHandle.of(writer.get(), writer) };
}
case "ab" -> {
// Open the file for binary appending, then create a wrapper around the reader
var writer = fileSystem.openForWrite(path, true, Function.identity());
var writer = getFileSystem().openForWrite(path, true, Function.identity());
return new Object[]{ BinaryWritableHandle.of(writer.get(), writer) };
}
default -> throw new LuaException("Unsupported mode");
@ -404,7 +411,7 @@ public final Object[] open(String path, String mode) throws LuaException {
* @param path The path to get the drive of.
* @return The name of the drive that the file is on; e.g. {@code hdd} for local files, or {@code rom} for ROM files.
* @throws LuaException If the path doesn't exist.
* @cc.treturn string The name of the drive that the file is on; e.g. {@code hdd} for local files, or {@code rom} for ROM files.
* @cc.treturn string|nil The name of the drive that the file is on; e.g. {@code hdd} for local files, or {@code rom} for ROM files.
* @cc.usage Print the drives of a couple of mounts:
*
* <pre>{@code
@ -412,10 +419,11 @@ public final Object[] open(String path, String mode) throws LuaException {
* print("/rom/: " .. fs.getDrive("rom"))
* }</pre>
*/
@Nullable
@LuaFunction
public final Object[] getDrive(String path) throws LuaException {
try {
return fileSystem.exists(path) ? new Object[]{ fileSystem.getMountLabel(path) } : null;
return getFileSystem().exists(path) ? new Object[]{ getFileSystem().getMountLabel(path) } : null;
} catch (FileSystemException e) {
throw new LuaException(e.getMessage());
}
@ -435,7 +443,7 @@ public final Object[] getDrive(String path) throws LuaException {
@LuaFunction
public final Object getFreeSpace(String path) throws LuaException {
try {
var freeSpace = fileSystem.getFreeSpace(path);
var freeSpace = getFileSystem().getFreeSpace(path);
return freeSpace >= 0 ? freeSpace : "unlimited";
} catch (FileSystemException e) {
throw new LuaException(e.getMessage());
@ -459,7 +467,7 @@ public final Object getFreeSpace(String path) throws LuaException {
public final String[] find(String path) throws LuaException {
try {
environment.observe(Metrics.FS_OPS);
return fileSystem.find(path);
return getFileSystem().find(path);
} catch (FileSystemException e) {
throw new LuaException(e.getMessage());
}
@ -476,10 +484,11 @@ public final String[] find(String path) throws LuaException {
* @cc.since 1.87.0
* @see #getFreeSpace To get the free space available on this drive.
*/
@Nullable
@LuaFunction
public final Object getCapacity(String path) throws LuaException {
try {
var capacity = fileSystem.getCapacity(path);
var capacity = getFileSystem().getCapacity(path);
return capacity.isPresent() ? capacity.getAsLong() : null;
} catch (FileSystemException e) {
throw new LuaException(e.getMessage());
@ -508,21 +517,21 @@ public final Object getCapacity(String path) throws LuaException {
@LuaFunction
public final Map<String, Object> attributes(String path) throws LuaException {
try {
var attributes = fileSystem.getAttributes(path);
var attributes = getFileSystem().getAttributes(path);
Map<String, Object> result = new HashMap<>();
result.put("modification", getFileTime(attributes.lastModifiedTime()));
result.put("modified", getFileTime(attributes.lastModifiedTime()));
result.put("created", getFileTime(attributes.creationTime()));
result.put("size", attributes.isDirectory() ? 0 : attributes.size());
result.put("isDir", attributes.isDirectory());
result.put("isReadOnly", fileSystem.isReadOnly(path));
result.put("isReadOnly", getFileSystem().isReadOnly(path));
return result;
} catch (FileSystemException e) {
throw new LuaException(e.getMessage());
}
}
private static long getFileTime(FileTime time) {
private static long getFileTime(@Nullable FileTime time) {
return time == null ? 0 : time.toMillis();
}
}

View File

@ -18,7 +18,6 @@
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import javax.annotation.Nonnull;
import java.util.Collections;
import java.util.Locale;
import java.util.Map;
@ -35,7 +34,7 @@
public class HTTPAPI implements ILuaAPI {
private final IAPIEnvironment apiEnvironment;
private final ResourceGroup<CheckUrl> checkUrls = new ResourceGroup<>(ResourceGroup.DEFAULT);
private final ResourceGroup<CheckUrl> checkUrls = new ResourceGroup<>(() -> ResourceGroup.DEFAULT_LIMIT);
private final ResourceGroup<HttpRequest> requests = new ResourceQueue<>(() -> CoreConfig.httpMaxRequests);
private final ResourceGroup<Websocket> websockets = new ResourceGroup<>(() -> CoreConfig.httpMaxWebsockets);
@ -155,8 +154,7 @@ public final Object[] websocket(String address, Optional<Map<?, ?>> headerTbl) t
}
}
@Nonnull
private HttpHeaders getHeaders(@Nonnull Map<?, ?> headerTable) throws LuaException {
private HttpHeaders getHeaders(Map<?, ?> headerTable) throws LuaException {
HttpHeaders headers = new DefaultHttpHeaders();
for (Map.Entry<?, ?> entry : headerTable.entrySet()) {
var value = entry.getValue();

View File

@ -14,7 +14,6 @@
import dan200.computercraft.core.metrics.Metric;
import dan200.computercraft.core.terminal.Terminal;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public interface IAPIEnvironment {
@ -27,16 +26,12 @@ interface IPeripheralChangeListener {
int getComputerID();
@Nonnull
ComputerEnvironment getComputerEnvironment();
@Nonnull
GlobalEnvironment getGlobalEnvironment();
@Nonnull
IWorkMonitor getMainThreadMonitor();
@Nonnull
Terminal getTerminal();
FileSystem getFileSystem();
@ -45,7 +40,7 @@ interface IPeripheralChangeListener {
void reboot();
void queueEvent(String event, Object... args);
void queueEvent(String event, @Nullable Object... args);
void setOutput(ComputerSide side, int output);
@ -73,7 +68,7 @@ interface IPeripheralChangeListener {
void cancelTimer(int id);
void observe(@Nonnull Metric.Event event, long change);
void observe(Metric.Event event, long change);
void observe(@Nonnull Metric.Counter counter);
void observe(Metric.Counter counter);
}

View File

@ -7,6 +7,7 @@
import dan200.computercraft.api.lua.LuaException;
import javax.annotation.Nullable;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
@ -117,6 +118,7 @@ private static int getField(Map<?, ?> table, String field, int def) throws LuaEx
return def;
}
@Nullable
private static Boolean getBoolField(Map<?, ?> table, String field) throws LuaException {
var value = table.get(field);
if (value instanceof Boolean || value == null) return (Boolean) value;

View File

@ -13,7 +13,7 @@
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZoneOffset;
@ -40,7 +40,7 @@ public class OSAPI implements ILuaAPI {
private record Alarm(double time, int day) implements Comparable<Alarm> {
@Override
public int compareTo(@Nonnull Alarm o) {
public int compareTo(Alarm o) {
var t = day * 24.0 + time;
var ot = day * 24.0 + time;
return Double.compare(t, ot);
@ -241,9 +241,10 @@ public final int getComputerID() {
* Returns the label of the computer, or {@code nil} if none is set.
*
* @return The label of the computer.
* @cc.treturn string The label of the computer.
* @cc.treturn string|nil The label of the computer.
* @cc.since 1.3
*/
@Nullable
@LuaFunction({ "getComputerLabel", "computerLabel" })
public final Object[] getComputerLabel() {
var label = apiEnvironment.getLabel();
@ -258,7 +259,7 @@ public final Object[] getComputerLabel() {
*/
@LuaFunction
public final void setComputerLabel(Optional<String> label) {
apiEnvironment.setLabel(StringUtil.normaliseLabel(label.orElse(null)));
apiEnvironment.setLabel(label.map(StringUtil::normaliseLabel).orElse(null));
}
/**

View File

@ -18,7 +18,6 @@
import dan200.computercraft.core.metrics.Metrics;
import dan200.computercraft.core.util.LuaUtil;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;
@ -99,20 +98,23 @@ public MethodResult call(ILuaContext context, String methodName, IArguments argu
}
// IComputerAccess implementation
@Nullable
@Override
public synchronized String mount(@Nonnull String desiredLoc, @Nonnull IMount mount, @Nonnull String driveName) {
public synchronized String mount(String desiredLoc, IMount mount, String driveName) {
if (!attached) throw new NotAttachedException();
return super.mount(desiredLoc, mount, driveName);
}
@Nullable
@Override
public synchronized String mountWritable(@Nonnull String desiredLoc, @Nonnull IWritableMount mount, @Nonnull String driveName) {
public synchronized String mountWritable(String desiredLoc, IWritableMount mount, String driveName) {
if (!attached) throw new NotAttachedException();
return super.mountWritable(desiredLoc, mount, driveName);
}
@Override
public synchronized void unmount(String location) {
public synchronized void unmount(@Nullable String location) {
if (!attached) throw new NotAttachedException();
super.unmount(location);
}
@ -124,19 +126,17 @@ public int getID() {
}
@Override
public void queueEvent(@Nonnull String event, Object... arguments) {
public void queueEvent(String event, @Nullable Object... arguments) {
if (!attached) throw new NotAttachedException();
super.queueEvent(event, arguments);
}
@Nonnull
@Override
public String getAttachmentName() {
if (!attached) throw new NotAttachedException();
return side;
}
@Nonnull
@Override
public Map<String, IPeripheral> getAvailablePeripherals() {
if (!attached) throw new NotAttachedException();
@ -153,7 +153,7 @@ public Map<String, IPeripheral> getAvailablePeripherals() {
@Nullable
@Override
public IPeripheral getAvailablePeripheral(@Nonnull String name) {
public IPeripheral getAvailablePeripheral(String name) {
if (!attached) throw new NotAttachedException();
for (var wrapper : peripherals) {
@ -164,7 +164,6 @@ public IPeripheral getAvailablePeripheral(@Nonnull String name) {
return null;
}
@Nonnull
@Override
public IWorkMonitor getMainThreadMonitor() {
if (!attached) throw new NotAttachedException();
@ -185,7 +184,7 @@ public PeripheralAPI(IAPIEnvironment environment) {
// IPeripheralChangeListener
@Override
public void onPeripheralChanged(ComputerSide side, IPeripheral newPeripheral) {
public void onPeripheralChanged(ComputerSide side, @Nullable IPeripheral newPeripheral) {
synchronized (peripherals) {
var index = side.ordinal();
if (peripherals[index] != null) {
@ -253,6 +252,7 @@ public final boolean isPresent(String sideName) {
return false;
}
@Nullable
@LuaFunction
public final Object[] getType(String sideName) {
var side = ComputerSide.valueOfInsensitive(sideName);
@ -264,6 +264,7 @@ public final Object[] getType(String sideName) {
}
}
@Nullable
@LuaFunction
public final Object[] hasType(String sideName, String type) {
var side = ComputerSide.valueOfInsensitive(sideName);
@ -278,6 +279,7 @@ public final Object[] hasType(String sideName, String type) {
return null;
}
@Nullable
@LuaFunction
public final Object[] getMethods(String sideName) {
var side = ComputerSide.valueOfInsensitive(sideName);

View File

@ -10,6 +10,8 @@
import dan200.computercraft.api.lua.LuaFunction;
import dan200.computercraft.core.computer.ComputerSide;
import java.util.List;
/**
* Get and set redstone signals adjacent to this computer.
* <p>
@ -72,7 +74,7 @@ public String[] getNames() {
* @cc.since 1.2
*/
@LuaFunction
public final String[] getSides() {
public final List<String> getSides() {
return ComputerSide.NAMES;
}

View File

@ -8,7 +8,6 @@
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.lua.LuaValues;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Map;
@ -22,17 +21,15 @@ private TableHelper() {
throw new IllegalStateException("Cannot instantiate singleton " + getClass().getName());
}
@Nonnull
public static LuaException badKey(@Nonnull String key, @Nonnull String expected, @Nullable Object actual) {
public static LuaException badKey(String key, String expected, @Nullable Object actual) {
return badKey(key, expected, LuaValues.getType(actual));
}
@Nonnull
public static LuaException badKey(@Nonnull String key, @Nonnull String expected, @Nonnull String actual) {
public static LuaException badKey(String key, String expected, String actual) {
return new LuaException("bad field '" + key + "' (" + expected + " expected, got " + actual + ")");
}
public static double getNumberField(@Nonnull Map<?, ?> table, @Nonnull String key) throws LuaException {
public static double getNumberField(Map<?, ?> table, String key) throws LuaException {
var value = table.get(key);
if (value instanceof Number) {
return ((Number) value).doubleValue();
@ -41,7 +38,7 @@ public static double getNumberField(@Nonnull Map<?, ?> table, @Nonnull String ke
}
}
public static int getIntField(@Nonnull Map<?, ?> table, @Nonnull String key) throws LuaException {
public static int getIntField(Map<?, ?> table, String key) throws LuaException {
var value = table.get(key);
if (value instanceof Number) {
return (int) ((Number) value).longValue();
@ -50,11 +47,11 @@ public static int getIntField(@Nonnull Map<?, ?> table, @Nonnull String key) thr
}
}
public static double getRealField(@Nonnull Map<?, ?> table, @Nonnull String key) throws LuaException {
public static double getRealField(Map<?, ?> table, String key) throws LuaException {
return checkReal(key, getNumberField(table, key));
}
public static boolean getBooleanField(@Nonnull Map<?, ?> table, @Nonnull String key) throws LuaException {
public static boolean getBooleanField(Map<?, ?> table, String key) throws LuaException {
var value = table.get(key);
if (value instanceof Boolean) {
return (Boolean) value;
@ -63,8 +60,7 @@ public static boolean getBooleanField(@Nonnull Map<?, ?> table, @Nonnull String
}
}
@Nonnull
public static String getStringField(@Nonnull Map<?, ?> table, @Nonnull String key) throws LuaException {
public static String getStringField(Map<?, ?> table, String key) throws LuaException {
var value = table.get(key);
if (value instanceof String) {
return (String) value;
@ -74,8 +70,7 @@ public static String getStringField(@Nonnull Map<?, ?> table, @Nonnull String ke
}
@SuppressWarnings("unchecked")
@Nonnull
public static Map<Object, Object> getTableField(@Nonnull Map<?, ?> table, @Nonnull String key) throws LuaException {
public static Map<Object, Object> getTableField(Map<?, ?> table, String key) throws LuaException {
var value = table.get(key);
if (value instanceof Map) {
return (Map<Object, Object>) value;
@ -84,7 +79,7 @@ public static Map<Object, Object> getTableField(@Nonnull Map<?, ?> table, @Nonnu
}
}
public static double optNumberField(@Nonnull Map<?, ?> table, @Nonnull String key, double def) throws LuaException {
public static double optNumberField(Map<?, ?> table, String key, double def) throws LuaException {
var value = table.get(key);
if (value == null) {
return def;
@ -95,7 +90,7 @@ public static double optNumberField(@Nonnull Map<?, ?> table, @Nonnull String ke
}
}
public static int optIntField(@Nonnull Map<?, ?> table, @Nonnull String key, int def) throws LuaException {
public static int optIntField(Map<?, ?> table, String key, int def) throws LuaException {
var value = table.get(key);
if (value == null) {
return def;
@ -106,11 +101,11 @@ public static int optIntField(@Nonnull Map<?, ?> table, @Nonnull String key, int
}
}
public static double optRealField(@Nonnull Map<?, ?> table, @Nonnull String key, double def) throws LuaException {
public static double optRealField(Map<?, ?> table, String key, double def) throws LuaException {
return checkReal(key, optNumberField(table, key, def));
}
public static boolean optBooleanField(@Nonnull Map<?, ?> table, @Nonnull String key, boolean def) throws LuaException {
public static boolean optBooleanField(Map<?, ?> table, String key, boolean def) throws LuaException {
var value = table.get(key);
if (value == null) {
return def;
@ -121,7 +116,8 @@ public static boolean optBooleanField(@Nonnull Map<?, ?> table, @Nonnull String
}
}
public static String optStringField(@Nonnull Map<?, ?> table, @Nonnull String key, String def) throws LuaException {
@Nullable
public static String optStringField(Map<?, ?> table, String key, @Nullable String def) throws LuaException {
var value = table.get(key);
if (value == null) {
return def;
@ -133,7 +129,7 @@ public static String optStringField(@Nonnull Map<?, ?> table, @Nonnull String ke
}
@SuppressWarnings("unchecked")
public static Map<Object, Object> optTableField(@Nonnull Map<?, ?> table, @Nonnull String key, Map<Object, Object> def) throws LuaException {
public static Map<Object, Object> optTableField(Map<?, ?> table, String key, Map<Object, Object> def) throws LuaException {
var value = table.get(key);
if (value == null) {
return def;
@ -144,7 +140,7 @@ public static Map<Object, Object> optTableField(@Nonnull Map<?, ?> table, @Nonnu
}
}
private static double checkReal(@Nonnull String key, double value) throws LuaException {
private static double checkReal(String key, double value) throws LuaException {
if (!Double.isFinite(value)) throw badKey(key, "number", getNumericType(value));
return value;
}

View File

@ -12,7 +12,6 @@
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.core.util.Colour;
import javax.annotation.Nonnull;
/**
* Interact with a computer's terminal or monitors, writing text and drawing
@ -51,7 +50,6 @@ public final Object[] nativePaletteColour(int colour) throws LuaException {
return new Object[]{ c.getR(), c.getG(), c.getB() };
}
@Nonnull
@Override
public Terminal getTerminal() {
return terminal;

View File

@ -12,7 +12,6 @@
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.core.util.StringUtil;
import javax.annotation.Nonnull;
import java.nio.ByteBuffer;
/**
@ -30,7 +29,6 @@ private static int getHighestBit(int group) {
return bit;
}
@Nonnull
public abstract Terminal getTerminal() throws LuaException;
/**

View File

@ -8,7 +8,9 @@
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.lua.LuaFunction;
import dan200.computercraft.core.filesystem.TrackingCloseable;
import dan200.computercraft.core.util.Nullability;
import javax.annotation.Nullable;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
@ -28,10 +30,10 @@ public class BinaryReadableHandle extends HandleGeneric {
private static final int BUFFER_SIZE = 8192;
private final ReadableByteChannel reader;
final SeekableByteChannel seekable;
final @Nullable SeekableByteChannel seekable;
private final ByteBuffer single = ByteBuffer.allocate(1);
BinaryReadableHandle(ReadableByteChannel reader, SeekableByteChannel seekable, TrackingCloseable closeable) {
BinaryReadableHandle(ReadableByteChannel reader, @Nullable SeekableByteChannel seekable, TrackingCloseable closeable) {
super(closeable);
this.reader = reader;
this.seekable = seekable;
@ -59,6 +61,7 @@ public static BinaryReadableHandle of(ReadableByteChannel channel) {
* @cc.treturn [3] string The bytes read as a string. This is returned when the {@code count} is given.
* @cc.changed 1.80pr1 Now accepts an integer argument to read multiple bytes, returning a string instead of a number.
*/
@Nullable
@LuaFunction
public final Object[] read(Optional<Integer> countArg) throws LuaException {
checkOpen();
@ -130,6 +133,7 @@ public final Object[] read(Optional<Integer> countArg) throws LuaException {
* @cc.treturn string|nil The remaining contents of the file, or {@code nil} if we are at the end.
* @cc.since 1.80pr1
*/
@Nullable
@LuaFunction
public final Object[] readAll() throws LuaException {
checkOpen();
@ -164,6 +168,7 @@ public final Object[] readAll() throws LuaException {
* @cc.since 1.80pr1.9
* @cc.changed 1.81.0 `\r` is now stripped.
*/
@Nullable
@LuaFunction
public final Object[] readLine(Optional<Boolean> withTrailingArg) throws LuaException {
checkOpen();
@ -230,10 +235,11 @@ public static class Seekable extends BinaryReadableHandle {
* @cc.treturn string The reason seeking failed.
* @cc.since 1.80pr1.9
*/
@Nullable
@LuaFunction
public final Object[] seek(Optional<String> whence, Optional<Long> offset) throws LuaException {
checkOpen();
return handleSeek(seekable, whence, offset);
return handleSeek(Nullability.assertNonNull(seekable), whence, offset);
}
}
}

View File

@ -10,7 +10,9 @@
import dan200.computercraft.api.lua.LuaFunction;
import dan200.computercraft.api.lua.LuaValues;
import dan200.computercraft.core.filesystem.TrackingCloseable;
import dan200.computercraft.core.util.Nullability;
import javax.annotation.Nullable;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
@ -26,10 +28,10 @@
*/
public class BinaryWritableHandle extends HandleGeneric {
private final WritableByteChannel writer;
final SeekableByteChannel seekable;
final @Nullable SeekableByteChannel seekable;
private final ByteBuffer single = ByteBuffer.allocate(1);
protected BinaryWritableHandle(WritableByteChannel writer, SeekableByteChannel seekable, TrackingCloseable closeable) {
protected BinaryWritableHandle(WritableByteChannel writer, @Nullable SeekableByteChannel seekable, TrackingCloseable closeable) {
super(closeable);
this.writer = writer;
this.seekable = seekable;
@ -86,7 +88,8 @@ public final void flush() throws LuaException {
try {
// Technically this is not needed
if (writer instanceof FileChannel channel) channel.force(false);
} catch (IOException ignored) {
} catch (IOException e) {
throw new LuaException(e.getMessage());
}
}
@ -114,10 +117,11 @@ public Seekable(SeekableByteChannel seekable, TrackingCloseable closeable) {
* @cc.treturn string The reason seeking failed.
* @cc.since 1.80pr1.9
*/
@Nullable
@LuaFunction
public final Object[] seek(Optional<String> whence, Optional<Long> offset) throws LuaException {
checkOpen();
return handleSeek(seekable, whence, offset);
return handleSeek(Nullability.assertNonNull(seekable), whence, offset);
}
}
}

View File

@ -9,7 +9,7 @@
import dan200.computercraft.api.lua.LuaFunction;
import dan200.computercraft.core.filesystem.TrackingCloseable;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.channels.Channels;
@ -30,12 +30,12 @@ public class EncodedReadableHandle extends HandleGeneric {
private final BufferedReader reader;
public EncodedReadableHandle(@Nonnull BufferedReader reader, @Nonnull TrackingCloseable closable) {
public EncodedReadableHandle(BufferedReader reader, TrackingCloseable closable) {
super(closable);
this.reader = reader;
}
public EncodedReadableHandle(@Nonnull BufferedReader reader) {
public EncodedReadableHandle(BufferedReader reader) {
this(reader, new TrackingCloseable.Impl(reader));
}
@ -48,6 +48,7 @@ public EncodedReadableHandle(@Nonnull BufferedReader reader) {
* @cc.treturn string|nil The read line or {@code nil} if at the end of the file.
* @cc.changed 1.81.0 Added option to return trailing newline.
*/
@Nullable
@LuaFunction
public final Object[] readLine(Optional<Boolean> withTrailingArg) throws LuaException {
checkOpen();
@ -73,6 +74,7 @@ public final Object[] readLine(Optional<Boolean> withTrailingArg) throws LuaExce
* @throws LuaException If the file has been closed.
* @cc.treturn nil|string The remaining contents of the file, or {@code nil} if we are at the end.
*/
@Nullable
@LuaFunction
public final Object[] readAll() throws LuaException {
checkOpen();
@ -102,6 +104,7 @@ public final Object[] readAll() throws LuaException {
* @cc.treturn string|nil The read characters, or {@code nil} if at the of the file.
* @cc.since 1.80pr1.4
*/
@Nullable
@LuaFunction
public final Object[] read(Optional<Integer> countA) throws LuaException {
checkOpen();

View File

@ -11,7 +11,6 @@
import dan200.computercraft.core.filesystem.TrackingCloseable;
import dan200.computercraft.core.util.StringUtil;
import javax.annotation.Nonnull;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.channels.Channels;
@ -28,7 +27,7 @@
public class EncodedWritableHandle extends HandleGeneric {
private final BufferedWriter writer;
public EncodedWritableHandle(@Nonnull BufferedWriter writer, @Nonnull TrackingCloseable closable) {
public EncodedWritableHandle(BufferedWriter writer, TrackingCloseable closable) {
super(closable);
this.writer = writer;
}
@ -80,7 +79,8 @@ public final void flush() throws LuaException {
checkOpen();
try {
writer.flush();
} catch (IOException ignored) {
} catch (IOException e) {
throw new LuaException(e.getMessage());
}
}

View File

@ -10,16 +10,16 @@
import dan200.computercraft.core.filesystem.TrackingCloseable;
import dan200.computercraft.core.util.IoUtil;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.nio.channels.Channel;
import java.nio.channels.SeekableByteChannel;
import java.util.Optional;
public abstract class HandleGeneric {
private TrackingCloseable closeable;
private @Nullable TrackingCloseable closeable;
protected HandleGeneric(@Nonnull TrackingCloseable closeable) {
protected HandleGeneric(TrackingCloseable closeable) {
this.closeable = closeable;
}
@ -57,6 +57,7 @@ public final void doClose() throws LuaException {
* @throws LuaException If the arguments were invalid
* @see <a href="https://www.lua.org/manual/5.1/manual.html#pdf-file:seek">{@code file:seek} in the Lua manual.</a>
*/
@Nullable
protected static Object[] handleSeek(SeekableByteChannel channel, Optional<String> whence, Optional<Long> offset) throws LuaException {
long actualOffset = offset.orElse(0L);
try {
@ -75,6 +76,7 @@ protected static Object[] handleSeek(SeekableByteChannel channel, Optional<Strin
}
}
@Nullable
protected static SeekableByteChannel asSeekable(Channel channel) {
if (!(channel instanceof SeekableByteChannel seekable)) return null;

View File

@ -7,6 +7,7 @@
import dan200.computercraft.core.apis.IAPIEnvironment;
import javax.annotation.Nullable;
import java.net.URI;
import java.util.concurrent.Future;
@ -18,7 +19,7 @@
public class CheckUrl extends Resource<CheckUrl> {
private static final String EVENT = "http_check";
private Future<?> future;
private @Nullable Future<?> future;
private final IAPIEnvironment environment;
private final String address;
@ -47,7 +48,7 @@ private void doRun() {
if (tryClose()) environment.queueEvent(EVENT, address, true);
} catch (HTTPRequestException e) {
if (tryClose()) environment.queueEvent(EVENT, address, false, e.getMessage());
if (tryClose()) environment.queueEvent(EVENT, address, false, NetworkUtils.toFriendlyError(e));
}
}

View File

@ -25,7 +25,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.TrustManagerFactory;
@ -65,10 +65,11 @@ private NetworkUtils() {
}
private static final Object sslLock = new Object();
private static TrustManagerFactory trustManager;
private static SslContext sslContext;
private static @Nullable TrustManagerFactory trustManager;
private static @Nullable SslContext sslContext;
private static boolean triedSslContext = false;
@Nullable
private static TrustManagerFactory getTrustManager() {
if (trustManager != null) return trustManager;
synchronized (sslLock) {
@ -86,6 +87,7 @@ private static TrustManagerFactory getTrustManager() {
}
}
@Nullable
public static SslContext getSslContext() throws HTTPRequestException {
if (sslContext != null || triedSslContext) return sslContext;
synchronized (sslLock) {
@ -171,10 +173,10 @@ public static byte[] toBytes(ByteBuf buffer) {
return bytes;
}
@Nonnull
public static String toFriendlyError(@Nonnull Throwable cause) {
public static String toFriendlyError(Throwable cause) {
if (cause instanceof WebSocketHandshakeException || cause instanceof HTTPRequestException) {
return cause.getMessage();
var message = cause.getMessage();
return message == null ? "Could not connect" : message;
} else if (cause instanceof TooLongFrameException) {
return "Message is too large";
} else if (cause instanceof ReadTimeoutException || cause instanceof ConnectTimeoutException) {

View File

@ -8,6 +8,7 @@
import dan200.computercraft.core.util.IoUtil;
import io.netty.channel.ChannelFuture;
import javax.annotation.Nullable;
import java.io.Closeable;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
@ -94,12 +95,14 @@ public final boolean queue(Consumer<T> task) {
return limiter.queue(thisT, () -> task.accept(thisT));
}
protected static <T extends Closeable> T closeCloseable(T closeable) {
@Nullable
protected static <T extends Closeable> T closeCloseable(@Nullable T closeable) {
IoUtil.closeQuietly(closeable);
return null;
}
protected static ChannelFuture closeChannel(ChannelFuture future) {
@Nullable
protected static ChannelFuture closeChannel(@Nullable ChannelFuture future) {
if (future != null) {
future.cancel(false);
var channel = future.channel();
@ -109,7 +112,8 @@ protected static ChannelFuture closeChannel(ChannelFuture future) {
return null;
}
protected static <T extends Future<?>> T closeFuture(T future) {
@Nullable
protected static <T extends Future<?>> T closeFuture(@Nullable T future) {
if (future != null) future.cancel(true);
return null;
}

View File

@ -18,9 +18,6 @@
*/
public class ResourceGroup<T extends Resource<T>> {
public static final int DEFAULT_LIMIT = 512;
public static final IntSupplier DEFAULT = () -> DEFAULT_LIMIT;
private static final IntSupplier ZERO = () -> 0;
final IntSupplier limit;
@ -32,10 +29,6 @@ public ResourceGroup(IntSupplier limit) {
this.limit = limit;
}
public ResourceGroup() {
limit = ZERO;
}
public void startup() {
active = true;
}

View File

@ -21,9 +21,6 @@ public ResourceQueue(IntSupplier limit) {
super(limit);
}
public ResourceQueue() {
}
@Override
public synchronized void shutdown() {
super.shutdown();

View File

@ -5,7 +5,6 @@
*/
package dan200.computercraft.core.apis.http.options;
import javax.annotation.Nonnull;
import java.util.OptionalInt;
import java.util.OptionalLong;
@ -17,7 +16,6 @@ public enum Action {
this, OptionalLong.empty(), OptionalLong.empty(), OptionalInt.empty(), OptionalInt.empty()
);
@Nonnull
public PartialOptions toPartial() {
return partial;
}

View File

@ -9,6 +9,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.regex.Pattern;
@ -51,6 +52,7 @@ public boolean matches(InetAddress address) {
return true;
}
@Nullable
public static HostRange parse(String addressStr, String prefixSizeStr) {
int prefixSize;
try {
@ -79,11 +81,11 @@ public static HostRange parse(String addressStr, String prefixSizeStr) {
var size = prefixSize;
for (var i = 0; i < minBytes.length; i++) {
if (size <= 0) {
minBytes[i] &= 0;
maxBytes[i] |= 0xFF;
minBytes[i] = (byte) 0;
maxBytes[i] = (byte) 0xFF;
} else if (size < 8) {
minBytes[i] &= 0xFF << (8 - size);
maxBytes[i] |= ~(0xFF << (8 - size));
minBytes[i] = (byte) (minBytes[i] & 0xFF << (8 - size));
maxBytes[i] = (byte) (maxBytes[i] | ~(0xFF << (8 - size)));
}
size -= 8;

View File

@ -10,7 +10,6 @@
import dan200.computercraft.core.apis.http.options.AddressPredicate.HostRange;
import dan200.computercraft.core.apis.http.options.AddressPredicate.PrivatePattern;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.net.Inet4Address;
import java.net.Inet6Address;
@ -32,14 +31,14 @@ public final class AddressRule {
private final OptionalInt port;
private final PartialOptions partial;
private AddressRule(@Nonnull AddressPredicate predicate, OptionalInt port, @Nonnull PartialOptions partial) {
private AddressRule(AddressPredicate predicate, OptionalInt port, PartialOptions partial) {
this.predicate = predicate;
this.partial = partial;
this.port = port;
}
@Nullable
public static AddressRule parse(String filter, OptionalInt port, @Nonnull PartialOptions partial) {
public static AddressRule parse(String filter, OptionalInt port, PartialOptions partial) {
var cidr = filter.indexOf('/');
if (cidr >= 0) {
var addressStr = filter.substring(0, cidr);
@ -63,7 +62,7 @@ public static AddressRule parse(String filter, OptionalInt port, @Nonnull Partia
* @param ipv4Address An ipv4 version of the address, if the original was an ipv6 address.
* @return Whether it matches any of these patterns.
*/
private boolean matches(String domain, int port, InetAddress address, Inet4Address ipv4Address) {
private boolean matches(String domain, int port, InetAddress address, @Nullable Inet4Address ipv4Address) {
if (this.port.isPresent() && this.port.getAsInt() != port) return false;
return predicate.matches(domain)
|| predicate.matches(address)

View File

@ -5,20 +5,18 @@
*/
package dan200.computercraft.core.apis.http.options;
import javax.annotation.Nonnull;
/**
* Options about a specific domain.
*/
public final class Options {
@Nonnull
public final Action action;
public final long maxUpload;
public final long maxDownload;
public final int timeout;
public final int websocketMessage;
Options(@Nonnull Action action, long maxUpload, long maxDownload, int timeout, int websocketMessage) {
Options(Action action, long maxUpload, long maxDownload, int timeout, int websocketMessage) {
this.action = action;
this.maxUpload = maxUpload;
this.maxDownload = maxDownload;

View File

@ -5,10 +5,13 @@
*/
package dan200.computercraft.core.apis.http.options;
import com.google.errorprone.annotations.Immutable;
import javax.annotation.Nullable;
import java.util.OptionalInt;
import java.util.OptionalLong;
@Immutable
public final class PartialOptions {
public static final PartialOptions DEFAULT = new PartialOptions(
null, OptionalLong.empty(), OptionalLong.empty(), OptionalInt.empty(), OptionalInt.empty()
@ -20,6 +23,7 @@ public final class PartialOptions {
private final OptionalInt timeout;
private final OptionalInt websocketMessage;
@SuppressWarnings("Immutable") // Lazily initialised, so this mutation is invisible in the public API
private @Nullable Options options;
public PartialOptions(@Nullable Action action, OptionalLong maxUpload, OptionalLong maxDownload, OptionalInt timeout, OptionalInt websocketMessage) {

View File

@ -24,6 +24,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
@ -42,9 +43,9 @@ public class HttpRequest extends Resource<HttpRequest> {
private static final int MAX_REDIRECTS = 16;
private Future<?> executorFuture;
private ChannelFuture connectFuture;
private HttpRequestHandler currentRequest;
private @Nullable Future<?> executorFuture;
private @Nullable ChannelFuture connectFuture;
private @Nullable HttpRequestHandler currentRequest;
private final IAPIEnvironment environment;
@ -55,7 +56,10 @@ public class HttpRequest extends Resource<HttpRequest> {
final AtomicInteger redirects;
public HttpRequest(ResourceGroup<HttpRequest> limiter, IAPIEnvironment environment, String address, String postText, HttpHeaders headers, boolean binary, boolean followRedirects) {
public HttpRequest(
ResourceGroup<HttpRequest> limiter, IAPIEnvironment environment, String address, @Nullable String postText,
HttpHeaders headers, boolean binary, boolean followRedirects
) {
super(limiter);
this.environment = environment;
this.address = address;
@ -171,7 +175,7 @@ protected void initChannel(SocketChannel ch) {
// Do an additional check for cancellation
checkClosed();
} catch (HTTPRequestException e) {
failure(e.getMessage());
failure(NetworkUtils.toFriendlyError(e));
} catch (Exception e) {
failure(NetworkUtils.toFriendlyError(e));
LOG.error(Logging.HTTP_ERROR, "Error in HTTP request", e);

View File

@ -20,6 +20,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.io.Closeable;
import java.net.URI;
import java.net.URISyntaxException;
@ -27,6 +28,7 @@
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import static dan200.computercraft.core.apis.http.request.HttpRequest.getHeaderSize;
@ -47,10 +49,10 @@ public final class HttpRequestHandler extends SimpleChannelInboundHandler<HttpOb
private final HttpMethod method;
private final Options options;
private Charset responseCharset;
private @Nullable Charset responseCharset;
private final HttpHeaders responseHeaders = new DefaultHttpHeaders();
private HttpResponseStatus responseStatus;
private CompositeByteBuf responseBody;
private @Nullable HttpResponseStatus responseStatus;
private @Nullable CompositeByteBuf responseBody;
HttpRequestHandler(HttpRequest request, URI uri, HttpMethod method, Options options) {
this.request = request;
@ -112,7 +114,7 @@ public void channelRead0(ChannelHandlerContext ctx, HttpObject message) {
HttpRequest.checkUri(redirect);
} catch (HTTPRequestException e) {
// If we cannot visit this uri, then fail.
request.failure(e.getMessage());
request.failure(NetworkUtils.toFriendlyError(e));
return;
}
@ -167,6 +169,9 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
}
private void sendResponse() {
Objects.requireNonNull(responseStatus, "Status has not been set");
Objects.requireNonNull(responseCharset, "Charset has not been set");
// Read the ByteBuf into a channel.
var body = responseBody;
var bytes = body == null ? EMPTY_BYTES : NetworkUtils.toBytes(body);
@ -203,6 +208,7 @@ private void sendResponse() {
* @param headers The headers of the HTTP response.
* @return The URI to redirect to, or {@code null} if no redirect should occur.
*/
@Nullable
private URI getRedirect(HttpResponseStatus status, HttpHeaders headers) {
var code = status.code();
if (code < 300 || code > 307 || code == 304 || code == 306) return null;

View File

@ -13,7 +13,6 @@
import dan200.computercraft.core.apis.handles.HandleGeneric;
import dan200.computercraft.core.asm.ObjectSource;
import javax.annotation.Nonnull;
import java.util.Collections;
import java.util.Map;
@ -31,7 +30,7 @@ public class HttpResponseHandle implements ObjectSource {
private final String responseStatus;
private final Map<String, String> responseHeaders;
public HttpResponseHandle(@Nonnull HandleGeneric reader, int responseCode, String responseStatus, @Nonnull Map<String, String> responseHeaders) {
public HttpResponseHandle(HandleGeneric reader, int responseCode, String responseStatus, Map<String, String> responseHeaders) {
this.reader = reader;
this.responseCode = responseCode;
this.responseStatus = responseStatus;

View File

@ -29,6 +29,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.lang.ref.WeakReference;
import java.net.URI;
import java.net.URISyntaxException;
@ -51,9 +52,9 @@ public class Websocket extends Resource<Websocket> {
static final String CLOSE_EVENT = "websocket_closed";
static final String MESSAGE_EVENT = "websocket_message";
private Future<?> executorFuture;
private ChannelFuture connectFuture;
private WeakReference<WebsocketHandle> websocketHandle;
private @Nullable Future<?> executorFuture;
private @Nullable ChannelFuture connectFuture;
private @Nullable WeakReference<WebsocketHandle> websocketHandle;
private final IAPIEnvironment environment;
private final URI uri;
@ -73,12 +74,14 @@ public static URI checkUri(String address) throws HTTPRequestException {
try {
uri = new URI(address);
} catch (URISyntaxException ignored) {
// Fall through to the case below
}
if (uri == null || uri.getHost() == null) {
try {
uri = new URI("ws://" + address);
} catch (URISyntaxException ignored) {
// Fall through to the case below
}
}
@ -152,7 +155,7 @@ protected void initChannel(SocketChannel ch) {
// Do an additional check for cancellation
checkClosed();
} catch (HTTPRequestException e) {
failure(e.getMessage());
failure(NetworkUtils.toFriendlyError(e));
} catch (Exception e) {
failure(NetworkUtils.toFriendlyError(e));
LOG.error(Logging.HTTP_ERROR, "Error in websocket", e);

View File

@ -15,7 +15,7 @@
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.Closeable;
import java.util.Arrays;
import java.util.Optional;
@ -36,7 +36,7 @@ public class WebsocketHandle implements Closeable {
private final Options options;
private boolean closed = false;
private Channel channel;
private @Nullable Channel channel;
public WebsocketHandle(Websocket websocket, Options options, Channel channel) {
this.websocket = websocket;
@ -127,7 +127,6 @@ private final class ReceiveCallback implements ILuaCallback {
this.timeoutId = timeoutId;
}
@Nonnull
@Override
public MethodResult resume(Object[] event) {
if (event.length >= 3 && Objects.equal(event[0], MESSAGE_EVENT) && Objects.equal(event[1], websocket.address())) {

View File

@ -21,7 +21,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@ -77,8 +76,7 @@ public final class Generator<T> {
this.methodDesc = methodDesc.toString();
}
@Nonnull
public List<NamedMethod<T>> getMethods(@Nonnull Class<?> klass) {
public List<NamedMethod<T>> getMethods(Class<?> klass) {
try {
return classCache.get(klass);
} catch (ExecutionException e) {
@ -87,7 +85,6 @@ public List<NamedMethod<T>> getMethods(@Nonnull Class<?> klass) {
}
}
@Nonnull
private List<NamedMethod<T>> build(Class<?> klass) {
ArrayList<NamedMethod<T>> methods = null;
for (var method : klass.getMethods()) {
@ -121,7 +118,7 @@ private List<NamedMethod<T>> build(Class<?> klass) {
return Collections.unmodifiableList(methods);
}
private void addMethod(List<NamedMethod<T>> methods, Method method, LuaFunction annotation, PeripheralType genericType, T instance) {
private void addMethod(List<NamedMethod<T>> methods, Method method, LuaFunction annotation, @Nullable PeripheralType genericType, T instance) {
var names = annotation.value();
var isSimple = method.getReturnType() != MethodResult.class && !annotation.mainThread();
if (names.length == 0) {
@ -133,7 +130,6 @@ private void addMethod(List<NamedMethod<T>> methods, Method method, LuaFunction
}
}
@Nonnull
private Optional<T> build(Method method) {
var name = method.getDeclaringClass().getName() + "." + method.getName();
var modifiers = method.getModifiers();
@ -259,6 +255,7 @@ private byte[] generate(String className, Class<?> target, Method method, boolea
return cw.toByteArray();
}
@Nullable
private Boolean loadArg(MethodVisitor mw, Class<?> target, Method method, boolean unsafe, java.lang.reflect.Type genericArg, int argIndex) {
if (genericArg == target) {
mw.visitVarInsn(ALOAD, 1);

View File

@ -12,7 +12,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
@ -30,12 +30,12 @@ public class GenericMethod {
final Method method;
final LuaFunction annotation;
final Class<?> target;
final PeripheralType peripheralType;
final @Nullable PeripheralType peripheralType;
private static final List<GenericSource> sources = new ArrayList<>();
private static List<GenericMethod> cache;
private static @Nullable List<GenericMethod> cache;
GenericMethod(Method method, LuaFunction annotation, Class<?> target, PeripheralType peripheralType) {
GenericMethod(Method method, LuaFunction annotation, Class<?> target, @Nullable PeripheralType peripheralType) {
this.method = method;
this.annotation = annotation;
this.target = target;
@ -52,7 +52,7 @@ static List<GenericMethod> all() {
return cache = sources.stream().flatMap(GenericMethod::getMethods).toList();
}
public static synchronized void register(@Nonnull GenericSource source) {
public static synchronized void register(GenericSource source) {
Objects.requireNonNull(source, "Source cannot be null");
if (cache != null) {

View File

@ -7,7 +7,6 @@
import dan200.computercraft.api.lua.*;
import javax.annotation.Nonnull;
import java.util.Collections;
public interface LuaMethod {
@ -21,6 +20,5 @@ public interface LuaMethod {
String[] EMPTY_METHODS = new String[0];
@Nonnull
MethodResult apply(@Nonnull Object target, @Nonnull ILuaContext context, @Nonnull IArguments args) throws LuaException;
MethodResult apply(Object target, ILuaContext context, IArguments args) throws LuaException;
}

View File

@ -7,7 +7,6 @@
import dan200.computercraft.api.peripheral.PeripheralType;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public final class NamedMethod<T> {
@ -15,21 +14,19 @@ public final class NamedMethod<T> {
private final T method;
private final boolean nonYielding;
private final PeripheralType genericType;
private final @Nullable PeripheralType genericType;
NamedMethod(String name, T method, boolean nonYielding, PeripheralType genericType) {
NamedMethod(String name, T method, boolean nonYielding, @Nullable PeripheralType genericType) {
this.name = name;
this.method = method;
this.nonYielding = nonYielding;
this.genericType = genericType;
}
@Nonnull
public String getName() {
return name;
}
@Nonnull
public T getMethod() {
return method;
}

View File

@ -12,7 +12,6 @@
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IDynamicPeripheral;
import javax.annotation.Nonnull;
import java.util.Arrays;
public interface PeripheralMethod {
@ -24,6 +23,5 @@ public interface PeripheralMethod {
method -> (instance, context, computer, args) -> ((IDynamicPeripheral) instance).callMethod(computer, context, method, args)
);
@Nonnull
MethodResult apply(@Nonnull Object target, @Nonnull ILuaContext context, @Nonnull IComputerAccess computer, @Nonnull IArguments args) throws LuaException;
MethodResult apply(Object target, ILuaContext context, IComputerAccess computer, IArguments args) throws LuaException;
}

View File

@ -7,10 +7,13 @@
import dan200.computercraft.api.lua.MethodResult;
import javax.annotation.Nullable;
final class ResultHelpers {
private ResultHelpers() {
}
@Nullable
static Object[] checkNormalResult(MethodResult result) {
if (result.getCallback() != null) {
// Due to how tasks are implemented, we can't currently return a MethodResult. This is an

View File

@ -16,6 +16,7 @@
import dan200.computercraft.core.filesystem.FileSystem;
import dan200.computercraft.core.terminal.Terminal;
import javax.annotation.Nullable;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
@ -37,7 +38,7 @@ public class Computer {
// Various properties of the computer
private final int id;
private String label = null;
private @Nullable String label = null;
// Read-only fields about the computer
private final GlobalEnvironment globalEnvironment;
@ -112,7 +113,7 @@ public void unload() {
executor.queueStop(false, true);
}
public void queueEvent(String event, Object[] args) {
public void queueEvent(String event, @Nullable Object[] args) {
executor.queueEvent(event, args);
}
@ -134,11 +135,12 @@ public int getID() {
return id;
}
@Nullable
public String getLabel() {
return label;
}
public void setLabel(String label) {
public void setLabel(@Nullable String label) {
if (!Objects.equal(label, this.label)) {
this.label = label;
externalOutputChanged.set(true);

View File

@ -19,10 +19,10 @@
import dan200.computercraft.core.metrics.MetricsObserver;
import dan200.computercraft.core.util.Colour;
import dan200.computercraft.core.util.IoUtil;
import dan200.computercraft.core.util.Nullability;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.InputStream;
import java.util.ArrayDeque;
@ -63,9 +63,9 @@ final class ComputerExecutor {
private final ComputerThread scheduler;
final TimeoutState timeout;
private FileSystem fileSystem;
private @Nullable FileSystem fileSystem;
private ILuaMachine machine;
private @Nullable ILuaMachine machine;
/**
* Whether the computer is currently on. This is set to false when a shutdown starts, or when turning on completes
@ -126,7 +126,7 @@ final class ComputerExecutor {
* Note, if command is not {@code null}, then some command is scheduled to be executed. Otherwise it is not
* currently in the queue (or is currently being executed).
*/
private volatile StateCommand command;
private volatile @Nullable StateCommand command;
/**
* The queue of events which should be executed when this computer is on.
@ -150,7 +150,7 @@ final class ComputerExecutor {
*/
private boolean closed;
private IWritableMount rootMount;
private @Nullable IWritableMount rootMount;
/**
* The thread the executor is running on. This is non-null when performing work. We use this to ensure we're only
@ -193,6 +193,8 @@ boolean isOn() {
}
FileSystem getFileSystem() {
var fileSystem = this.fileSystem;
if (fileSystem == null) throw new IllegalStateException("FileSystem has not been created yet");
return fileSystem;
}
@ -276,7 +278,7 @@ private void immediateFail(StateCommand command) {
* @param event The event's name
* @param args The event's arguments
*/
void queueEvent(@Nonnull String event, @Nullable Object[] args) {
void queueEvent(String event, @Nullable Object[] args) {
// Events should be skipped if we're not on.
if (!isOn) return;
@ -320,19 +322,28 @@ void tick() {
}
}
@Nullable
private IMount getRomMount() {
return computer.getGlobalEnvironment().createResourceMount("computercraft", "lua/rom");
}
@Nullable
private IWritableMount getRootMount() {
if (rootMount == null) rootMount = computerEnvironment.createRootMount();
return rootMount;
}
@Nullable
private FileSystem createFileSystem() {
FileSystem filesystem = null;
try {
filesystem = new FileSystem("hdd", getRootMount());
var mount = getRootMount();
if (mount == null) {
displayFailure("Cannot mount computer mount", null);
return null;
}
filesystem = new FileSystem("hdd", mount);
var romMount = getRomMount();
if (romMount == null) {
@ -351,12 +362,14 @@ private FileSystem createFileSystem() {
}
}
@Nullable
private ILuaMachine createLuaMachine() {
// Load the bios resource
InputStream biosStream = null;
try {
biosStream = computer.getGlobalEnvironment().createResourceFile("computercraft", "lua/bios.lua");
} catch (Exception ignored) {
} catch (Exception e) {
LOG.error("Failed to load BIOS", e);
}
if (biosStream == null) {
@ -562,7 +575,7 @@ void printState(StringBuilder out) {
if (machine != null) machine.printExecutionState(out);
}
private void displayFailure(String message, String extra) {
private void displayFailure(String message, @Nullable String extra) {
var terminal = computer.getTerminal();
terminal.reset();
@ -583,8 +596,8 @@ private void displayFailure(String message, String extra) {
terminal.write("ComputerCraft may be installed incorrectly");
}
private void resumeMachine(String event, Object[] args) throws InterruptedException {
var result = machine.handleEvent(event, args);
private void resumeMachine(@Nullable String event, @Nullable Object[] args) throws InterruptedException {
var result = Nullability.assertNonNull(machine).handleEvent(event, args);
interruptedEvent = result.isPause();
if (!result.isError()) return;
@ -600,6 +613,6 @@ private enum StateCommand {
ERROR,
}
private record Event(String name, Object[] args) {
private record Event(String name, @Nullable Object[] args) {
}
}

View File

@ -5,8 +5,8 @@
*/
package dan200.computercraft.core.computer;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
/**
* A side on a computer. This is relative to the direction the computer is facing.
@ -19,7 +19,7 @@ public enum ComputerSide {
RIGHT("right"),
LEFT("left");
public static final String[] NAMES = new String[]{ "bottom", "top", "back", "front", "right", "left" };
public static final List<String> NAMES = List.of("bottom", "top", "back", "front", "right", "left");
public static final int COUNT = 6;
@ -31,13 +31,12 @@ public enum ComputerSide {
this.name = name;
}
@Nonnull
public static ComputerSide valueOf(int side) {
return VALUES[side];
}
@Nullable
public static ComputerSide valueOfInsensitive(@Nonnull String name) {
public static ComputerSide valueOfInsensitive(String name) {
for (var side : VALUES) {
if (side.name.equalsIgnoreCase(name)) return side;
}

View File

@ -13,7 +13,6 @@
import dan200.computercraft.core.apis.ComputerAccess;
import dan200.computercraft.core.apis.IAPIEnvironment;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Collections;
import java.util.Map;
@ -33,7 +32,6 @@ public class ComputerSystem extends ComputerAccess implements IComputerSystem {
this.environment = environment;
}
@Nonnull
@Override
public String getAttachmentName() {
return "computer";
@ -52,7 +50,6 @@ public String getLabel() {
return environment.getLabel();
}
@Nonnull
@Override
public Map<String, IPeripheral> getAvailablePeripherals() {
// TODO: Should this return peripherals on the current computer?
@ -61,7 +58,7 @@ public Map<String, IPeripheral> getAvailablePeripherals() {
@Nullable
@Override
public IPeripheral getAvailablePeripheral(@Nonnull String name) {
public IPeripheral getAvailablePeripheral(String name) {
return null;
}
}

View File

@ -13,7 +13,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Objects;
import java.util.TreeSet;
@ -53,6 +52,7 @@
* @see TimeoutState For how hard timeouts are handled.
* @see ComputerExecutor For how computers actually do execution.
*/
@SuppressWarnings("GuardedBy") // FIXME: Hard to know what the correct thing to do is.
public final class ComputerThread {
private static final Logger LOG = LoggerFactory.getLogger(ComputerThread.class);
private static final ThreadFactory monitorFactory = ThreadUtils.factory("Computer-Monitor");
@ -537,7 +537,7 @@ private final class Worker implements Runnable {
/**
* The thread this runner runs on.
*/
final @Nonnull Thread owner;
final Thread owner;
/**
* Whether this runner is currently executing. This may be set to false when this worker terminates, or when

View File

@ -16,7 +16,7 @@
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.Iterator;
@ -56,7 +56,7 @@ public final class Environment implements IAPIEnvironment {
private final int[] bundledInput = new int[ComputerSide.COUNT];
private final IPeripheral[] peripherals = new IPeripheral[ComputerSide.COUNT];
private IPeripheralChangeListener peripheralListener = null;
private @Nullable IPeripheralChangeListener peripheralListener = null;
private final Int2ObjectMap<Timer> timers = new Int2ObjectOpenHashMap<>();
private int nextTimerToken = 0;
@ -72,25 +72,21 @@ public int getComputerID() {
return computer.getID();
}
@Nonnull
@Override
public ComputerEnvironment getComputerEnvironment() {
return environment;
}
@Nonnull
@Override
public GlobalEnvironment getGlobalEnvironment() {
return computer.getGlobalEnvironment();
}
@Nonnull
@Override
public IWorkMonitor getMainThreadMonitor() {
return computer.getMainThreadMonitor();
}
@Nonnull
@Override
public Terminal getTerminal() {
return computer.getTerminal();
@ -112,7 +108,7 @@ public void reboot() {
}
@Override
public void queueEvent(String event, Object... args) {
public void queueEvent(String event, @Nullable Object... args) {
computer.queueEvent(event, args);
}
@ -262,6 +258,7 @@ void resetOutput() {
}
}
@Nullable
@Override
public IPeripheral getPeripheral(ComputerSide side) {
synchronized (peripherals) {
@ -269,7 +266,7 @@ public IPeripheral getPeripheral(ComputerSide side) {
}
}
public void setPeripheral(ComputerSide side, IPeripheral peripheral) {
public void setPeripheral(ComputerSide side, @Nullable IPeripheral peripheral) {
synchronized (peripherals) {
var index = side.ordinal();
var existing = peripherals[index];
@ -283,19 +280,20 @@ public void setPeripheral(ComputerSide side, IPeripheral peripheral) {
}
@Override
public void setPeripheralChangeListener(IPeripheralChangeListener listener) {
public void setPeripheralChangeListener(@Nullable IPeripheralChangeListener listener) {
synchronized (peripherals) {
peripheralListener = listener;
}
}
@Nullable
@Override
public String getLabel() {
return computer.getLabel();
}
@Override
public void setLabel(String label) {
public void setLabel(@Nullable String label) {
computer.setLabel(label);
}
@ -315,12 +313,12 @@ public void cancelTimer(int id) {
}
@Override
public void observe(@Nonnull Metric.Event event, long change) {
public void observe(Metric.Event event, long change) {
metrics.observe(event, change);
}
@Override
public void observe(@Nonnull Metric.Counter counter) {
public void observe(Metric.Counter counter) {
metrics.observe(counter);
}

View File

@ -12,7 +12,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
class LuaContext implements ILuaContext {
private static final Logger LOG = LoggerFactory.getLogger(LuaContext.class);
@ -23,7 +22,7 @@ class LuaContext implements ILuaContext {
}
@Override
public long issueMainThreadTask(@Nonnull final ILuaTask task) throws LuaException {
public long issueMainThreadTask(final ILuaTask task) throws LuaException {
// Issue command
final var taskID = computer.getUniqueTaskId();
final Runnable iTask = () -> {

View File

@ -7,7 +7,6 @@
import dan200.computercraft.core.metrics.MetricsObserver;
import javax.annotation.Nonnull;
import java.util.concurrent.TimeUnit;
/**
@ -38,7 +37,7 @@ public boolean shouldWork() {
}
@Override
public void trackWork(long time, @Nonnull TimeUnit unit) {
public void trackWork(long time, TimeUnit unit) {
}
}
}

View File

@ -1,117 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.filesystem;
import dan200.computercraft.api.filesystem.FileOperationException;
import dan200.computercraft.api.filesystem.IMount;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.nio.channels.ReadableByteChannel;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class ComboMount implements IMount {
private final IMount[] parts;
public ComboMount(IMount[] parts) {
this.parts = parts;
}
// IMount implementation
@Override
public boolean exists(@Nonnull String path) throws IOException {
for (var i = parts.length - 1; i >= 0; --i) {
var part = parts[i];
if (part.exists(path)) {
return true;
}
}
return false;
}
@Override
public boolean isDirectory(@Nonnull String path) throws IOException {
for (var i = parts.length - 1; i >= 0; --i) {
var part = parts[i];
if (part.isDirectory(path)) {
return true;
}
}
return false;
}
@Override
public void list(@Nonnull String path, @Nonnull List<String> contents) throws IOException {
// Combine the lists from all the mounts
List<String> foundFiles = null;
var foundDirs = 0;
for (var i = parts.length - 1; i >= 0; --i) {
var part = parts[i];
if (part.exists(path) && part.isDirectory(path)) {
if (foundFiles == null) {
foundFiles = new ArrayList<>();
}
part.list(path, foundFiles);
foundDirs++;
}
}
if (foundDirs == 1) {
// We found one directory, so we know it already doesn't contain duplicates
contents.addAll(foundFiles);
} else if (foundDirs > 1) {
// We found multiple directories, so filter for duplicates
Set<String> seen = new HashSet<>();
for (var file : foundFiles) {
if (seen.add(file)) {
contents.add(file);
}
}
} else {
throw new FileOperationException(path, "Not a directory");
}
}
@Override
public long getSize(@Nonnull String path) throws IOException {
for (var i = parts.length - 1; i >= 0; --i) {
var part = parts[i];
if (part.exists(path)) {
return part.getSize(path);
}
}
throw new FileOperationException(path, "No such file");
}
@Nonnull
@Override
public ReadableByteChannel openForRead(@Nonnull String path) throws IOException {
for (var i = parts.length - 1; i >= 0; --i) {
var part = parts[i];
if (part.exists(path) && !part.isDirectory(path)) {
return part.openForRead(path);
}
}
throw new FileOperationException(path, "No such file");
}
@Nonnull
@Override
public BasicFileAttributes getAttributes(@Nonnull String path) throws IOException {
for (var i = parts.length - 1; i >= 0; --i) {
var part = parts[i];
if (part.exists(path) && !part.isDirectory(path)) {
return part.getAttributes(path);
}
}
throw new FileOperationException(path, "No such file");
}
}

View File

@ -1,41 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.filesystem;
import dan200.computercraft.api.filesystem.FileOperationException;
import dan200.computercraft.api.filesystem.IMount;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.nio.channels.ReadableByteChannel;
import java.util.List;
public class EmptyMount implements IMount {
@Override
public boolean exists(@Nonnull String path) {
return path.isEmpty();
}
@Override
public boolean isDirectory(@Nonnull String path) {
return path.isEmpty();
}
@Override
public void list(@Nonnull String path, @Nonnull List<String> contents) {
}
@Override
public long getSize(@Nonnull String path) {
return 0;
}
@Nonnull
@Override
public ReadableByteChannel openForRead(@Nonnull String path) throws IOException {
throw new FileOperationException(path, "No such file");
}
}

View File

@ -11,7 +11,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
@ -25,7 +24,7 @@
public class FileMount implements IWritableMount {
private static final Logger LOG = LoggerFactory.getLogger(FileMount.class);
private static final int MINIMUM_FILE_SIZE = 500;
private static final long MINIMUM_FILE_SIZE = 500;
private static final Set<OpenOption> READ_OPTIONS = Collections.singleton(StandardOpenOption.READ);
private static final Set<OpenOption> WRITE_OPTIONS = Sets.newHashSet(StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
private static final Set<OpenOption> APPEND_OPTIONS = Sets.newHashSet(StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.APPEND);
@ -41,7 +40,7 @@ private class WritableCountingChannel implements WritableByteChannel {
}
@Override
public int write(@Nonnull ByteBuffer b) throws IOException {
public int write(ByteBuffer b) throws IOException {
count(b.remaining());
return inner.write(b);
}
@ -129,7 +128,7 @@ public FileMount(File rootPath, long capacity) {
// IMount implementation
@Override
public boolean exists(@Nonnull String path) {
public boolean exists(String path) {
if (!created()) return path.isEmpty();
var file = getRealPath(path);
@ -137,7 +136,7 @@ public boolean exists(@Nonnull String path) {
}
@Override
public boolean isDirectory(@Nonnull String path) {
public boolean isDirectory(String path) {
if (!created()) return path.isEmpty();
var file = getRealPath(path);
@ -145,7 +144,7 @@ public boolean isDirectory(@Nonnull String path) {
}
@Override
public void list(@Nonnull String path, @Nonnull List<String> contents) throws IOException {
public void list(String path, List<String> contents) throws IOException {
if (!created()) {
if (!path.isEmpty()) throw new FileOperationException(path, "Not a directory");
return;
@ -161,7 +160,7 @@ public void list(@Nonnull String path, @Nonnull List<String> contents) throws IO
}
@Override
public long getSize(@Nonnull String path) throws IOException {
public long getSize(String path) throws IOException {
if (!created()) {
if (path.isEmpty()) return 0;
} else {
@ -172,9 +171,8 @@ public long getSize(@Nonnull String path) throws IOException {
throw new FileOperationException(path, "No such file");
}
@Nonnull
@Override
public ReadableByteChannel openForRead(@Nonnull String path) throws IOException {
public ReadableByteChannel openForRead(String path) throws IOException {
if (created()) {
var file = getRealPath(path);
if (file.exists() && !file.isDirectory()) return FileChannel.open(file.toPath(), READ_OPTIONS);
@ -183,9 +181,8 @@ public ReadableByteChannel openForRead(@Nonnull String path) throws IOException
throw new FileOperationException(path, "No such file");
}
@Nonnull
@Override
public BasicFileAttributes getAttributes(@Nonnull String path) throws IOException {
public BasicFileAttributes getAttributes(String path) throws IOException {
if (created()) {
var file = getRealPath(path);
if (file.exists()) return Files.readAttributes(file.toPath(), BasicFileAttributes.class);
@ -197,7 +194,7 @@ public BasicFileAttributes getAttributes(@Nonnull String path) throws IOExceptio
// IWritableMount implementation
@Override
public void makeDirectory(@Nonnull String path) throws IOException {
public void makeDirectory(String path) throws IOException {
create();
var file = getRealPath(path);
if (file.exists()) {
@ -224,7 +221,7 @@ public void makeDirectory(@Nonnull String path) throws IOException {
}
@Override
public void delete(@Nonnull String path) throws IOException {
public void delete(String path) throws IOException {
if (path.isEmpty()) throw new FileOperationException(path, "Access denied");
if (created()) {
@ -252,9 +249,8 @@ private void deleteRecursively(File file) throws IOException {
}
}
@Nonnull
@Override
public WritableByteChannel openForWrite(@Nonnull String path) throws IOException {
public WritableByteChannel openForWrite(String path) throws IOException {
create();
var file = getRealPath(path);
if (file.exists() && file.isDirectory()) throw new FileOperationException(path, "Cannot write to directory");
@ -269,9 +265,8 @@ public WritableByteChannel openForWrite(@Nonnull String path) throws IOException
return new SeekableCountingChannel(Files.newByteChannel(file.toPath(), WRITE_OPTIONS), MINIMUM_FILE_SIZE);
}
@Nonnull
@Override
public WritableByteChannel openForAppend(@Nonnull String path) throws IOException {
public WritableByteChannel openForAppend(String path) throws IOException {
if (!created()) {
throw new FileOperationException(path, "No such file");
}
@ -292,7 +287,6 @@ public long getRemainingSpace() {
return Math.max(capacity - usedSpace, 0);
}
@Nonnull
@Override
public OptionalLong getCapacity() {
return OptionalLong.of(capacity - MINIMUM_FILE_SIZE);

View File

@ -5,6 +5,7 @@
*/
package dan200.computercraft.core.filesystem;
import com.google.common.base.Splitter;
import com.google.common.io.ByteStreams;
import dan200.computercraft.api.filesystem.IFileSystem;
import dan200.computercraft.api.filesystem.IMount;
@ -12,7 +13,6 @@
import dan200.computercraft.core.CoreConfig;
import dan200.computercraft.core.util.IoUtil;
import javax.annotation.Nonnull;
import java.io.Closeable;
import java.io.IOException;
import java.lang.ref.Reference;
@ -60,16 +60,15 @@ public void close() {
}
public synchronized void mount(String label, String location, IMount mount) throws FileSystemException {
if (mount == null) throw new NullPointerException();
Objects.requireNonNull(mount, "mount cannot be null");
location = sanitizePath(location);
if (location.contains("..")) throw new FileSystemException("Cannot mount below the root");
mount(new MountWrapper(label, location, mount));
}
public synchronized void mountWritable(String label, String location, IWritableMount mount) throws FileSystemException {
if (mount == null) {
throw new NullPointerException();
}
Objects.requireNonNull(mount, "mount cannot be null");
location = sanitizePath(location);
if (location.contains("..")) {
throw new FileSystemException("Cannot mount below the root");
@ -313,7 +312,7 @@ private synchronized void copyRecursive(String sourcePath, MountWrapper sourceMo
} catch (AccessDeniedException e) {
throw new FileSystemException("Access denied");
} catch (IOException e) {
throw new FileSystemException(e.getMessage());
throw FileSystemException.of(e);
}
}
}
@ -327,7 +326,7 @@ private void cleanup() {
}
}
private synchronized <T extends Closeable> FileSystemWrapper<T> openFile(@Nonnull MountWrapper mount, @Nonnull Channel channel, @Nonnull T file) throws FileSystemException {
private synchronized <T extends Closeable> FileSystemWrapper<T> openFile(MountWrapper mount, Channel channel, T file) throws FileSystemException {
synchronized (openFiles) {
if (CoreConfig.maximumFilesOpen > 0 &&
openFiles.size() >= CoreConfig.maximumFilesOpen) {
@ -355,7 +354,7 @@ public synchronized <T extends Closeable> FileSystemWrapper<T> openForRead(Strin
path = sanitizePath(path);
var mount = getMount(path);
var channel = mount.openForRead(path);
return channel != null ? openFile(mount, channel, open.apply(channel)) : null;
return openFile(mount, channel, open.apply(channel));
}
public synchronized <T extends Closeable> FileSystemWrapper<T> openForWrite(String path, boolean append, Function<WritableByteChannel, T> open) throws FileSystemException {
@ -364,7 +363,7 @@ public synchronized <T extends Closeable> FileSystemWrapper<T> openForWrite(Stri
path = sanitizePath(path);
var mount = getMount(path);
var channel = append ? mount.openForAppend(path) : mount.openForWrite(path);
return channel != null ? openFile(mount, channel, open.apply(channel)) : null;
return openFile(mount, channel, open.apply(channel));
}
public synchronized long getFreeSpace(String path) throws FileSystemException {
@ -373,7 +372,6 @@ public synchronized long getFreeSpace(String path) throws FileSystemException {
return mount.getFreeSpace();
}
@Nonnull
public synchronized OptionalLong getCapacity(String path) throws FileSystemException {
path = sanitizePath(path);
var mount = getMount(path);
@ -430,9 +428,8 @@ public static String sanitizePath(String path, boolean allowWildcards) {
path = cleanName.toString();
// Collapse the string into its component parts, removing ..'s
var parts = path.split("/");
var outputParts = new Stack<String>();
for (var part : parts) {
var outputParts = new ArrayDeque<String>();
for (var part : Splitter.on('/').split(path)) {
if (part.isEmpty() || part.equals(".") || threeDotsPattern.matcher(part).matches()) {
// . is redundant
// ... and more are treated as .
@ -441,37 +438,26 @@ public static String sanitizePath(String path, boolean allowWildcards) {
if (part.equals("..")) {
// .. can cancel out the last folder entered
if (!outputParts.empty()) {
var top = outputParts.peek();
if (!outputParts.isEmpty()) {
var top = outputParts.peekLast();
if (!top.equals("..")) {
outputParts.pop();
outputParts.removeLast();
} else {
outputParts.push("..");
outputParts.addLast("..");
}
} else {
outputParts.push("..");
outputParts.addLast("..");
}
} else if (part.length() >= 255) {
// If part length > 255 and it is the last part
outputParts.push(part.substring(0, 255));
outputParts.addLast(part.substring(0, 255));
} else {
// Anything else we add to the stack
outputParts.push(part);
outputParts.addLast(part);
}
}
// Recombine the output parts into a new string
var result = new StringBuilder();
var it = outputParts.iterator();
while (it.hasNext()) {
var part = it.next();
result.append(part);
if (it.hasNext()) {
result.append('/');
}
}
return result.toString();
return String.join("/", outputParts);
}
public static boolean contains(String pathA, String pathB) {

View File

@ -5,6 +5,7 @@
*/
package dan200.computercraft.core.filesystem;
import java.io.IOException;
import java.io.Serial;
public class FileSystemException extends Exception {
@ -14,4 +15,12 @@ public class FileSystemException extends Exception {
FileSystemException(String s) {
super(s);
}
public static FileSystemException of(IOException e) {
return new FileSystemException(getMessage(e));
}
public static String getMessage(IOException e) {
return e.getMessage() == null ? "Access denied" : e.getMessage();
}
}

View File

@ -7,7 +7,6 @@
import dan200.computercraft.core.util.IoUtil;
import javax.annotation.Nonnull;
import java.io.Closeable;
import java.io.IOException;
import java.lang.ref.ReferenceQueue;
@ -57,7 +56,6 @@ public boolean isOpen() {
return isOpen;
}
@Nonnull
public T get() {
return closeable.get();
}

View File

@ -7,7 +7,6 @@
import dan200.computercraft.api.filesystem.IFileSystem;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
@ -23,7 +22,7 @@ public FileSystemWrapperMount(FileSystem filesystem) {
}
@Override
public void makeDirectory(@Nonnull String path) throws IOException {
public void makeDirectory(String path) throws IOException {
try {
filesystem.makeDir(path);
} catch (FileSystemException e) {
@ -32,7 +31,7 @@ public void makeDirectory(@Nonnull String path) throws IOException {
}
@Override
public void delete(@Nonnull String path) throws IOException {
public void delete(String path) throws IOException {
try {
filesystem.delete(path);
} catch (FileSystemException e) {
@ -40,9 +39,8 @@ public void delete(@Nonnull String path) throws IOException {
}
}
@Nonnull
@Override
public ReadableByteChannel openForRead(@Nonnull String path) throws IOException {
public ReadableByteChannel openForRead(String path) throws IOException {
try {
// FIXME: Think of a better way of implementing this, so closing this will close on the computer.
return filesystem.openForRead(path, Function.identity()).get();
@ -51,9 +49,8 @@ public ReadableByteChannel openForRead(@Nonnull String path) throws IOException
}
}
@Nonnull
@Override
public WritableByteChannel openForWrite(@Nonnull String path) throws IOException {
public WritableByteChannel openForWrite(String path) throws IOException {
try {
return filesystem.openForWrite(path, false, Function.identity()).get();
} catch (FileSystemException e) {
@ -61,9 +58,8 @@ public WritableByteChannel openForWrite(@Nonnull String path) throws IOException
}
}
@Nonnull
@Override
public WritableByteChannel openForAppend(@Nonnull String path) throws IOException {
public WritableByteChannel openForAppend(String path) throws IOException {
try {
return filesystem.openForWrite(path, true, Function.identity()).get();
} catch (FileSystemException e) {
@ -81,7 +77,7 @@ public long getRemainingSpace() throws IOException {
}
@Override
public boolean exists(@Nonnull String path) throws IOException {
public boolean exists(String path) throws IOException {
try {
return filesystem.exists(path);
} catch (FileSystemException e) {
@ -90,7 +86,7 @@ public boolean exists(@Nonnull String path) throws IOException {
}
@Override
public boolean isDirectory(@Nonnull String path) throws IOException {
public boolean isDirectory(String path) throws IOException {
try {
return filesystem.isDir(path);
} catch (FileSystemException e) {
@ -99,7 +95,7 @@ public boolean isDirectory(@Nonnull String path) throws IOException {
}
@Override
public void list(@Nonnull String path, @Nonnull List<String> contents) throws IOException {
public void list(String path, List<String> contents) throws IOException {
try {
Collections.addAll(contents, filesystem.list(path));
} catch (FileSystemException e) {
@ -108,7 +104,7 @@ public void list(@Nonnull String path, @Nonnull List<String> contents) throws IO
}
@Override
public long getSize(@Nonnull String path) throws IOException {
public long getSize(String path) throws IOException {
try {
return filesystem.getSize(path);
} catch (FileSystemException e) {

View File

@ -8,12 +8,13 @@
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.io.ByteStreams;
import com.google.errorprone.annotations.concurrent.LazyInit;
import dan200.computercraft.api.filesystem.FileOperationException;
import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.core.apis.handles.ArrayByteChannel;
import dan200.computercraft.core.util.IoUtil;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
@ -100,6 +101,7 @@ public JarMount(File jarFile, String subPath) throws IOException {
}
}
@Nullable
private FileEntry get(String path) {
var lastEntry = root;
var lastIndex = 0;
@ -139,18 +141,18 @@ private void create(ZipEntry entry, String localPath) {
}
@Override
public boolean exists(@Nonnull String path) {
public boolean exists(String path) {
return get(path) != null;
}
@Override
public boolean isDirectory(@Nonnull String path) {
public boolean isDirectory(String path) {
var file = get(path);
return file != null && file.isDirectory();
}
@Override
public void list(@Nonnull String path, @Nonnull List<String> contents) throws IOException {
public void list(String path, List<String> contents) throws IOException {
var file = get(path);
if (file == null || !file.isDirectory()) throw new FileOperationException(path, "Not a directory");
@ -158,15 +160,14 @@ public void list(@Nonnull String path, @Nonnull List<String> contents) throws IO
}
@Override
public long getSize(@Nonnull String path) throws IOException {
public long getSize(String path) throws IOException {
var file = get(path);
if (file != null) return file.size;
throw new FileOperationException(path, "No such file");
}
@Nonnull
@Override
public ReadableByteChannel openForRead(@Nonnull String path) throws IOException {
public ReadableByteChannel openForRead(String path) throws IOException {
var file = get(path);
if (file != null && !file.isDirectory()) {
var contents = CONTENTS_CACHE.getIfPresent(file);
@ -191,9 +192,8 @@ public ReadableByteChannel openForRead(@Nonnull String path) throws IOException
throw new FileOperationException(path, "No such file");
}
@Nonnull
@Override
public BasicFileAttributes getAttributes(@Nonnull String path) throws IOException {
public BasicFileAttributes getAttributes(String path) throws IOException {
var file = get(path);
if (file != null) {
var entry = zip.getEntry(file.path);
@ -204,8 +204,12 @@ public BasicFileAttributes getAttributes(@Nonnull String path) throws IOExceptio
}
private static class FileEntry {
@LazyInit // TODO: Might be nicer to use @Initializer on setup(...)
String path;
long size;
@Nullable
Map<String, FileEntry> children;
void setup(ZipEntry entry) {
@ -285,6 +289,7 @@ public long size() {
return entry.getSize();
}
@Nullable
@Override
public Object fileKey() {
return null;
@ -292,7 +297,7 @@ public Object fileKey() {
private static final FileTime EPOCH = FileTime.from(Instant.EPOCH);
private static FileTime orEpoch(FileTime time) {
private static FileTime orEpoch(@Nullable FileTime time) {
return time == null ? EPOCH : time;
}
}

View File

@ -9,7 +9,6 @@
import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.api.filesystem.IWritableMount;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.nio.channels.ReadableByteChannel;
@ -24,7 +23,7 @@ class MountWrapper {
private final String location;
private final IMount mount;
private final IWritableMount writableMount;
private final @Nullable IWritableMount writableMount;
MountWrapper(String label, String location, IMount mount) {
this.label = label;
@ -107,7 +106,6 @@ public long getSize(String path) throws FileSystemException {
}
}
@Nonnull
public BasicFileAttributes getAttributes(String path) throws FileSystemException {
path = toLocal(path);
try {
@ -213,9 +211,9 @@ private String toLocal(String path) {
return FileSystem.toLocal(path, location);
}
private FileSystemException localExceptionOf(@Nullable String localPath, @Nonnull IOException e) {
private FileSystemException localExceptionOf(@Nullable String localPath, IOException e) {
if (!location.isEmpty() && e instanceof FileOperationException ex) {
if (ex.getFilename() != null) return localExceptionOf(ex.getFilename(), ex.getMessage());
if (ex.getFilename() != null) return localExceptionOf(ex.getFilename(), FileSystemException.getMessage(ex));
}
if (e instanceof java.nio.file.FileSystemException ex) {
@ -225,7 +223,7 @@ private FileSystemException localExceptionOf(@Nullable String localPath, @Nonnul
return localPath == null ? new FileSystemException(message) : localExceptionOf(localPath, message);
}
return new FileSystemException(e.getMessage());
return FileSystemException.of(e);
}
private FileSystemException localExceptionOf(String path, String message) {

View File

@ -7,7 +7,6 @@
import dan200.computercraft.api.filesystem.IMount;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.nio.channels.ReadableByteChannel;
import java.nio.file.attribute.BasicFileAttributes;
@ -23,34 +22,32 @@ public SubMount(IMount parent, String subPath) {
}
@Override
public boolean exists(@Nonnull String path) throws IOException {
public boolean exists(String path) throws IOException {
return parent.exists(getFullPath(path));
}
@Override
public boolean isDirectory(@Nonnull String path) throws IOException {
public boolean isDirectory(String path) throws IOException {
return parent.isDirectory(getFullPath(path));
}
@Override
public void list(@Nonnull String path, @Nonnull List<String> contents) throws IOException {
public void list(String path, List<String> contents) throws IOException {
parent.list(getFullPath(path), contents);
}
@Override
public long getSize(@Nonnull String path) throws IOException {
public long getSize(String path) throws IOException {
return parent.getSize(getFullPath(path));
}
@Nonnull
@Override
public ReadableByteChannel openForRead(@Nonnull String path) throws IOException {
public ReadableByteChannel openForRead(String path) throws IOException {
return parent.openForRead(getFullPath(path));
}
@Nonnull
@Override
public BasicFileAttributes getAttributes(@Nonnull String path) throws IOException {
public BasicFileAttributes getAttributes(String path) throws IOException {
return parent.getAttributes(getFullPath(path));
}

View File

@ -28,14 +28,14 @@ class BasicFunction extends VarArgFunction {
private final LuaMethod method;
private final Object instance;
private final ILuaContext context;
private final String name;
private final String funcName;
BasicFunction(CobaltLuaMachine machine, LuaMethod method, Object instance, ILuaContext context, String name) {
this.machine = machine;
this.method = method;
this.instance = instance;
this.context = context;
this.name = name;
funcName = name;
}
@Override
@ -47,7 +47,7 @@ public Varargs invoke(LuaState luaState, Varargs args) throws LuaError {
} catch (LuaException e) {
throw wrap(e);
} catch (Throwable t) {
LOG.error(Logging.JAVA_ERROR, "Error calling {} on {}", name, instance, t);
LOG.error(Logging.JAVA_ERROR, "Error calling {} on {}", funcName, instance, t);
throw new LuaError("Java Exception Thrown: " + t, 0);
} finally {
arguments.close();

View File

@ -27,7 +27,6 @@
import org.squiddev.cobalt.lib.*;
import org.squiddev.cobalt.lib.platform.VoidResourceManipulator;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.InputStream;
import java.io.Serial;
@ -58,11 +57,11 @@ public class CobaltLuaMachine implements ILuaMachine {
private final TimeoutDebugHandler debug;
private final ILuaContext context;
private LuaState state;
private LuaTable globals;
private @Nullable LuaState state;
private @Nullable LuaTable globals;
private LuaThread mainRoutine = null;
private String eventFilter = null;
private @Nullable LuaThread mainRoutine = null;
private @Nullable String eventFilter = null;
public CobaltLuaMachine(MachineEnvironment environment) {
timeout = environment.timeout();
@ -115,7 +114,9 @@ public CobaltLuaMachine(MachineEnvironment environment) {
}
@Override
public void addAPI(@Nonnull ILuaAPI api) {
public void addAPI(ILuaAPI api) {
if (globals == null) throw new IllegalStateException("Machine has been closed");
// Add the methods of an API to the global table
var table = wrapLuaObject(api);
if (table == null) {
@ -128,9 +129,9 @@ public void addAPI(@Nonnull ILuaAPI api) {
}
@Override
public MachineResult loadBios(@Nonnull InputStream bios) {
// Begin executing a file (ie, the bios)
if (mainRoutine != null) return MachineResult.OK;
public MachineResult loadBios(InputStream bios) {
if (mainRoutine != null) throw new IllegalStateException("Already set up the machine");
if (state == null || globals == null) throw new IllegalStateException("Machine has been destroyed.");
try {
var value = LoadState.load(state, bios, "@bios.lua", globals);
@ -147,8 +148,8 @@ public MachineResult loadBios(@Nonnull InputStream bios) {
}
@Override
public MachineResult handleEvent(String eventName, Object[] arguments) {
if (mainRoutine == null) return MachineResult.OK;
public MachineResult handleEvent(@Nullable String eventName, @Nullable Object[] arguments) {
if (mainRoutine == null || state == null) throw new IllegalStateException("Machine has been closed");
if (eventFilter != null && eventName != null && !eventName.equals(eventFilter) && !eventName.equals("terminate")) {
return MachineResult.OK;
@ -232,13 +233,13 @@ private LuaTable wrapLuaObject(Object object) {
try {
if (table.keyCount() == 0) return null;
} catch (LuaError ignored) {
// next should never throw on nil.
}
return table;
}
@Nonnull
private LuaValue toValue(@Nullable Object object, @Nullable Map<Object, LuaValue> values) {
private LuaValue toValue(@Nullable Object object, @Nullable IdentityHashMap<Object, LuaValue> values) {
if (object == null) return Constants.NIL;
if (object instanceof Number num) return valueOf(num.doubleValue());
if (object instanceof Boolean bool) return valueOf(bool);
@ -304,11 +305,11 @@ private LuaValue toValue(@Nullable Object object, @Nullable Map<Object, LuaValue
return Constants.NIL;
}
Varargs toValues(Object[] objects) {
Varargs toValues(@Nullable Object[] objects) {
if (objects == null || objects.length == 0) return Constants.NONE;
if (objects.length == 1) return toValue(objects[0], null);
Map<Object, LuaValue> result = new IdentityHashMap<>(0);
var result = new IdentityHashMap<Object, LuaValue>(0);
var values = new LuaValue[objects.length];
for (var i = 0; i < values.length; i++) {
var object = objects[i];
@ -317,7 +318,8 @@ Varargs toValues(Object[] objects) {
return varargsOf(values);
}
static Object toObject(LuaValue value, Map<LuaValue, Object> objects) {
@Nullable
static Object toObject(LuaValue value, @Nullable IdentityHashMap<LuaValue, Object> objects) {
switch (value.type()) {
case Constants.TNIL:
case Constants.TNONE:
@ -448,6 +450,7 @@ private static final class HardAbortError extends Error {
@Serial
private static final long serialVersionUID = 7954092008586367501L;
@SuppressWarnings("StaticAssignmentOfThrowable")
static final HardAbortError INSTANCE = new HardAbortError();
private HardAbortError() {

View File

@ -8,7 +8,6 @@
import dan200.computercraft.api.lua.IDynamicLuaObject;
import dan200.computercraft.api.lua.ILuaAPI;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.InputStream;
@ -32,7 +31,7 @@ public interface ILuaMachine {
*
* @param api The API to register.
*/
void addAPI(@Nonnull ILuaAPI api);
void addAPI(ILuaAPI api);
/**
* Create a function from the provided program, and set it up to run when {@link #handleEvent(String, Object[])} is
@ -43,7 +42,7 @@ public interface ILuaMachine {
* @param bios The stream containing the boot program.
* @return The result of loading this machine. Will either be OK, or the error message when loading the bios.
*/
MachineResult loadBios(@Nonnull InputStream bios);
MachineResult loadBios(InputStream bios);
/**
* Resume the machine, either starting or resuming the coroutine.

View File

@ -7,7 +7,6 @@
import dan200.computercraft.core.computer.TimeoutState;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.InputStream;
@ -42,19 +41,19 @@ public final class MachineResult {
private final boolean error;
private final boolean pause;
private final String message;
private final @Nullable String message;
private MachineResult(boolean error, boolean pause, String message) {
private MachineResult(boolean error, boolean pause, @Nullable String message) {
this.pause = pause;
this.message = message;
this.error = error;
}
public static MachineResult error(@Nonnull String error) {
public static MachineResult error(String error) {
return new MachineResult(true, false, error);
}
public static MachineResult error(@Nonnull Exception error) {
public static MachineResult error(Exception error) {
return new MachineResult(true, false, error.getMessage());
}

View File

@ -17,7 +17,6 @@
import org.squiddev.cobalt.debug.DebugFrame;
import org.squiddev.cobalt.function.ResumableVarArgFunction;
import javax.annotation.Nonnull;
/**
* Calls a {@link LuaMethod}, and interprets the resulting {@link MethodResult}, either returning the result or yielding
@ -26,7 +25,6 @@
class ResultInterpreterFunction extends ResumableVarArgFunction<ResultInterpreterFunction.Container> {
private static final Logger LOG = LoggerFactory.getLogger(ResultInterpreterFunction.class);
@Nonnull
static class Container {
ILuaCallback callback;
final int errorAdjust;
@ -41,14 +39,14 @@ static class Container {
private final LuaMethod method;
private final Object instance;
private final ILuaContext context;
private final String name;
private final String funcName;
ResultInterpreterFunction(CobaltLuaMachine machine, LuaMethod method, Object instance, ILuaContext context, String name) {
this.machine = machine;
this.method = method;
this.instance = instance;
this.context = context;
this.name = name;
funcName = name;
}
@Override
@ -60,7 +58,7 @@ protected Varargs invoke(LuaState state, DebugFrame debugFrame, Varargs args) th
} catch (LuaException e) {
throw wrap(e, 0);
} catch (Throwable t) {
LOG.error(Logging.JAVA_ERROR, "Error calling {} on {}", name, instance, t);
LOG.error(Logging.JAVA_ERROR, "Error calling {} on {}", funcName, instance, t);
throw new LuaError("Java Exception Thrown: " + t, 0);
} finally {
arguments.close();
@ -84,7 +82,7 @@ protected Varargs resumeThis(LuaState state, Container container, Varargs args)
} catch (LuaException e) {
throw wrap(e, container.errorAdjust);
} catch (Throwable t) {
LOG.error(Logging.JAVA_ERROR, "Error calling {} on {}", name, container.callback, t);
LOG.error(Logging.JAVA_ERROR, "Error calling {} on {}", funcName, container.callback, t);
throw new LuaError("Java Exception Thrown: " + t, 0);
}

View File

@ -9,7 +9,7 @@
import dan200.computercraft.api.lua.LuaValues;
import org.squiddev.cobalt.*;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;
import static dan200.computercraft.api.lua.LuaValues.badTableItem;
@ -18,7 +18,7 @@
class TableImpl implements dan200.computercraft.api.lua.LuaTable<Object, Object> {
private final VarargArguments arguments;
private final LuaTable table;
private Map<Object, Object> backingMap;
private @Nullable Map<Object, Object> backingMap;
TableImpl(VarargArguments arguments, LuaTable table) {
this.arguments = arguments;
@ -61,7 +61,6 @@ public boolean isEmpty() {
}
}
@Nonnull
private LuaValue getImpl(Object o) {
checkValid();
if (o instanceof String) return table.rawget((String) o);
@ -74,12 +73,12 @@ public boolean containsKey(Object o) {
return !getImpl(o).isNil();
}
@Nullable
@Override
public Object get(Object o) {
return CobaltLuaMachine.toObject(getImpl(o), null);
}
@Nonnull
private Map<Object, Object> getBackingMap() {
checkValid();
if (backingMap != null) return backingMap;
@ -93,19 +92,16 @@ public boolean containsValue(Object o) {
return getBackingMap().containsKey(o);
}
@Nonnull
@Override
public Set<Object> keySet() {
return getBackingMap().keySet();
}
@Nonnull
@Override
public Collection<Object> values() {
return getBackingMap().values();
}
@Nonnull
@Override
public Set<Entry<Object, Object>> entrySet() {
return getBackingMap().entrySet();

View File

@ -10,7 +10,6 @@
import dan200.computercraft.api.lua.LuaValues;
import org.squiddev.cobalt.*;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.nio.ByteBuffer;
import java.util.Optional;
@ -20,7 +19,7 @@ final class VarargArguments implements IArguments {
boolean closed;
private final Varargs varargs;
private Object[] cache;
private @Nullable Object[] cache;
private VarargArguments(Varargs varargs) {
this.varargs = varargs;
@ -72,7 +71,6 @@ public long getLong(int index) throws LuaException {
return value instanceof LuaInteger ? value.toInteger() : (long) LuaValues.checkFinite(index, value.toDouble());
}
@Nonnull
@Override
public ByteBuffer getBytes(int index) throws LuaException {
var value = varargs.arg(index + 1);
@ -92,7 +90,6 @@ public Optional<ByteBuffer> optBytes(int index) throws LuaException {
return Optional.of(ByteBuffer.wrap(str.bytes, str.offset, str.length).asReadOnlyBuffer());
}
@Nonnull
@Override
public dan200.computercraft.api.lua.LuaTable<?, ?> getTableUnsafe(int index) throws LuaException {
if (closed) {
@ -104,7 +101,6 @@ public Optional<ByteBuffer> optBytes(int index) throws LuaException {
return new TableImpl(this, (LuaTable) value);
}
@Nonnull
@Override
public Optional<dan200.computercraft.api.lua.LuaTable<?, ?>> optTableUnsafe(int index) throws LuaException {
if (closed) {

View File

@ -0,0 +1,22 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
/**
* ComputerCraft's core Lua runtime and APIs.
* <p>
* This is not considered part of the stable API, and so should not be consumed by other Minecraft mods. However,
* emulators or other CC-tooling may find this useful.
*/
@DefaultQualifier(value = NonNull.class, locations = {
TypeUseLocation.RETURN,
TypeUseLocation.PARAMETER,
TypeUseLocation.FIELD,
})
package dan200.computercraft.core;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.framework.qual.DefaultQualifier;
import org.checkerframework.framework.qual.TypeUseLocation;

View File

@ -7,7 +7,6 @@
import dan200.computercraft.core.util.Colour;
import javax.annotation.Nonnull;
public class Palette {
public static final int PALETTE_SIZE = 16;
@ -46,7 +45,7 @@ public void setColour(int i, Colour colour) {
}
public double[] getColour(int i) {
return i >= 0 && i < PALETTE_SIZE ? colours[i] : null;
return colours[i];
}
/**
@ -58,7 +57,6 @@ public double[] getColour(int i) {
* @param i The colour index.
* @return The number as a tuple of bytes.
*/
@Nonnull
public byte[] getRenderColours(int i) {
return byteColours[i];
}

View File

@ -7,7 +7,6 @@
import dan200.computercraft.core.util.Colour;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.nio.ByteBuffer;
@ -36,7 +35,7 @@ public Terminal(int width, int height, boolean colour) {
this(width, height, colour, null);
}
public Terminal(int width, int height, boolean colour, Runnable changedCallback) {
public Terminal(int width, int height, boolean colour, @Nullable Runnable changedCallback) {
this.width = width;
this.height = height;
this.colour = colour;
@ -163,7 +162,6 @@ public int getBackgroundColour() {
return cursorBackgroundColour;
}
@Nonnull
public Palette getPalette() {
return palette;
}
@ -234,10 +232,7 @@ public synchronized void clearLine() {
}
public synchronized TextBuffer getLine(int y) {
if (y >= 0 && y < height) {
return text[y];
}
return null;
return text[y];
}
public synchronized void setLine(int y, String text, String textColour, String backgroundColour) {
@ -248,17 +243,11 @@ public synchronized void setLine(int y, String text, String textColour, String b
}
public synchronized TextBuffer getTextColourLine(int y) {
if (y >= 0 && y < height) {
return textColour[y];
}
return null;
return textColour[y];
}
public synchronized TextBuffer getBackgroundColourLine(int y) {
if (y >= 0 && y < height) {
return backgroundColour[y];
}
return null;
return backgroundColour[y];
}
public final void setChanged() {

View File

@ -5,6 +5,8 @@
*/
package dan200.computercraft.core.util;
import javax.annotation.Nullable;
public enum Colour {
BLACK(0x111111),
RED(0xcc4c4c),
@ -29,6 +31,7 @@ public static Colour fromInt(int colour) {
return Colour.VALUES[colour];
}
@Nullable
public static Colour fromHex(int colour) {
for (var entry : VALUES) {
if (entry.getHex() == colour) return entry;

View File

@ -17,6 +17,7 @@ public static void closeQuietly(@Nullable Closeable closeable) {
try {
if (closeable != null) closeable.close();
} catch (IOException ignored) {
// The whole point here is to suppress these exceptions!
}
}
}

View File

@ -12,8 +12,6 @@ private StringUtil() {
}
public static String normaliseLabel(String label) {
if (label == null) return null;
var length = Math.min(32, label.length());
var builder = new StringBuilder(length);
for (var i = 0; i < length; i++) {

View File

@ -23,7 +23,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;
@ -249,7 +248,6 @@ private static String formatName(String name) {
}
public static class FakeModem implements IPeripheral {
@Nonnull
@Override
public String getType() {
return "modem";
@ -267,7 +265,6 @@ public final boolean isOpen(int channel) {
}
public static class FakePeripheralHub implements IPeripheral {
@Nonnull
@Override
public String getType() {
return "peripheral_hub";

View File

@ -8,7 +8,6 @@
import dan200.computercraft.api.lua.*;
import dan200.computercraft.core.asm.LuaMethod;
import javax.annotation.Nonnull;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
@ -51,7 +50,7 @@ public <T> T callOf(Class<T> klass, String name, Object... args) throws LuaExcep
}
@Override
public long issueMainThreadTask(@Nonnull ILuaTask task) {
public long issueMainThreadTask(ILuaTask task) {
throw new IllegalStateException("Method should never queue events");
}
}

View File

@ -11,7 +11,6 @@
import org.hamcrest.Matcher;
import org.junit.jupiter.api.Test;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.util.Collection;
import java.util.Map;
@ -241,7 +240,7 @@ public static <T> Matcher<NamedMethod<T>> named(String method) {
private static final ILuaContext CONTEXT = new ILuaContext() {
@Override
public long issueMainThreadTask(@Nonnull ILuaTask task) {
public long issueMainThreadTask(ILuaTask task) {
return 0;
}
};

View File

@ -13,7 +13,6 @@
import dan200.computercraft.core.computer.ComputerSide;
import org.junit.jupiter.api.Test;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Collections;
@ -99,7 +98,6 @@ public final int go() {
return 123;
}
@Nonnull
@Override
public String getType() {
return "main_thread";
@ -112,21 +110,18 @@ public boolean equals(@Nullable IPeripheral other) {
}
public static class Dynamic implements IDynamicLuaObject, ILuaAPI, IDynamicPeripheral {
@Nonnull
@Override
public String[] getMethodNames() {
return new String[]{ "foo" };
}
@Nonnull
@Override
public MethodResult callMethod(@Nonnull ILuaContext context, int method, @Nonnull IArguments arguments) {
public MethodResult callMethod(ILuaContext context, int method, IArguments arguments) {
return MethodResult.of(123);
}
@Nonnull
@Override
public MethodResult callMethod(@Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull IArguments arguments) {
public MethodResult callMethod(IComputerAccess computer, ILuaContext context, int method, IArguments arguments) {
return callMethod(context, method, arguments);
}
@ -140,7 +135,6 @@ public String[] getNames() {
return new String[]{ "dynamic" };
}
@Nonnull
@Override
public String getType() {
return "dynamic";
@ -179,7 +173,6 @@ public final void mainThread() throws LuaException {
throw new LuaException("!");
}
@Nonnull
@Override
public String getType() {
return "throw";
@ -192,7 +185,6 @@ public boolean equals(@Nullable IPeripheral other) {
}
public static class ManyMethods implements IDynamicLuaObject, ILuaAPI {
@Nonnull
@Override
public String[] getMethodNames() {
var methods = new String[40];
@ -200,9 +192,8 @@ public String[] getMethodNames() {
return methods;
}
@Nonnull
@Override
public MethodResult callMethod(@Nonnull ILuaContext context, int method, @Nonnull IArguments arguments) throws LuaException {
public MethodResult callMethod(ILuaContext context, int method, IArguments arguments) throws LuaException {
return MethodResult.of();
}

View File

@ -34,20 +34,6 @@ void testSetAndGetLine() {
assertEquals("fedcba9876543210", terminal.getBackgroundColourLine(1).toString());
}
@Test
void testGetLineOutOfBounds() {
var terminal = new Terminal(16, 9, true);
assertNull(terminal.getLine(-5));
assertNull(terminal.getLine(12));
assertNull(terminal.getTextColourLine(-5));
assertNull(terminal.getTextColourLine(12));
assertNull(terminal.getBackgroundColourLine(-5));
assertNull(terminal.getBackgroundColourLine(12));
}
@Test
void testDefaults() {
var terminal = new Terminal(16, 9, true);

View File

@ -8,7 +8,6 @@
import net.jqwik.api.*;
import net.jqwik.api.arbitraries.SizableArbitrary;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.math.BigInteger;
import java.nio.ByteBuffer;
@ -43,25 +42,21 @@ public static ArbitraryByteBuffer bytes() {
return DEFAULT;
}
@Nonnull
@Override
public SizableArbitrary<ByteBuffer> ofMinSize(int minSize) {
return new ArbitraryByteBuffer(minSize, maxSize, distribution);
}
@Nonnull
@Override
public SizableArbitrary<ByteBuffer> ofMaxSize(int maxSize) {
return new ArbitraryByteBuffer(minSize, maxSize, distribution);
}
@Nonnull
@Override
public SizableArbitrary<ByteBuffer> withSizeDistribution(@Nonnull RandomDistribution distribution) {
public SizableArbitrary<ByteBuffer> withSizeDistribution(RandomDistribution distribution) {
return new ArbitraryByteBuffer(minSize, maxSize, distribution);
}
@Nonnull
@Override
public RandomGenerator<ByteBuffer> generator(int genSize) {
var min = BigInteger.valueOf(minSize);
@ -78,7 +73,6 @@ public RandomGenerator<ByteBuffer> generator(int genSize) {
};
}
@Nonnull
@Override
public EdgeCases<ByteBuffer> edgeCases(int maxEdgeCases) {
return EdgeCases.fromSuppliers(Arrays.asList(
@ -133,13 +127,11 @@ private ShrinkableBuffer(ByteBuffer value, int minSize) {
this.minSize = minSize;
}
@Nonnull
@Override
public ByteBuffer value() {
return value;
}
@Nonnull
@Override
public Stream<Shrinkable<ByteBuffer>> shrink() {
return StreamSupport.stream(new Spliterators.AbstractSpliterator<Shrinkable<ByteBuffer>>(3, 0) {
@ -160,7 +152,6 @@ public boolean tryAdvance(Consumer<? super Shrinkable<ByteBuffer>> action) {
}, false);
}
@Nonnull
@Override
public ShrinkingDistance distance() {
return ShrinkingDistance.of(value.remaining() - minSize);

View File

@ -16,7 +16,6 @@
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.test.core.computer.BasicEnvironment;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public abstract class BasicApiEnvironment implements IAPIEnvironment {
@ -32,25 +31,21 @@ public int getComputerID() {
return 0;
}
@Nonnull
@Override
public ComputerEnvironment getComputerEnvironment() {
return environment;
}
@Nonnull
@Override
public GlobalEnvironment getGlobalEnvironment() {
return environment;
}
@Nonnull
@Override
public IWorkMonitor getMainThreadMonitor() {
throw new IllegalStateException("Main thread monitor not available");
}
@Nonnull
@Override
public Terminal getTerminal() {
throw new IllegalStateException("Terminal not available");
@ -128,10 +123,10 @@ public void cancelTimer(int id) {
}
@Override
public void observe(@Nonnull Metric.Event summary, long value) {
public void observe(Metric.Event summary, long value) {
}
@Override
public void observe(@Nonnull Metric.Counter counter) {
public void observe(Metric.Counter counter) {
}
}

View File

@ -16,7 +16,6 @@
import dan200.computercraft.core.metrics.MetricsObserver;
import dan200.computercraft.test.core.filesystem.MemoryMount;
import javax.annotation.Nonnull;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@ -60,13 +59,11 @@ public MetricsObserver getMetrics() {
return this;
}
@Nonnull
@Override
public String getHostString() {
return "ComputerCraft 1.0 (Test environment)";
}
@Nonnull
@Override
public String getUserAgent() {
return "ComputerCraft/1.0";

View File

@ -5,16 +5,17 @@
*/
package dan200.computercraft.test.core.filesystem;
import dan200.computercraft.api.filesystem.FileOperationException;
import dan200.computercraft.api.filesystem.IWritableMount;
import dan200.computercraft.core.apis.handles.ArrayByteChannel;
import javax.annotation.Nonnull;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.StandardCharsets;
import java.util.*;
/**
@ -30,7 +31,7 @@ public MemoryMount() {
@Override
public void makeDirectory(@Nonnull String path) {
public void makeDirectory(String path) {
var file = new File(path);
while (file != null) {
directories.add(file.getPath());
@ -39,7 +40,7 @@ public void makeDirectory(@Nonnull String path) {
}
@Override
public void delete(@Nonnull String path) {
public void delete(String path) {
if (files.containsKey(path)) {
files.remove(path);
} else {
@ -55,9 +56,8 @@ public void delete(@Nonnull String path) {
}
}
@Nonnull
@Override
public WritableByteChannel openForWrite(@Nonnull final String path) {
public WritableByteChannel openForWrite(final String path) {
return Channels.newChannel(new ByteArrayOutputStream() {
@Override
public void close() throws IOException {
@ -67,9 +67,8 @@ public void close() throws IOException {
});
}
@Nonnull
@Override
public WritableByteChannel openForAppend(@Nonnull final String path) throws IOException {
public WritableByteChannel openForAppend(final String path) throws IOException {
var stream = new ByteArrayOutputStream() {
@Override
public void close() throws IOException {
@ -90,35 +89,36 @@ public long getRemainingSpace() {
}
@Override
public boolean exists(@Nonnull String path) {
public boolean exists(String path) {
return files.containsKey(path) || directories.contains(path);
}
@Override
public boolean isDirectory(@Nonnull String path) {
public boolean isDirectory(String path) {
return directories.contains(path);
}
@Override
public void list(@Nonnull String path, @Nonnull List<String> files) {
public void list(String path, List<String> files) {
for (var file : this.files.keySet()) {
if (file.startsWith(path)) files.add(file.substring(path.length() + 1));
}
}
@Override
public long getSize(@Nonnull String path) {
public long getSize(String path) {
throw new RuntimeException("Not implemented");
}
@Nonnull
@Override
public ReadableByteChannel openForRead(@Nonnull String path) {
return new ArrayByteChannel(files.get(path));
public ReadableByteChannel openForRead(String path) throws FileOperationException {
var file = files.get(path);
if (file == null) throw new FileOperationException(path, "File not found");
return new ArrayByteChannel(file);
}
public MemoryMount addFile(String file, String contents) {
files.put(file, contents.getBytes());
files.put(file, contents.getBytes(StandardCharsets.UTF_8));
return this;
}
}

View File

@ -0,0 +1,19 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
/**
* Test helpers for ComputerCraft's core Lua runtime and APIs.
*/
@DefaultQualifier(value = NonNull.class, locations = {
TypeUseLocation.RETURN,
TypeUseLocation.PARAMETER,
TypeUseLocation.FIELD,
})
package dan200.computercraft.test.core;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.framework.qual.DefaultQualifier;
import org.checkerframework.framework.qual.TypeUseLocation;

View File

@ -5,7 +5,6 @@
*/
package dan200.computercraft.shared.computer.terminal;
import dan200.computercraft.core.util.IoUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufOutputStream;
@ -119,14 +118,10 @@ private ByteBuf getCompressed() {
if (compressed != null) return compressed;
var compressed = Unpooled.buffer();
OutputStream stream = null;
try {
stream = new GZIPOutputStream(new ByteBufOutputStream(compressed));
try (OutputStream stream = new GZIPOutputStream(new ByteBufOutputStream(compressed))) {
stream.write(buffer.array(), buffer.arrayOffset(), buffer.readableBytes());
} catch (IOException e) {
throw new UncheckedIOException(e);
} finally {
IoUtil.closeQuietly(stream);
}
return this.compressed = compressed;
@ -135,9 +130,7 @@ private ByteBuf getCompressed() {
private static ByteBuf readCompressed(ByteBuf buf, int length, boolean compress) {
if (compress) {
var buffer = Unpooled.buffer();
InputStream stream = null;
try {
stream = new GZIPInputStream(new ByteBufInputStream(buf, length));
try (InputStream stream = new GZIPInputStream(new ByteBufInputStream(buf, length))) {
var swap = new byte[8192];
while (true) {
var bytes = stream.read(swap);
@ -146,8 +139,6 @@ private static ByteBuf readCompressed(ByteBuf buf, int length, boolean compress)
}
} catch (IOException e) {
throw new UncheckedIOException(e);
} finally {
IoUtil.closeQuietly(stream);
}
return buffer;
} else {

View File

@ -9,9 +9,9 @@
import dan200.computercraft.api.lua.LuaFunction;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.core.util.StringUtil;
import dan200.computercraft.shared.MediaProviders;
import dan200.computercraft.shared.media.items.ItemDisk;
import dan200.computercraft.core.util.StringUtil;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -79,17 +79,16 @@ public final Object[] getDiskLabel() {
* If the inserted disk's label can't be changed (for example, a record),
* an error will be thrown.
*
* @param labelA The new label of the disk, or {@code nil} to clear.
* @param label The new label of the disk, or {@code nil} to clear.
* @throws LuaException If the disk's label can't be changed.
*/
@LuaFunction(mainThread = true)
public final void setDiskLabel(Optional<String> labelA) throws LuaException {
var label = labelA.orElse(null);
public final void setDiskLabel(Optional<String> label) throws LuaException {
var stack = diskDrive.getDiskStack();
var media = MediaProviders.get(stack);
if (media == null) return;
if (!media.setLabel(stack, StringUtil.normaliseLabel(label))) {
if (!media.setLabel(stack, label.map(StringUtil::normaliseLabel).orElse(null))) {
throw new LuaException("Disk label cannot be changed");
}
diskDrive.setDiskStack(stack);

View File

@ -135,7 +135,7 @@ public final boolean endPage() throws LuaException {
@LuaFunction
public final void setPageTitle(Optional<String> title) throws LuaException {
getCurrentPage();
printer.setPageTitle(StringUtil.normaliseLabel(title.orElse("")));
printer.setPageTitle(title.map(StringUtil::normaliseLabel).orElse(null));
}
/**

View File

@ -73,13 +73,15 @@ override fun addAPI(api: ILuaAPI) {
apis.add(api)
if (api is OSAPI) {
val newMachine = if (api.computerID != 1) {
CobaltLuaMachine(environment)
} else if (api.computerLabel != null) {
KotlinMachine(environment, api.computerLabel[0] as String)
} else {
LOGGER.error("Kotlin Lua machine must have a label")
CobaltLuaMachine(environment)
val id = api.computerID
val label = api.computerLabel
val newMachine = when {
id != 1 -> CobaltLuaMachine(environment)
label != null && label[0] != null -> KotlinMachine(environment, label[0] as String)
else -> {
LOGGER.error("Kotlin Lua machine must have a label")
CobaltLuaMachine(environment)
}
}
this.delegate = newMachine
@ -106,7 +108,8 @@ override fun close() {
}
}
private class KotlinMachine(environment: MachineEnvironment, private val label: String) : KotlinLuaMachine(environment) {
private class KotlinMachine(environment: MachineEnvironment, private val label: String) :
KotlinLuaMachine(environment) {
override fun getTask(): (suspend KotlinLuaMachine.() -> Unit)? = computers[label]?.poll()
}