mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2026-10-20 14:32:29 +00:00
Enable JSpecify support in NullAway
Except in the common main classes: there's a couple of bugs in the current version which crash the type checker.
This commit is contained in:
@@ -89,6 +89,7 @@ tasks.withType(JavaCompile::class.java).configureEach {
|
||||
option("UnusedMethod:ExemptingMethodAnnotations", "dan200.computercraft.api.lua.LuaFunction")
|
||||
|
||||
check("NullAway", CheckSeverity.ERROR)
|
||||
check("JSpecifyUnrecognizedAnnotationLocation", CheckSeverity.ERROR)
|
||||
option(
|
||||
"NullAway:AnnotatedPackages",
|
||||
listOf("dan200.computercraft", "cc.tweaked", "net.fabricmc.fabric.api").joinToString(","),
|
||||
@@ -97,6 +98,8 @@ tasks.withType(JavaCompile::class.java).configureEach {
|
||||
option("NullAway:CastToNonNullMethod", "dan200.computercraft.core.util.Nullability.assertNonNull")
|
||||
option("NullAway:CheckOptionalEmptiness")
|
||||
option("NullAway:AcknowledgeRestrictiveAnnotations")
|
||||
option("NullAway:JSpecifyMode")
|
||||
option("NullAway:JSpecifyExperimental")
|
||||
|
||||
excludedPaths.set(".*/jmh_generated/.*")
|
||||
}
|
||||
|
||||
@@ -54,6 +54,10 @@ val verifyClassesMatch = tasks.register<VerifyJavaCompile>("verifyClassesMatch")
|
||||
check("MissingOverride", CheckSeverity.OFF)
|
||||
check("LoaderOverride", CheckSeverity.DEFAULT)
|
||||
check("MissingLoaderOverride", CheckSeverity.DEFAULT)
|
||||
|
||||
// TODO: Enable JSpecify in the common classes. Unfortunately there's several bugs blocking us right now.
|
||||
option("NullAway:JSpecifyMode", false)
|
||||
option("NullAway:JSpecifyExperimental", false)
|
||||
}
|
||||
|
||||
source = minecraftEmbeddedProjectResolved.map {
|
||||
|
||||
@@ -70,7 +70,7 @@ illuaminate = "0.1.0-83-g1131f68"
|
||||
lwjgl = "3.4.3"
|
||||
minotaur = "2.9.0"
|
||||
modDevGradle = "2.0.147"
|
||||
nullAway = "0.14.0"
|
||||
nullAway = "0.14.2"
|
||||
shadow = "9.6.1"
|
||||
spotless = "8.10.4"
|
||||
teavm = "0.16.0"
|
||||
|
||||
@@ -15,7 +15,6 @@ import java.util.stream.Stream;
|
||||
*
|
||||
* @since 1.115
|
||||
*/
|
||||
@Nullable
|
||||
public interface PrintoutContents {
|
||||
/**
|
||||
* Get the (possibly empty) title for this printout.
|
||||
|
||||
@@ -8,6 +8,7 @@ import cc.tweaked.gradle.MergeTrees
|
||||
import cc.tweaked.gradle.annotationProcessorEverywhere
|
||||
import cc.tweaked.vanillaextract.configurations.Capabilities.clientClasses
|
||||
import cc.tweaked.vanillaextract.configurations.Capabilities.commonClasses
|
||||
import net.ltgt.gradle.errorprone.errorprone
|
||||
|
||||
plugins {
|
||||
`java-test-fixtures`
|
||||
@@ -70,6 +71,14 @@ dependencies {
|
||||
"cctJavadoc"(libs.cctJavadoc)
|
||||
}
|
||||
|
||||
// TODO: Enable JSpecify in the common classes. Unfortunately there's several bugs blocking us right now.
|
||||
tasks.compileJava {
|
||||
options.errorprone {
|
||||
option("NullAway:JSpecifyMode", false)
|
||||
option("NullAway:JSpecifyExperimental", false)
|
||||
}
|
||||
}
|
||||
|
||||
illuaminate {
|
||||
version = libs.versions.illuaminate
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ public final class ClientRegistry {
|
||||
return layer == 0 ? ((IColouredItem) stack.getItem()).getColour(stack) : 0xFFFFFF;
|
||||
}
|
||||
|
||||
public static void registerShaders(ResourceProvider resources, BiConsumer<ShaderInstance, Consumer<ShaderInstance>> load) throws IOException {
|
||||
public static void registerShaders(ResourceProvider resources, BiConsumer<ShaderInstance, Consumer<@Nullable ShaderInstance>> load) throws IOException {
|
||||
RenderTypes.registerShaders(resources, (name, create, onLoaded) -> {
|
||||
ShaderInstance shader;
|
||||
try {
|
||||
|
||||
+2
-1
@@ -183,7 +183,8 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
|
||||
return;
|
||||
}
|
||||
|
||||
var name = file.getFileName().toString();
|
||||
var fileName = file.getFileName();
|
||||
var name = fileName == null ? "unknown" : fileName.toString();
|
||||
if (name.length() > UploadFileMessage.MAX_FILE_NAME) {
|
||||
alert(UploadResult.FAILED_TITLE, Component.translatable("gui.computercraft.upload.failed.name_too_long"));
|
||||
return;
|
||||
|
||||
+3
-2
@@ -7,6 +7,7 @@ package dan200.computercraft.client.integration;
|
||||
import dan200.computercraft.client.render.RenderTypes;
|
||||
import dan200.computercraft.client.render.text.DirectFixedWidthFontRenderer;
|
||||
import dan200.computercraft.client.render.vbo.DirectVertexBuffer;
|
||||
import dan200.computercraft.core.util.Nullability;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Optional;
|
||||
@@ -48,10 +49,10 @@ public class ShaderMod {
|
||||
}
|
||||
|
||||
private static final class Storage {
|
||||
static final ShaderMod INSTANCE = ServiceLoader.load(Provider.class)
|
||||
static final ShaderMod INSTANCE = Nullability.assertNonNull(ServiceLoader.load(Provider.class)
|
||||
.stream()
|
||||
.flatMap(x -> x.get().get().stream())
|
||||
.findFirst()
|
||||
.orElseGet(ShaderMod::new);
|
||||
.orElseGet(ShaderMod::new));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ package dan200.computercraft.data;
|
||||
import com.google.common.hash.HashCode;
|
||||
import com.google.common.hash.HashFunction;
|
||||
import com.google.common.hash.Hashing;
|
||||
import dan200.computercraft.core.util.Nullability;
|
||||
import dan200.computercraft.shared.util.PrettyJsonWriter;
|
||||
import net.minecraft.data.CachedOutput;
|
||||
import net.minecraft.data.DataProvider;
|
||||
@@ -38,7 +39,7 @@ public record PrettyDataProvider<T extends DataProvider>(T provider) implements
|
||||
|
||||
@Override
|
||||
public void writeIfNeeded(Path path, byte[] bytes, HashCode hashCode) throws IOException {
|
||||
var name = path.getFileName().toString();
|
||||
var name = Nullability.assertNonNull(path.getFileName()).toString();
|
||||
if (name.endsWith(".json") || name.endsWith(".mcmeta")) {
|
||||
bytes = PrettyJsonWriter.reformat(bytes);
|
||||
hashCode = HASH_FUNCTION.hashBytes(bytes);
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
package dan200.computercraft.impl.network.wired;
|
||||
|
||||
import dan200.computercraft.api.network.wired.WiredNode;
|
||||
import dan200.computercraft.core.util.Nullability;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A disjoint-set/union-find of {@link WiredNodeImpl}s.
|
||||
@@ -95,6 +95,6 @@ class NodeSet {
|
||||
* @return The associated network.
|
||||
*/
|
||||
WiredNetworkImpl network() {
|
||||
return Objects.requireNonNull(find().network);
|
||||
return Nullability.assertNonNull(find().network);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.core.util.Colour;
|
||||
import dan200.computercraft.core.util.Nullability;
|
||||
import dan200.computercraft.impl.PocketUpgrades;
|
||||
import dan200.computercraft.impl.TurtleUpgrades;
|
||||
import dan200.computercraft.impl.UpgradeManager;
|
||||
@@ -123,7 +124,6 @@ import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@@ -480,7 +480,7 @@ public final class ModRegistry {
|
||||
|
||||
ComputerCraftAPI.registerAPIFactory(computer -> {
|
||||
var turtle = computer.getComponent(ComputerComponents.TURTLE);
|
||||
var metrics = Objects.requireNonNull(computer.getComponent(ServerComputer.METRICS));
|
||||
var metrics = Nullability.assertNonNull(computer.getComponent(ServerComputer.METRICS));
|
||||
return turtle == null ? null : new TurtleAPI(metrics, (TurtleAccessInternal) turtle);
|
||||
});
|
||||
|
||||
|
||||
+1
-1
@@ -60,6 +60,6 @@ class ItemDetailsTest {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Matcher<Map<? extends String, ?>> containsEntryWith(String key, Matcher<?> value) {
|
||||
return CustomMatchers.containsEntryWith(key, (Matcher<? super Object>) value);
|
||||
return CustomMatchers.containsEntryWith(key, (Matcher<Object>) value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.mojang.blaze3d.platform.Lighting;
|
||||
import com.mojang.blaze3d.platform.NativeImage;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.vertex.VertexSorting;
|
||||
import dan200.computercraft.core.util.Nullability;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.FogRenderer;
|
||||
import org.joml.Matrix4f;
|
||||
@@ -33,7 +34,7 @@ public class ImageRenderer implements AutoCloseable {
|
||||
}
|
||||
|
||||
public void captureRender(Path output, Runnable render) throws IOException {
|
||||
Files.createDirectories(output.getParent());
|
||||
Files.createDirectories(Nullability.assertNonNull(output.getParent()));
|
||||
|
||||
framebuffer.setClearColor(0, 0, 0, 0);
|
||||
framebuffer.clear(Minecraft.ON_OSX);
|
||||
|
||||
+2
-3
@@ -4,6 +4,7 @@
|
||||
|
||||
package dan200.computercraft.mixin.gametest;
|
||||
|
||||
import dan200.computercraft.core.util.Nullability;
|
||||
import dan200.computercraft.gametest.core.TestHooks;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtUtils;
|
||||
@@ -12,8 +13,6 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Patches {@link NbtUtils#structureToSnbt(CompoundTag)} to remove air blocks from the structure file. This
|
||||
* significantly reduces the size of our generated templates.
|
||||
@@ -27,7 +26,7 @@ abstract class NbtUtilsMixin {
|
||||
@SuppressWarnings("unused")
|
||||
private static void structureToSnbt(CompoundTag tag, CallbackInfoReturnable<String> ci) {
|
||||
// Load in the structure, strip out air, then save it back again.
|
||||
var structure = Objects.requireNonNull(TestHooks.getStructureManager()).readStructure(tag);
|
||||
var structure = Nullability.assertNonNull(TestHooks.getStructureManager()).readStructure(tag);
|
||||
var palette = ((StructureTemplateAccessor) structure).getPalettes().get(0);
|
||||
palette.blocks().removeIf(x -> x.state().isAir());
|
||||
var newTag = structure.save(new CompoundTag());
|
||||
|
||||
@@ -74,7 +74,7 @@ public interface IArguments {
|
||||
* @see #get(int) To get a single argument.
|
||||
*/
|
||||
default @Nullable Object[] getAll() throws LuaException {
|
||||
var result = new Object[count()];
|
||||
var result = new @Nullable Object[count()];
|
||||
for (var i = 0; i < result.length; i++) result[i] = get(i);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -458,7 +458,7 @@ public interface LuaTable<K, V> extends Map<K, V> {
|
||||
}
|
||||
|
||||
@Override
|
||||
default V remove(Object o) {
|
||||
default V remove(@Nullable Object o) {
|
||||
throw new UnsupportedOperationException("Cannot modify LuaTable");
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Various utility functions for operating with Lua values.
|
||||
@@ -159,7 +160,7 @@ public final class LuaValues {
|
||||
* @throws LuaException If this is not a known enum value.
|
||||
*/
|
||||
public static <T extends Enum<T>> T checkEnum(int index, Class<T> klass, String value) throws LuaException {
|
||||
for (var possibility : klass.getEnumConstants()) {
|
||||
for (var possibility : Objects.requireNonNull(klass.getEnumConstants())) {
|
||||
if (possibility.name().equalsIgnoreCase(value)) return possibility;
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ public final class ObjectArguments implements IArguments {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getAll() {
|
||||
public @Nullable Object[] getAll() {
|
||||
return args.toArray();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import java.util.Set;
|
||||
public class ObjectLuaTable implements LuaTable<Object, Object> {
|
||||
private final Map<Object, Object> map;
|
||||
|
||||
@SuppressWarnings("NullAway")
|
||||
public ObjectLuaTable(Map<?, ?> map) {
|
||||
this.map = Collections.unmodifiableMap(map);
|
||||
}
|
||||
@@ -32,18 +33,17 @@ public class ObjectLuaTable implements LuaTable<Object, Object> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object o) {
|
||||
public boolean containsKey(@Nullable Object o) {
|
||||
return map.containsKey(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object o) {
|
||||
public boolean containsValue(@Nullable Object o) {
|
||||
return map.containsKey(o);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object get(Object o) {
|
||||
public @Nullable Object get(@Nullable Object o) {
|
||||
return map.get(o);
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ public class ObjectLuaTable implements LuaTable<Object, Object> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
public boolean equals(@Nullable Object o) {
|
||||
return this == o || o instanceof Map<?, ?> otherMap && map.equals(otherMap);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import dan200.computercraft.api.lua.IArguments;
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.core.util.Nullability;
|
||||
import dan200.computercraft.core.util.StringUtil;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
@@ -135,7 +136,7 @@ public class OSAPI implements ILuaAPI {
|
||||
* @param args The parameters of the event.
|
||||
* @cc.tparam string name The name of the event to queue.
|
||||
* @cc.param ... The parameters of the event. These can be any primitive type (boolean, number, string) as well as
|
||||
* tables. Other types (like functions), as well as metatables, will not be preserved.
|
||||
* tables. Other types (like functions), as well as metatables, will not be preserved.
|
||||
* @cc.see os.pullEvent To pull the event queued
|
||||
*/
|
||||
@LuaFunction
|
||||
@@ -408,7 +409,6 @@ public class OSAPI implements ILuaAPI {
|
||||
* <pre>{@code
|
||||
* os.date("%A %d %B %Y") -- See the reference above!
|
||||
* }</pre>
|
||||
*
|
||||
* @cc.usage Convert a timestamp to a table.
|
||||
* <pre>{@code
|
||||
* os.date("!*t", 1242534247)
|
||||
@@ -430,7 +430,7 @@ public class OSAPI implements ILuaAPI {
|
||||
@LuaFunction
|
||||
public final Object date(Optional<String> formatA, Optional<Long> timeA) throws LuaException {
|
||||
var format = formatA.orElse("%c");
|
||||
long time = timeA.orElseGet(() -> Instant.now().getEpochSecond());
|
||||
long time = Nullability.assertNonNull(timeA.orElseGet(() -> Instant.now().getEpochSecond()));
|
||||
|
||||
var instant = Instant.ofEpochSecond(time);
|
||||
ZonedDateTime date;
|
||||
|
||||
@@ -187,7 +187,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
|
||||
private final IAPIEnvironment environment;
|
||||
private final MethodSupplier<PeripheralMethod> peripheralMethods;
|
||||
private final PeripheralWrapper[] peripherals = new PeripheralWrapper[6];
|
||||
private final @Nullable PeripheralWrapper[] peripherals = new PeripheralWrapper[6];
|
||||
private boolean running;
|
||||
|
||||
public PeripheralAPI(IAPIEnvironment environment, MethodSupplier<PeripheralMethod> peripheralMethods) {
|
||||
|
||||
+2
@@ -71,6 +71,8 @@ public final class AddressRule {
|
||||
|
||||
var port = socketAddress.getPort();
|
||||
var address = socketAddress.getAddress();
|
||||
if (address == null) throw new IllegalStateException("Socket address must be resolved");
|
||||
|
||||
var ipv4Address = address instanceof Inet6Address inet6 && InetAddresses.is6to4Address(inet6)
|
||||
? InetAddresses.get6to4IPv4Address(inet6) : null;
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.google.common.primitives.Primitives;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import dan200.computercraft.api.lua.*;
|
||||
import dan200.computercraft.core.methods.LuaMethod;
|
||||
import dan200.computercraft.core.util.Nullability;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -179,7 +180,7 @@ final class Generator<T> {
|
||||
}
|
||||
|
||||
// unsafe can only be used on the computer thread, so reject it for mainThread functions.
|
||||
var annotation = method.getAnnotation(LuaFunction.class);
|
||||
var annotation = Nullability.assertNonNull(method.getAnnotation(LuaFunction.class));
|
||||
if (annotation.unsafe() && annotation.mainThread()) {
|
||||
LOG.error("Lua Method {}.{} cannot use unsafe and mainThread.", method.getDeclaringClass().getName(), method.getName());
|
||||
return false;
|
||||
@@ -231,7 +232,7 @@ final class Generator<T> {
|
||||
private Optional<T> build(Method method, MethodHandle handle, List<Type> parameters) {
|
||||
LOG.debug("Generating method wrapper for {}.{}.", method.getDeclaringClass().getName(), method.getName());
|
||||
|
||||
var annotation = method.getAnnotation(LuaFunction.class);
|
||||
var annotation = Nullability.assertNonNull(method.getAnnotation(LuaFunction.class));
|
||||
var wrappedHandle = buildMethodHandle(method, handle, parameters, annotation.unsafe());
|
||||
if (wrappedHandle == null) return Optional.empty();
|
||||
|
||||
|
||||
@@ -4,12 +4,14 @@
|
||||
|
||||
package dan200.computercraft.core.asm;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.function.IntFunction;
|
||||
|
||||
final class IntCache<T> {
|
||||
private final IntFunction<T> factory;
|
||||
private volatile Object[] cache = new Object[16];
|
||||
private volatile @Nullable Object[] cache = new Object[16];
|
||||
|
||||
IntCache(IntFunction<T> factory) {
|
||||
this.factory = factory;
|
||||
|
||||
@@ -30,7 +30,7 @@ final class MethodSupplierImpl<T> implements MethodSupplier<T> {
|
||||
private final List<GenericMethod> genericMethods;
|
||||
private final Generator<T> generator;
|
||||
private final IntCache<T> dynamic;
|
||||
private final Function<Object, String[]> dynamicMethods;
|
||||
private final Function<Object, String @Nullable []> dynamicMethods;
|
||||
|
||||
private final ClassValue<List<NamedMethod<T>>> classCache = new ClassValue<>() {
|
||||
private final Function<Class<?>, List<NamedMethod<T>>> getter = catching(MethodSupplierImpl.this::getMethodsImpl, List.of());
|
||||
@@ -45,7 +45,7 @@ final class MethodSupplierImpl<T> implements MethodSupplier<T> {
|
||||
List<GenericMethod> genericMethods,
|
||||
Generator<T> generator,
|
||||
IntCache<T> dynamic,
|
||||
Function<Object, String[]> dynamicMethods
|
||||
Function<Object, String @Nullable []> dynamicMethods
|
||||
) {
|
||||
this.genericMethods = genericMethods;
|
||||
this.generator = generator;
|
||||
|
||||
@@ -31,7 +31,7 @@ public final class Environment implements IAPIEnvironment {
|
||||
private final ComputerEnvironment environment;
|
||||
private final MetricsObserver metrics;
|
||||
|
||||
private final IPeripheral[] peripherals = new IPeripheral[ComputerSide.COUNT];
|
||||
private final @Nullable IPeripheral[] peripherals = new IPeripheral[ComputerSide.COUNT];
|
||||
private @Nullable IPeripheralChangeListener peripheralListener = null;
|
||||
|
||||
private final Int2ObjectMap<Timer> timers = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
+7
-5
@@ -120,7 +120,7 @@ public final class ComputerThread implements ComputerScheduler {
|
||||
* The array of current workers, and their owning threads.
|
||||
*/
|
||||
@GuardedBy("threadLock")
|
||||
private final WorkerThread[] workers;
|
||||
private final @Nullable WorkerThread[] workers;
|
||||
|
||||
/**
|
||||
* The number of workers in {@link #workers}.
|
||||
@@ -187,7 +187,7 @@ public final class ComputerThread implements ComputerScheduler {
|
||||
}
|
||||
|
||||
@SuppressWarnings("GuardedBy")
|
||||
private WorkerThread[] workersReadOnly() {
|
||||
private @Nullable WorkerThread[] workersReadOnly() {
|
||||
return workers;
|
||||
}
|
||||
|
||||
@@ -204,7 +204,9 @@ public final class ComputerThread implements ComputerScheduler {
|
||||
try {
|
||||
LOG.trace("Possibly spawning a worker or monitor.");
|
||||
|
||||
if (monitor == null || !monitor.isAlive()) (monitor = monitorFactory.newThread(new Monitor())).start();
|
||||
if (monitor == null || !monitor.isAlive()) {
|
||||
(monitor = Objects.requireNonNull(monitorFactory.newThread(new Monitor()), "Cannot create monitor thread")).start();
|
||||
}
|
||||
if (idleWorkers.get() == 0 || workerCount < workers.length) {
|
||||
for (var i = 0; i < workers.length; i++) {
|
||||
if (workers[i] == null) {
|
||||
@@ -575,7 +577,7 @@ public final class ComputerThread implements ComputerScheduler {
|
||||
/**
|
||||
* The computer we're currently running.
|
||||
*/
|
||||
final AtomicReference<ExecutorImpl> currentExecutor = new AtomicReference<>(null);
|
||||
final AtomicReference<@Nullable ExecutorImpl> currentExecutor = new AtomicReference<>(null);
|
||||
|
||||
/**
|
||||
* The last time we reported a stack trace, used to avoid spamming the logs.
|
||||
@@ -584,7 +586,7 @@ public final class ComputerThread implements ComputerScheduler {
|
||||
|
||||
WorkerThread(int index) {
|
||||
this.index = index;
|
||||
owner = workerFactory.newThread(this);
|
||||
owner = Objects.requireNonNull(workerFactory.newThread(this), "Cannot create thread");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.google.common.base.Joiner;
|
||||
import dan200.computercraft.api.filesystem.FileAttributes;
|
||||
import dan200.computercraft.api.filesystem.FileOperationException;
|
||||
import dan200.computercraft.api.filesystem.Mount;
|
||||
import dan200.computercraft.core.util.Nullability;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
@@ -58,7 +59,7 @@ public class FileMount implements Mount {
|
||||
if (path.isEmpty() && !created()) return;
|
||||
|
||||
try (var stream = Files.newDirectoryStream(resolvePath(path))) {
|
||||
stream.forEach(x -> contents.add(x.getFileName().toString()));
|
||||
stream.forEach(x -> contents.add(Nullability.assertNonNull(x.getFileName()).toString()));
|
||||
} catch (IOException e) {
|
||||
throw remapException(path, e);
|
||||
}
|
||||
|
||||
@@ -381,7 +381,7 @@ public class FileSystem {
|
||||
|
||||
if (part.equals("..")) {
|
||||
// .. can cancel out the last folder entered
|
||||
if (outputParts.isEmpty() || outputParts.peekLast().equals("..")) {
|
||||
if (outputParts.isEmpty() || outputParts.getLast().equals("..")) {
|
||||
outputParts.addLast("..");
|
||||
} else {
|
||||
outputParts.removeLast();
|
||||
|
||||
@@ -67,6 +67,8 @@ public final class JarMount extends ArchiveMount<JarMount.FileEntry> implements
|
||||
if (file.zipEntry == null) throw new FileOperationException(path, NO_SUCH_FILE);
|
||||
|
||||
try (var stream = zip.getInputStream(file.zipEntry)) {
|
||||
if (stream == null) throw new IllegalStateException("Zip entry must exist");
|
||||
|
||||
return stream.readAllBytes();
|
||||
} catch (IOException e) {
|
||||
// Mask other IO exceptions as a non-existent file.
|
||||
|
||||
@@ -200,7 +200,7 @@ public final class MemoryMount extends AbstractInMemoryMount<MemoryMount.FileEnt
|
||||
|
||||
private long computeUsedSpace() {
|
||||
Queue<FileEntry> queue = new ArrayDeque<>();
|
||||
queue.add(root);
|
||||
queue.add(Nullability.assertNonNull(root));
|
||||
|
||||
long size = 0;
|
||||
|
||||
|
||||
+14
-5
@@ -61,13 +61,22 @@ public class WritableFileMount extends FileMount implements WritableMount {
|
||||
return capacity - MINIMUM_FILE_SIZE;
|
||||
}
|
||||
|
||||
private File getParentFile(File file) {
|
||||
if (file.equals(rootFile)) throw new IllegalArgumentException("Cannot navigate above root file");
|
||||
|
||||
var parent = file.getParentFile();
|
||||
if (parent == null) throw new IllegalStateException("File has no parent");
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadOnly(String path) {
|
||||
var file = resolveFile(path);
|
||||
while (true) {
|
||||
if (file.exists()) return !file.canWrite();
|
||||
if (file.equals(rootFile)) return false;
|
||||
file = file.getParentFile();
|
||||
file = getParentFile(file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,10 +90,10 @@ public class WritableFileMount extends FileMount implements WritableMount {
|
||||
}
|
||||
|
||||
var dirsToCreate = 1;
|
||||
var parent = file.getParentFile();
|
||||
var parent = getParentFile(file);
|
||||
while (!parent.exists()) {
|
||||
++dirsToCreate;
|
||||
parent = parent.getParentFile();
|
||||
parent = getParentFile(parent);
|
||||
}
|
||||
|
||||
if (getRemainingSpace() < dirsToCreate * MINIMUM_FILE_SIZE) {
|
||||
@@ -112,8 +121,8 @@ public class WritableFileMount extends FileMount implements WritableMount {
|
||||
// Empty directories first
|
||||
if (file.isDirectory()) {
|
||||
var children = file.list();
|
||||
for (var aChildren : children) {
|
||||
deleteRecursively(new File(file, aChildren));
|
||||
if (children != null) {
|
||||
for (var aChildren : children) deleteRecursively(new File(file, aChildren));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -313,7 +313,7 @@ public class CobaltLuaMachine implements ILuaMachine {
|
||||
|
||||
static @Nullable Object[] toObjects(Varargs values) {
|
||||
var count = values.count();
|
||||
var objects = new Object[count];
|
||||
var objects = new @Nullable Object[count];
|
||||
for (var i = 0; i < count; i++) objects[i] = toObject(values.arg(i + 1), null);
|
||||
return objects;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ package dan200.computercraft.core.lua;
|
||||
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaValues;
|
||||
import dan200.computercraft.core.util.Nullability;
|
||||
import org.jetbrains.annotations.VisibleForTesting;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.squiddev.cobalt.*;
|
||||
@@ -59,7 +60,7 @@ class TableImpl implements dan200.computercraft.api.lua.LuaTable<Object, Object>
|
||||
}
|
||||
}
|
||||
|
||||
private LuaValue getImpl(Object o) {
|
||||
private LuaValue getImpl(@Nullable Object o) {
|
||||
checkValid();
|
||||
var value = convertValue(o);
|
||||
return value == null ? Constants.NIL : table.rawget(value);
|
||||
@@ -82,13 +83,13 @@ class TableImpl implements dan200.computercraft.api.lua.LuaTable<Object, Object>
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object o) {
|
||||
public boolean containsKey(@Nullable Object o) {
|
||||
return !getImpl(o).isNil();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object get(Object o) {
|
||||
public Object get(@Nullable Object o) {
|
||||
return CobaltLuaMachine.toObject(getImpl(o), null);
|
||||
}
|
||||
|
||||
@@ -102,12 +103,12 @@ class TableImpl implements dan200.computercraft.api.lua.LuaTable<Object, Object>
|
||||
checkValid();
|
||||
if (backingMap != null) return backingMap;
|
||||
return backingMap = Collections.unmodifiableMap(
|
||||
Objects.requireNonNull((Map<?, ?>) CobaltLuaMachine.toObject(table, null))
|
||||
(Map<? extends Object, ? extends Object>) Nullability.assertNonNull(CobaltLuaMachine.toObject(table, null))
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object o) {
|
||||
public boolean containsValue(@Nullable Object o) {
|
||||
return getBackingMap().containsKey(o);
|
||||
}
|
||||
|
||||
|
||||
@@ -231,11 +231,10 @@ final class VarargArguments implements IArguments {
|
||||
}
|
||||
|
||||
@SuppressWarnings("ArrayRecordComponent")
|
||||
private record ArraySlice<T>(T[] array, int offset) {
|
||||
private record ArraySlice<T>(@Nullable T[] array, int offset) {
|
||||
// FIXME: We should be able to remove the @Nullables if we update NullAway.
|
||||
|
||||
@Nullable
|
||||
T get(int index) {
|
||||
@Nullable T get(int index) {
|
||||
return array[offset + index];
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ public class LuaUtil {
|
||||
private static final Set<?> EMPTY_SET = Set.of();
|
||||
private static final Map<?, ?> EMPTY_MAP = Map.of();
|
||||
|
||||
public static Object[] consArray(Object value, Collection<?> rest) {
|
||||
public static Object[] consArray(Object value, Collection<? extends Object> rest) {
|
||||
if (rest.isEmpty()) return new Object[]{ value };
|
||||
|
||||
// I'm not proud of this code.
|
||||
|
||||
@@ -70,11 +70,12 @@ public final class ThreadUtils {
|
||||
* @see #factory(String)
|
||||
*/
|
||||
public static ThreadFactoryBuilder builder(String name) {
|
||||
var group = new ThreadGroup(baseGroup, baseGroup.getName() + "-" + name);
|
||||
var fullName = baseGroup.getName() + "-" + name;
|
||||
var group = new ThreadGroup(baseGroup, fullName);
|
||||
return new ThreadFactoryBuilder()
|
||||
.setDaemon(true)
|
||||
.setNameFormat(group.getName().replace("%", "%%") + "-%d")
|
||||
.setUncaughtExceptionHandler((t, e) -> LOG.error("Exception in thread " + t.getName(), e))
|
||||
.setNameFormat(fullName.replace("%", "%%") + "-%d")
|
||||
.setUncaughtExceptionHandler((t, e) -> LOG.error("Exception in thread {}", t.getName(), e))
|
||||
.setThreadFactory(x -> new Thread(group, x));
|
||||
}
|
||||
|
||||
|
||||
+4
-3
@@ -6,6 +6,7 @@ package dan200.computercraft.test.core;
|
||||
|
||||
import org.hamcrest.FeatureMatcher;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
@@ -16,7 +17,7 @@ import java.util.function.Function;
|
||||
* @param <T> The type of the object to be matched.
|
||||
* @param <U> The type of the projection/field to be matched.
|
||||
*/
|
||||
public final class ContramapMatcher<T, U> extends FeatureMatcher<T, U> {
|
||||
public final class ContramapMatcher<T, U extends @Nullable Object> extends FeatureMatcher<T, U> {
|
||||
private final Function<T, U> convert;
|
||||
|
||||
public ContramapMatcher(String desc, Function<T, U> convert, Matcher<U> matcher) {
|
||||
@@ -29,11 +30,11 @@ public final class ContramapMatcher<T, U> extends FeatureMatcher<T, U> {
|
||||
return convert.apply(actual);
|
||||
}
|
||||
|
||||
public static <T, U> Matcher<T> contramap(Matcher<U> matcher, String desc, Function<T, U> convert) {
|
||||
public static <T, U extends @Nullable Object> Matcher<T> contramap(Matcher<U> matcher, String desc, Function<T, U> convert) {
|
||||
return new ContramapMatcher<>(desc, convert, matcher);
|
||||
}
|
||||
|
||||
public static <T, U> Matcher<T> contramap(Matcher<U> matcher, Function<T, U> convert) {
|
||||
public static <T, U extends @Nullable Object> Matcher<T> contramap(Matcher<U> matcher, Function<T, U> convert) {
|
||||
return new ContramapMatcher<>("-f(_)->", convert, matcher);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -6,6 +6,7 @@ package dan200.computercraft.test.core;
|
||||
|
||||
import org.hamcrest.Matcher;
|
||||
import org.hamcrest.StringDescription;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -53,7 +54,7 @@ public class CustomMatchers {
|
||||
* @param <V> The type of values in the map.
|
||||
* @return A matcher that projects out of a map.
|
||||
*/
|
||||
public static <K, V> Matcher<Map<? extends K, ? extends V>> containsEntryWith(K key, Matcher<? super V> value) {
|
||||
public static <K, V extends @Nullable Object> Matcher<Map<? extends K, ? extends V>> containsEntryWith(K key, Matcher<? super V> value) {
|
||||
return ContramapMatcher.contramap(value, new StringDescription().appendValue(key).toString(), x -> x.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
+6
-3
@@ -69,7 +69,7 @@ final class StructuralEqualities {
|
||||
}
|
||||
}
|
||||
|
||||
static final class FeatureEquality<T, U> implements StructuralEquality<T> {
|
||||
static final class FeatureEquality<T, U extends @Nullable Object> implements StructuralEquality<T> {
|
||||
private final String desc;
|
||||
private final Function<T, U> get;
|
||||
private final StructuralEquality<? super U> inner;
|
||||
@@ -100,7 +100,9 @@ final class StructuralEqualities {
|
||||
}
|
||||
}
|
||||
|
||||
record ListEquality<T>(StructuralEquality<T> equality) implements StructuralEquality<List<T>> {
|
||||
record ListEquality<T extends @Nullable Object>(
|
||||
StructuralEquality<T> equality
|
||||
) implements StructuralEquality<List<T>> {
|
||||
@Override
|
||||
public boolean equals(List<T> left, List<T> right) {
|
||||
if (left.size() != right.size()) return false;
|
||||
@@ -111,6 +113,7 @@ final class StructuralEqualities {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("NullAway") // FIXME: See https://github.com/uber/NullAway/issues/1727
|
||||
public void describe(Description description, List<T> object) {
|
||||
description.appendText("[");
|
||||
var separator = false;
|
||||
@@ -123,7 +126,7 @@ final class StructuralEqualities {
|
||||
}
|
||||
}
|
||||
|
||||
static final class EqualityMatcher<T> extends TypeSafeMatcher<T> {
|
||||
static final class EqualityMatcher<T extends @Nullable Object> extends TypeSafeMatcher<@Nullable T> {
|
||||
private final StructuralEquality<T> equality;
|
||||
private final T equalTo;
|
||||
|
||||
|
||||
+11
-6
@@ -6,6 +6,7 @@ package dan200.computercraft.test.core;
|
||||
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
@@ -18,7 +19,7 @@ import java.util.function.Function;
|
||||
*
|
||||
* @param <T> The type of the value to check.
|
||||
*/
|
||||
public interface StructuralEquality<T> {
|
||||
public interface StructuralEquality<T extends @Nullable Object> {
|
||||
/**
|
||||
* Check if two non-null values are equal.
|
||||
*
|
||||
@@ -52,7 +53,7 @@ public interface StructuralEquality<T> {
|
||||
* @param expected The expected value.
|
||||
* @return A matcher which checks if its input is equal to the given value.
|
||||
*/
|
||||
default Matcher<T> asMatcher(Class<T> klass, T expected) {
|
||||
default Matcher<@Nullable T> asMatcher(Class<T> klass, T expected) {
|
||||
return new StructuralEqualities.EqualityMatcher<>(klass, this, expected);
|
||||
}
|
||||
|
||||
@@ -117,8 +118,7 @@ public interface StructuralEquality<T> {
|
||||
* @param <U> The type of the field's.
|
||||
* @return The newly created {@link StructuralEquality} object.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T, U> StructuralEquality<T> field(Class<T> klass, String fieldName, StructuralEquality<U> inner) {
|
||||
static <T, U extends @Nullable Object> StructuralEquality<T> field(Class<T> klass, String fieldName, StructuralEquality<U> inner) {
|
||||
Field field;
|
||||
try {
|
||||
field = klass.getDeclaredField(fieldName);
|
||||
@@ -127,13 +127,18 @@ public interface StructuralEquality<T> {
|
||||
throw new IllegalArgumentException("Cannot find field", e);
|
||||
}
|
||||
|
||||
return new StructuralEqualities.FeatureEquality<>(fieldName, x -> {
|
||||
return new StructuralEqualities.FeatureEquality<>(fieldName, makeFieldReader(field), inner);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "NullAway" })
|
||||
private static <T, U extends @Nullable Object> Function<T, U> makeFieldReader(Field field) {
|
||||
return x -> {
|
||||
try {
|
||||
return (U) field.get(x);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new IllegalStateException("Cannot read field", e);
|
||||
}
|
||||
}, inner);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+9
-2
@@ -13,6 +13,7 @@ import dan200.computercraft.core.filesystem.FileMount;
|
||||
import dan200.computercraft.core.filesystem.JarMount;
|
||||
import dan200.computercraft.core.filesystem.MemoryMount;
|
||||
import dan200.computercraft.core.metrics.MetricsObserver;
|
||||
import dan200.computercraft.core.util.Nullability;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
@@ -75,7 +76,7 @@ public class BasicEnvironment implements ComputerEnvironment, GlobalEnvironment
|
||||
|
||||
@Override
|
||||
public @Nullable InputStream getLuaBios() {
|
||||
return ComputerContext.class.getClassLoader().getResourceAsStream("data/computercraft/lua/bios.lua");
|
||||
return Nullability.assertNonNull(ComputerContext.class.getClassLoader()).getResourceAsStream("data/computercraft/lua/bios.lua");
|
||||
}
|
||||
|
||||
public static Mount createMount(Class<?> klass, String path, String fallback) {
|
||||
@@ -104,7 +105,13 @@ public class BasicEnvironment implements ComputerEnvironment, GlobalEnvironment
|
||||
}
|
||||
|
||||
private static File getContainingFile(Class<?> klass) {
|
||||
var path = klass.getProtectionDomain().getCodeSource().getLocation().getPath();
|
||||
var source = klass.getProtectionDomain().getCodeSource();
|
||||
if (source == null) throw new IllegalStateException("Cannot find owning Jar");
|
||||
|
||||
var location = source.getLocation();
|
||||
if (location == null) throw new IllegalStateException("Cannot find owning Jar");
|
||||
|
||||
var path = location.getPath();
|
||||
var bangIndex = path.indexOf("!");
|
||||
|
||||
// Plain old file, so step up from dan200.computercraft.
|
||||
|
||||
@@ -21,11 +21,13 @@ import org.jspecify.annotations.Nullable;
|
||||
public final class MediaLookup {
|
||||
public static final ResourceLocation ID = new ResourceLocation(ComputerCraftAPI.MOD_ID, "media");
|
||||
|
||||
@SuppressWarnings("NullAway") // FIXME(26.3): Can be removed when FAPI fixes their annotation
|
||||
private static final ItemApiLookup<IMedia, @Nullable Void> lookup = ItemApiLookup.get(ID, IMedia.class, Void.class);
|
||||
|
||||
private MediaLookup() {
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullAway") // FIXME(26.3): Can be removed when FAPI fixes their annotation
|
||||
public static ItemApiLookup<IMedia, @Nullable Void> get() {
|
||||
return lookup;
|
||||
}
|
||||
|
||||
+4
-1
@@ -4,6 +4,7 @@
|
||||
|
||||
package dan200.computercraft.client.integration.rei;
|
||||
|
||||
import dan200.computercraft.core.util.Nullability;
|
||||
import dan200.computercraft.shared.integration.UpgradeRecipeGenerator;
|
||||
import me.shedaniel.rei.api.client.registry.display.DynamicDisplayGenerator;
|
||||
import me.shedaniel.rei.api.common.entry.EntryStack;
|
||||
@@ -17,7 +18,9 @@ import java.util.Optional;
|
||||
* Provides custom recipe and usage hints for pocket/turtle upgrades.
|
||||
*/
|
||||
class UpgradeDisplayGenerator implements DynamicDisplayGenerator<DefaultCraftingDisplay<?>> {
|
||||
private final UpgradeRecipeGenerator<DefaultCraftingDisplay<?>> resolver = new UpgradeRecipeGenerator<>(DefaultCraftingDisplay::of);
|
||||
private final UpgradeRecipeGenerator<DefaultCraftingDisplay<? extends Object>> resolver = new UpgradeRecipeGenerator<>(
|
||||
r -> Nullability.assertNonNull(DefaultCraftingDisplay.of(r))
|
||||
);
|
||||
|
||||
@Override
|
||||
public Optional<List<DefaultCraftingDisplay<?>>> getRecipeFor(EntryStack<?> entry) {
|
||||
|
||||
@@ -53,7 +53,7 @@ public final class FoiledModel extends ForwardingBakedModel implements RenderCon
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
return this == obj || (obj instanceof FoiledModel other && wrapped.equals(other.wrapped));
|
||||
}
|
||||
|
||||
|
||||
@@ -141,6 +141,7 @@ public class ComputerCraft {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullAway") // FIXME(26.3): Can be removed when FAPI fixes their annotation
|
||||
private record ItemComponentImpl<T>(
|
||||
ItemApiLookup<T, @Nullable Void> lookup
|
||||
) implements ModRegistry.ItemComponent<T> {
|
||||
|
||||
+1
-2
@@ -41,7 +41,7 @@ public final class InventoryMethods extends AbstractInventoryMethods<InventoryMe
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (!(obj instanceof StorageWrapper other)) return false;
|
||||
|
||||
@@ -95,7 +95,6 @@ public final class InventoryMethods extends AbstractInventoryMethods<InventoryMe
|
||||
return storage == null ? null : new StorageWrapper(storage);
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullAway") // FIXME: Doesn't cope with @Nullable type parameter.
|
||||
private static @Nullable Storage<ItemVariant> extractContainerImpl(Level level, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, @Nullable Direction direction) {
|
||||
var internal = ItemStorage.SIDED.find(level, pos, state, blockEntity, null);
|
||||
if (internal != null) return internal;
|
||||
|
||||
+6
-2
@@ -22,6 +22,7 @@ import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
@@ -52,7 +53,9 @@ public final class FabricConfigFile extends ConfigFile {
|
||||
.build();
|
||||
|
||||
try {
|
||||
Files.createDirectories(path.getParent());
|
||||
var parent = path.getParent();
|
||||
if (parent != null) Files.createDirectories(parent);
|
||||
|
||||
FileWatcher.defaultInstance().addWatch(config.getNioPath(), this::loadConfig);
|
||||
} catch (IOException e) {
|
||||
LOG.error("Failed to load config from {}.", path, e);
|
||||
@@ -153,7 +156,8 @@ public final class FabricConfigFile extends ConfigFile {
|
||||
var path = getPath(name);
|
||||
spec.define(path, defaultValue, o -> o != null && o != NullObject.NULL_OBJECT && EnumGetMethod.NAME_IGNORECASE.validate(o, defaultValue.getDeclaringClass()));
|
||||
|
||||
var suffix = "Allowed Values: " + Arrays.stream(defaultValue.getDeclaringClass().getEnumConstants()).map(Enum::name).collect(Collectors.joining(", "));
|
||||
var suffix = "Allowed Values: " + Arrays.stream(Objects.requireNonNull(defaultValue.getDeclaringClass().getEnumConstants()))
|
||||
.map(Enum::name).collect(Collectors.joining(", "));
|
||||
return defineValue(name, takeComment(), suffix, defaultValue, (c, p, d) -> c.getEnumOrElse(p, d, EnumGetMethod.NAME_IGNORECASE));
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ public final class ForgeComputerCraftAPI {
|
||||
* @param capability The capability to register.
|
||||
* @see GenericSource
|
||||
*/
|
||||
public static void registerGenericCapability(Capability<?> capability) {
|
||||
public static void registerGenericCapability(Capability<? extends Object> capability) {
|
||||
getInstance().registerGenericCapability(capability);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ public interface ComputerCraftAPIForgeService extends ComputerCraftAPIService {
|
||||
|
||||
void registerPeripheralProvider(IPeripheralProvider provider);
|
||||
|
||||
void registerGenericCapability(Capability<?> capability);
|
||||
void registerGenericCapability(Capability<? extends Object> capability);
|
||||
|
||||
LazyOptional<WiredElement> getWiredElementAt(BlockGetter world, BlockPos pos, Direction side);
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ public final class ForgeClientRegistry {
|
||||
|
||||
@SubscribeEvent
|
||||
public static void registerShaders(RegisterShadersEvent event) throws IOException {
|
||||
ClientRegistry.registerShaders(event.getResourceProvider(), event::registerShader);
|
||||
ClientRegistry.registerShaders(event.getResourceProvider(), (s, c) -> event.registerShader(s, c));
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
|
||||
@@ -39,7 +39,7 @@ public final class FoiledModel extends BakedModelWrapper<BakedModel> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
return this == obj || (obj instanceof FoiledModel other && originalModel.equals(other.originalModel));
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ import java.util.Arrays;
|
||||
@AutoService(dan200.computercraft.impl.client.ClientPlatformHelper.class)
|
||||
public class ClientPlatformHelperImpl implements ClientPlatformHelper {
|
||||
private static final RandomSource random = RandomSource.create(0);
|
||||
private static final Direction[] directions = Arrays.copyOf(Direction.values(), 7);
|
||||
private static final @Nullable Direction[] directions = Arrays.copyOf(Direction.values(), 7);
|
||||
|
||||
@Override
|
||||
public BakedModel getModel(ModelManager manager, ResourceLocation location) {
|
||||
|
||||
@@ -43,10 +43,14 @@ import net.minecraftforge.fml.event.config.ModConfigEvent;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
|
||||
import net.minecraftforge.registries.NewRegistryEvent;
|
||||
import net.minecraftforge.registries.RegistryBuilder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@Mod(ComputerCraftAPI.MOD_ID)
|
||||
@Mod.EventBusSubscriber(modid = ComputerCraftAPI.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD)
|
||||
public final class ComputerCraft {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ComputerCraft.class);
|
||||
|
||||
public ComputerCraft() {
|
||||
ModRegistry.register();
|
||||
|
||||
@@ -135,8 +139,11 @@ public final class ComputerCraft {
|
||||
Pack.Position.BOTTOM,
|
||||
PackSource.FEATURE
|
||||
);
|
||||
|
||||
event.addRepositorySource(add -> add.accept(pack));
|
||||
if (pack == null) {
|
||||
LOG.error("Cannot find " + TurtleStorage.DATA_PACK_NAME + " datapack");
|
||||
} else {
|
||||
event.addRepositorySource(add -> add.accept(pack));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ public final class ComputerCraftAPIImpl extends AbstractComputerCraftAPI impleme
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerGenericCapability(Capability<?> capability) {
|
||||
public void registerGenericCapability(Capability<? extends Object> capability) {
|
||||
Peripherals.registerGenericCapability(capability);
|
||||
}
|
||||
|
||||
|
||||
@@ -59,14 +59,13 @@ public final class Peripherals {
|
||||
* @param <T> The type of the capability we look up.
|
||||
*/
|
||||
private record CapabilityLookup<T>(Capability<T> capability) implements ComponentLookup<InvalidateCallback> {
|
||||
@Nullable
|
||||
@Override
|
||||
public T find(ServerLevel level, BlockPos pos, BlockState state, BlockEntity blockEntity, Direction side, InvalidateCallback invalidate) {
|
||||
public @Nullable T find(ServerLevel level, BlockPos pos, BlockState state, BlockEntity blockEntity, Direction side, InvalidateCallback invalidate) {
|
||||
return CapabilityUtil.unwrap(CapabilityUtil.getCapability(blockEntity, this.capability(), side), invalidate);
|
||||
}
|
||||
}
|
||||
|
||||
public static void registerGenericCapability(Capability<?> capability) {
|
||||
public static void registerGenericCapability(Capability<? extends Object> capability) {
|
||||
Objects.requireNonNull(capability, "Capability cannot be null");
|
||||
registerGenericLookup(new CapabilityLookup<>(capability));
|
||||
}
|
||||
|
||||
+3
-3
@@ -29,17 +29,17 @@ import java.util.function.Supplier;
|
||||
*/
|
||||
public final class CapabilityProvider<T> implements ICapabilityProvider {
|
||||
private final Capability<T> cap;
|
||||
private final Supplier<T> supplier;
|
||||
private final Supplier<@Nullable T> supplier;
|
||||
private final BooleanSupplier isRemoved;
|
||||
private @Nullable LazyOptional<T> instance;
|
||||
|
||||
private CapabilityProvider(Capability<T> cap, Supplier<T> supplier, BooleanSupplier isRemoved) {
|
||||
private CapabilityProvider(Capability<T> cap, Supplier<@Nullable T> supplier, BooleanSupplier isRemoved) {
|
||||
this.cap = Objects.requireNonNull(cap, "Capability cannot be null");
|
||||
this.supplier = supplier;
|
||||
this.isRemoved = isRemoved;
|
||||
}
|
||||
|
||||
public static <T> CapabilityProvider<T> attach(AttachCapabilitiesEvent<?> event, ResourceLocation id, Capability<T> cap, Supplier<T> instance) {
|
||||
public static <T> CapabilityProvider<T> attach(AttachCapabilitiesEvent<?> event, ResourceLocation id, Capability<T> cap, Supplier<@Nullable T> instance) {
|
||||
BooleanSupplier isRemoved
|
||||
= event.getObject() instanceof BlockEntity be ? be::isRemoved
|
||||
: event.getObject() instanceof Entity entity ? entity::isRemoved
|
||||
|
||||
@@ -4,12 +4,14 @@
|
||||
|
||||
package cc.tweaked.standalone;
|
||||
|
||||
import dan200.computercraft.core.util.Nullability;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL45C;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
@@ -79,6 +81,12 @@ public class GLObjects implements AutoCloseable {
|
||||
return texture;
|
||||
}
|
||||
|
||||
private InputStream getResource(String path) throws IOException {
|
||||
var stream = Nullability.assertNonNull(getClass().getClassLoader()).getResourceAsStream(path);
|
||||
if (stream == null) throw new IOException("Cannot find " + path);
|
||||
return stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a texture, loading it from an image.
|
||||
*
|
||||
@@ -88,8 +96,7 @@ public class GLObjects implements AutoCloseable {
|
||||
*/
|
||||
public int loadTexture(String path) throws IOException {
|
||||
BufferedImage image;
|
||||
try (var stream = getClass().getClassLoader().getResourceAsStream(path)) {
|
||||
if (stream == null) throw new IOException("Cannot find " + path);
|
||||
try (var stream = getResource(path)) {
|
||||
image = ImageIO.read(stream);
|
||||
}
|
||||
|
||||
@@ -122,8 +129,7 @@ public class GLObjects implements AutoCloseable {
|
||||
*/
|
||||
public int compileShader(int type, String path) throws IOException {
|
||||
String contents;
|
||||
try (var stream = getClass().getClassLoader().getResourceAsStream(path)) {
|
||||
if (stream == null) throw new IOException("Could not load shader " + path);
|
||||
try (var stream = getResource(path)) {
|
||||
contents = new String(stream.readAllBytes(), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
|
||||
@@ -127,7 +127,8 @@ public class InputState {
|
||||
continue;
|
||||
}
|
||||
|
||||
files.add(new TransferredFile(path.getFileName().toString(), new ArrayByteChannel(contents)));
|
||||
var name = path.getFileName();
|
||||
files.add(new TransferredFile(name == null ? "unknown" : name.toString(), new ArrayByteChannel(contents)));
|
||||
}
|
||||
|
||||
if (!files.isEmpty()) computer.queueEvent(TransferredFiles.EVENT, new Object[]{ new TransferredFiles(files) });
|
||||
|
||||
@@ -74,7 +74,9 @@ public class Main {
|
||||
var matcher = TermSize.PATTERN.matcher(value);
|
||||
if (!matcher.matches()) throw new ParseException("'" + value + "' is not a valid terminal size.");
|
||||
|
||||
return new TermSize(Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2)));
|
||||
var width = Integer.parseInt(Objects.requireNonNull(matcher.group(1)));
|
||||
var height = Integer.parseInt(Objects.requireNonNull(matcher.group(2)));
|
||||
return new TermSize(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +87,9 @@ public class Main {
|
||||
var matcher = MountPaths.PATTERN.matcher(value);
|
||||
if (!matcher.matches()) throw new ParseException("'" + value + "' is not a mount spec.");
|
||||
|
||||
return new MountPaths(parsePath(matcher.group(1)), matcher.group(2));
|
||||
var src = parsePath(Objects.requireNonNull(matcher.group(1)));
|
||||
var dest = Objects.requireNonNull(matcher.group(2));
|
||||
return new MountPaths(src, dest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.nio.file.StandardCopyOption;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -37,7 +38,7 @@ public class Builder {
|
||||
var input = getPath(scope, "cct.input");
|
||||
var classpath = getPath(scope, "cct.classpath");
|
||||
var output = getFile("cct.output");
|
||||
var version = System.getProperty("cct.version");
|
||||
var version = Objects.requireNonNull(System.getProperty("cct.version"), "cct.version must be present");
|
||||
var minify = Boolean.parseBoolean(System.getProperty("cct.minify", "true"));
|
||||
|
||||
buildClasses(input, classpath, output, minify);
|
||||
@@ -73,7 +74,7 @@ public class Builder {
|
||||
run(output, remapper, TeaVMTargetType.JAVASCRIPT, TeaVMOptimizationLevel.ADVANCED, minify);
|
||||
run(output, remapper, TeaVMTargetType.WEBASSEMBLY_GC, TeaVMOptimizationLevel.SIMPLE, minify);
|
||||
|
||||
try (var runtime = Builder.class.getClassLoader().getResourceAsStream("org/teavm/backend/wasm/wasm-gc-module-runtime.js")) {
|
||||
try (var runtime = Objects.requireNonNull(Builder.class.getClassLoader()).getResourceAsStream("org/teavm/backend/wasm/wasm-gc-module-runtime.js")) {
|
||||
if (runtime == null) throw new IllegalStateException("Cannot find WASM runtime");
|
||||
Files.copy(runtime, output.resolve("wasm-gc-runtime.js"), StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
@@ -143,7 +144,9 @@ public class Builder {
|
||||
}
|
||||
|
||||
private static List<Path> getPath(CloseScope scope, String name) {
|
||||
return Arrays.stream(System.getProperty(name).split(File.pathSeparator))
|
||||
var path = System.getProperty(name);
|
||||
if (path == null) throw new NullPointerException(name + " must be given");
|
||||
return Arrays.stream(path.split(File.pathSeparator))
|
||||
.map(Path::of)
|
||||
.filter(Files::exists)
|
||||
.map(file -> {
|
||||
@@ -157,6 +160,8 @@ public class Builder {
|
||||
}
|
||||
|
||||
private static Path getFile(String name) {
|
||||
return Path.of(System.getProperty(name));
|
||||
var path = System.getProperty(name);
|
||||
if (path == null) throw new NullPointerException(name + " must be given");
|
||||
return Path.of(path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,10 @@ import org.teavm.jso.core.JSString;
|
||||
/**
|
||||
* Utility methods for converting between Java and Javascript representations.
|
||||
*/
|
||||
public class JavascriptConv {
|
||||
public final class JavascriptConv {
|
||||
private JavascriptConv() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an array of Javascript values to an equivalent array of Java values.
|
||||
*
|
||||
@@ -24,7 +27,7 @@ public class JavascriptConv {
|
||||
@Contract("null -> null; !null -> !null")
|
||||
public static @Nullable Object @Nullable [] toJava(@Nullable JSObject @Nullable [] value) {
|
||||
if (value == null) return null;
|
||||
var out = new Object[value.length];
|
||||
var out = new @Nullable Object[value.length];
|
||||
for (var i = 0; i < value.length; i++) out[i] = toJava(value[i]);
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.google.common.reflect.TypeToken;
|
||||
import dan200.computercraft.api.lua.*;
|
||||
import dan200.computercraft.core.asm.Reflect;
|
||||
import dan200.computercraft.core.methods.LuaMethod;
|
||||
import dan200.computercraft.core.util.Nullability;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
@@ -106,7 +107,7 @@ public final class StaticGenerator<T> {
|
||||
}
|
||||
}
|
||||
|
||||
var annotation = method.getAnnotation(LuaFunction.class);
|
||||
var annotation = Nullability.assertNonNull(method.getAnnotation(LuaFunction.class));
|
||||
if (annotation.unsafe() && annotation.mainThread()) {
|
||||
System.err.printf("Lua Method %s cannot use unsafe and mainThread.\n", name);
|
||||
return Optional.empty();
|
||||
|
||||
Reference in New Issue
Block a user