Fix many editor diagnostics (#1707)

This addresses many IDE warnings resulting from the switch to JSpecify.

Also, this changes the behavior of exporting VmObject; there's no place
in our code that does not force a VmObject prior to export, so
the existing logic around handling nullable values has been removed.
This commit is contained in:
Daniel Chao
2026-06-29 09:22:19 -07:00
committed by GitHub
parent 742a8e88da
commit 139d1ae8ec
63 changed files with 208 additions and 130 deletions
@@ -1,5 +1,5 @@
/* /*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. * Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -69,11 +69,11 @@ public record ImportGraph(Map<URI, Set<Import>> imports, Map<URI, URI> resolvedI
var value = entry.getValue(); var value = entry.getValue();
var set = new TreeSet<Import>(); var set = new TreeSet<Import>();
if (!(value instanceof JsArray array)) { if (!(value instanceof JsArray array)) {
throw new FormatException("array", value.getClass()); throw new FormatException("array", value == null ? Void.class : value.getClass());
} }
for (var elem : array) { for (var elem : array) {
if (!(elem instanceof JsObject importObj)) { if (!(elem instanceof JsObject importObj)) {
throw new FormatException("object", elem.getClass()); throw new FormatException("object", elem == null ? Void.class : elem.getClass());
} }
set.add(parseImport(importObj)); set.add(parseImport(importObj));
} }
@@ -98,7 +98,7 @@ public record ImportGraph(Map<URI, Set<Import>> imports, Map<URI, URI> resolvedI
var key = new URI(entry.getKey()); var key = new URI(entry.getKey());
var value = entry.getValue(); var value = entry.getValue();
if (!(value instanceof String str)) { if (!(value instanceof String str)) {
throw new FormatException("string", value.getClass()); throw new FormatException("string", value == null ? Void.class : value.getClass());
} }
var valueUri = new URI(str); var valueUri = new URI(str);
ret.put(key, valueUri); ret.put(key, valueUri);
@@ -109,6 +109,8 @@ final class JsonRenderer implements ValueRenderer {
"Values of type `Bytes` cannot be rendered as JSON. Value: %s", (Object) value)); "Values of type `Bytes` cannot be rendered as JSON. Value: %s", (Object) value));
} }
// Pair coming from the runtime can never admit null (in-language null is represented as PNull)
@SuppressWarnings("DataFlowIssue")
@Override @Override
public void visitPair(Pair<?, ?> value) { public void visitPair(Pair<?, ?> value) {
try { try {
@@ -18,7 +18,6 @@ package org.pkl.core;
import java.net.URI; import java.net.URI;
import java.util.*; import java.util.*;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
import org.pkl.core.util.LateInit;
/** Describes the property, method and class members of a module. */ /** Describes the property, method and class members of a module. */
public final class ModuleSchema { public final class ModuleSchema {
@@ -33,8 +32,8 @@ public final class ModuleSchema {
private final Map<String, TypeAlias> typeAliases; private final Map<String, TypeAlias> typeAliases;
private final Map<String, URI> imports; private final Map<String, URI> imports;
@LateInit private Map<String, PClass> __allClasses; private @Nullable Map<String, PClass> __allClasses;
@LateInit private Map<String, TypeAlias> __allTypeAliases; private @Nullable Map<String, TypeAlias> __allTypeAliases;
/** Constructs a {@code ModuleSchema} instance. */ /** Constructs a {@code ModuleSchema} instance. */
public ModuleSchema( public ModuleSchema(
@@ -1,5 +1,5 @@
/* /*
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. * Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -227,9 +227,7 @@ final class PcfRenderer implements ValueRenderer {
write(currIndent); write(currIndent);
writeIdentifier(name); writeIdentifier(name);
if (value == null) { // unevaluated property if (value instanceof Composite) {
write(" = ?");
} else if (value instanceof Composite) {
write(' '); write(' ');
visit(value); visit(value);
} else { } else {
@@ -1,5 +1,5 @@
/* /*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved. * Copyright © 2025-2026 Apple Inc. and the Pkl project authors. All rights reserved.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -21,7 +21,9 @@ import java.io.UncheckedIOException;
import java.net.URI; import java.net.URI;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.jspecify.annotations.Nullable;
import org.msgpack.core.MessagePack; import org.msgpack.core.MessagePack;
import org.msgpack.core.MessageUnpacker; import org.msgpack.core.MessageUnpacker;
import org.pkl.core.runtime.BaseModule; import org.pkl.core.runtime.BaseModule;
@@ -111,7 +113,7 @@ public class PklBinaryDecoder extends AbstractPklBinaryDecoder {
@Override @Override
protected Object doDecodeMap(MapDecodeIterator iter) { protected Object doDecodeMap(MapDecodeIterator iter) {
var map = CollectionUtils.newLinkedHashMap(iter.getSize()); Map<@Nullable Object, @Nullable Object> map = CollectionUtils.newLinkedHashMap(iter.getSize());
while (iter.hasNext()) { while (iter.hasNext()) {
var entry = iter.next(); var entry = iter.next();
map.put(entry.getFirst(), entry.getSecond()); map.put(entry.getFirst(), entry.getSecond());
@@ -54,6 +54,9 @@ final class PropertiesRenderer implements ValueRenderer {
} else if (value instanceof Map<?, ?> map) { } else if (value instanceof Map<?, ?> map) {
doVisitMap(null, map); doVisitMap(null, map);
} else if (value instanceof Pair<?, ?> pair) { } else if (value instanceof Pair<?, ?> pair) {
// Pair coming from the runtime can never admit null (in-language null is represented as
// PNull)
//noinspection DataFlowIssue
doVisitKeyAndValue(null, pair.getFirst(), pair.getSecond()); doVisitKeyAndValue(null, pair.getFirst(), pair.getSecond());
} else { } else {
throw new RendererException( throw new RendererException(
@@ -1,5 +1,5 @@
/* /*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. * Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -17,7 +17,7 @@ package org.pkl.core;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import org.pkl.core.util.LateInit; import org.jspecify.annotations.Nullable;
/** A type parameter of a generic class, type alias, or method. */ /** A type parameter of a generic class, type alias, or method. */
public final class TypeParameter implements Serializable { public final class TypeParameter implements Serializable {
@@ -27,7 +27,7 @@ public final class TypeParameter implements Serializable {
private final String name; private final String name;
private final int index; private final int index;
@LateInit private volatile Member owner; private volatile @Nullable Member owner;
public TypeParameter(Variance variance, String name, int index) { public TypeParameter(Variance variance, String name, int index) {
this.variance = variance; this.variance = variance;
@@ -48,6 +48,7 @@ public final class TypeParameter implements Serializable {
/** Returns the generic class, type alias, or method that this type parameter belongs to. */ /** Returns the generic class, type alias, or method that this type parameter belongs to. */
public Member getOwner() { public Member getOwner() {
assert owner != null; assert owner != null;
//noinspection DataFlowIssue
return owner; return owner;
} }
@@ -18,7 +18,6 @@ package org.pkl.core;
import java.util.*; import java.util.*;
import java.util.regex.*; import java.util.regex.*;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
import org.pkl.core.util.LateInit;
/** /**
* A <a href="https://semver.org/spec/v2.0.0.html">semantic version</a>. * A <a href="https://semver.org/spec/v2.0.0.html">semantic version</a>.
@@ -59,7 +58,7 @@ public final class Version implements Comparable<Version> {
private final @Nullable String preRelease; private final @Nullable String preRelease;
private final @Nullable String build; private final @Nullable String build;
@LateInit private volatile Identifier[] __preReleaseIdentifiers; private volatile Identifier @Nullable [] __preReleaseIdentifiers;
/** Constructs a semantic version. */ /** Constructs a semantic version. */
public Version( public Version(
@@ -230,6 +229,7 @@ public final class Version implements Comparable<Version> {
: new Identifier(-1, str)) : new Identifier(-1, str))
.toArray(Identifier[]::new); .toArray(Identifier[]::new);
} }
//noinspection DataFlowIssue
return __preReleaseIdentifiers; return __preReleaseIdentifiers;
} }
@@ -1,5 +1,5 @@
/* /*
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved. * Copyright © 2025-2026 Apple Inc. and the Pkl project authors. All rights reserved.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -20,19 +20,19 @@ import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.type.TypeNode; import org.pkl.core.ast.type.TypeNode;
import org.pkl.core.ast.type.TypeNode.UInt8TypeAliasTypeNode; import org.pkl.core.ast.type.TypeNode.UInt8TypeAliasTypeNode;
import org.pkl.core.ast.type.VmTypeMismatchException; import org.pkl.core.ast.type.VmTypeMismatchException;
import org.pkl.core.runtime.VmBytes; import org.pkl.core.runtime.VmBytes;
import org.pkl.core.runtime.VmUtils; import org.pkl.core.runtime.VmUtils;
import org.pkl.core.util.LateInit;
@NodeInfo(shortName = "Bytes()") @NodeInfo(shortName = "Bytes()")
public final class BytesLiteralNode extends ExpressionNode { public final class BytesLiteralNode extends ExpressionNode {
@Children private final ExpressionNode[] elements; @Children private final ExpressionNode[] elements;
@Child @LateInit private TypeNode typeNode; @Child private @Nullable TypeNode typeNode;
public BytesLiteralNode(SourceSection sourceSection, ExpressionNode[] elements) { public BytesLiteralNode(SourceSection sourceSection, ExpressionNode[] elements) {
super(sourceSection); super(sourceSection);
@@ -22,6 +22,7 @@ import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.api.nodes.DirectCallNode;
import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.runtime.Identifier; import org.pkl.core.runtime.Identifier;
import org.pkl.core.runtime.VmObjectLike; import org.pkl.core.runtime.VmObjectLike;
@@ -34,7 +35,7 @@ public abstract sealed class AbstractInvokeMethodLexicalNode extends ExpressionN
protected final int levelsUp; protected final int levelsUp;
private final boolean needsConst; private final boolean needsConst;
@Children private ExpressionNode[] argumentNodes; @Children private ExpressionNode[] argumentNodes;
@Child private DirectCallNode callNode; @Child private @Nullable DirectCallNode callNode;
@CompilationFinal protected boolean isConstChecked; @CompilationFinal protected boolean isConstChecked;
protected AbstractInvokeMethodLexicalNode( protected AbstractInvokeMethodLexicalNode(
@@ -90,6 +91,7 @@ public abstract sealed class AbstractInvokeMethodLexicalNode extends ExpressionN
callNode = DirectCallNode.create(getCallTarget(owner)); callNode = DirectCallNode.create(getCallTarget(owner));
insert(callNode); insert(callNode);
} }
assert callNode != null;
return callNode; return callNode;
} }
} }
@@ -19,17 +19,17 @@ import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.type.TypeNode.UnknownTypeNode; import org.pkl.core.ast.type.TypeNode.UnknownTypeNode;
import org.pkl.core.runtime.*; import org.pkl.core.runtime.*;
import org.pkl.core.util.LateInit;
/** Infers the parent to amend in `function createPerson(): Person = new { ... }`. */ /** Infers the parent to amend in `function createPerson(): Person = new { ... }`. */
public final class InferParentWithinMethodNode extends ExpressionNode { public final class InferParentWithinMethodNode extends ExpressionNode {
private final VmLanguage language; private final VmLanguage language;
private final Identifier methodName; private final Identifier methodName;
@Child private ExpressionNode ownerNode; @Child private @Nullable ExpressionNode ownerNode;
@CompilationFinal @LateInit private Object inferredParent; @CompilationFinal private @Nullable Object inferredParent;
public InferParentWithinMethodNode( public InferParentWithinMethodNode(
SourceSection sourceSection, SourceSection sourceSection,
@@ -52,6 +52,7 @@ public final class InferParentWithinMethodNode extends ExpressionNode {
CompilerDirectives.transferToInterpreter(); CompilerDirectives.transferToInterpreter();
assert ownerNode != null;
var owner = (VmObjectLike) ownerNode.executeGeneric(frame); var owner = (VmObjectLike) ownerNode.executeGeneric(frame);
assert owner.isPrototype(); assert owner.isPrototype();
@@ -19,6 +19,7 @@ import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.member.ObjectMethodNode; import org.pkl.core.ast.member.ObjectMethodNode;
import org.pkl.core.ast.type.TypeNode.UnknownTypeNode; import org.pkl.core.ast.type.TypeNode.UnknownTypeNode;
@@ -26,14 +27,13 @@ import org.pkl.core.runtime.Identifier;
import org.pkl.core.runtime.VmDynamic; import org.pkl.core.runtime.VmDynamic;
import org.pkl.core.runtime.VmLanguage; import org.pkl.core.runtime.VmLanguage;
import org.pkl.core.runtime.VmObjectLike; import org.pkl.core.runtime.VmObjectLike;
import org.pkl.core.util.LateInit;
/** Infers the parent to amend in `obj { local function createPerson(): Person = new { ... } }`. */ /** Infers the parent to amend in `obj { local function createPerson(): Person = new { ... } }`. */
public final class InferParentWithinObjectMethodNode extends ExpressionNode { public final class InferParentWithinObjectMethodNode extends ExpressionNode {
private final VmLanguage language; private final VmLanguage language;
private final Identifier localMethodName; private final Identifier localMethodName;
@Child private ExpressionNode ownerNode; @Child private @Nullable ExpressionNode ownerNode;
@CompilationFinal @LateInit private Object inferredParent; @CompilationFinal private @Nullable Object inferredParent;
public InferParentWithinObjectMethodNode( public InferParentWithinObjectMethodNode(
SourceSection sourceSection, SourceSection sourceSection,
@@ -58,6 +58,7 @@ public final class InferParentWithinObjectMethodNode extends ExpressionNode {
CompilerDirectives.transferToInterpreter(); CompilerDirectives.transferToInterpreter();
assert ownerNode != null;
var owner = (VmObjectLike) ownerNode.executeGeneric(frame); var owner = (VmObjectLike) ownerNode.executeGeneric(frame);
var member = owner.getMember(localMethodName); var member = owner.getMember(localMethodName);
@@ -18,6 +18,7 @@ package org.pkl.core.ast.expression.member;
import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.MemberLookupMode; import org.pkl.core.ast.MemberLookupMode;
import org.pkl.core.ast.expression.primary.GetEnclosingReceiverNode; import org.pkl.core.ast.expression.primary.GetEnclosingReceiverNode;
@@ -49,8 +50,8 @@ public final class ReadAmbiguousLocalityPropertyNode extends ExpressionNode {
private final Identifier name; private final Identifier name;
private final int levelsUp; private final int levelsUp;
private final boolean needsConst; private final boolean needsConst;
private @Child ExpressionNode readLocalPropertyNode; @Child private @Nullable ExpressionNode readLocalPropertyNode;
private @Child ExpressionNode readPropertyNode; @Child private @Nullable ExpressionNode readPropertyNode;
public ReadAmbiguousLocalityPropertyNode( public ReadAmbiguousLocalityPropertyNode(
SourceSection sourceSection, Identifier name, int levelsUp, boolean needsConst) { SourceSection sourceSection, Identifier name, int levelsUp, boolean needsConst) {
@@ -22,6 +22,7 @@ import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.api.nodes.DirectCallNode;
import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable;
import org.pkl.core.PklBugException; import org.pkl.core.PklBugException;
import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.member.ObjectMember; import org.pkl.core.ast.member.ObjectMember;
@@ -34,8 +35,8 @@ public final class ReadLocalPropertyNode extends ExpressionNode {
private final Identifier name; private final Identifier name;
private final int levelsUp; private final int levelsUp;
private final boolean needsConst; private final boolean needsConst;
@Child private DirectCallNode callNode; @Child private @Nullable DirectCallNode callNode;
@CompilationFinal private ObjectMember property; @CompilationFinal @Nullable private ObjectMember property;
public ReadLocalPropertyNode( public ReadLocalPropertyNode(
SourceSection sourceSection, Identifier name, int levelsUp, boolean needsConst) { SourceSection sourceSection, Identifier name, int levelsUp, boolean needsConst) {
@@ -91,6 +92,7 @@ public final class ReadLocalPropertyNode extends ExpressionNode {
callNode = DirectCallNode.create(property.getCallTarget()); callNode = DirectCallNode.create(property.getCallTarget());
insert(callNode); insert(callNode);
} }
assert callNode != null;
return callNode; return callNode;
} }
} }
@@ -23,6 +23,7 @@ import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import org.jspecify.annotations.Nullable;
import org.pkl.core.SecurityManagerException; import org.pkl.core.SecurityManagerException;
import org.pkl.core.ast.member.SharedMemberNode; import org.pkl.core.ast.member.SharedMemberNode;
import org.pkl.core.externalreader.ExternalReaderProcessException; import org.pkl.core.externalreader.ExternalReaderProcessException;
@@ -35,13 +36,12 @@ import org.pkl.core.runtime.VmMapping;
import org.pkl.core.runtime.VmObjectBuilder; import org.pkl.core.runtime.VmObjectBuilder;
import org.pkl.core.util.GlobResolver; import org.pkl.core.util.GlobResolver;
import org.pkl.core.util.GlobResolver.InvalidGlobPatternException; import org.pkl.core.util.GlobResolver.InvalidGlobPatternException;
import org.pkl.core.util.LateInit;
@NodeInfo(shortName = "import*") @NodeInfo(shortName = "import*")
public class ImportGlobNode extends AbstractImportNode { public class ImportGlobNode extends AbstractImportNode {
private final String globPattern; private final String globPattern;
@Child @LateInit private SharedMemberNode memberNode; @Child private @Nullable SharedMemberNode memberNode;
@CompilationFinal @LateInit private VmMapping cachedResult; @CompilationFinal private @Nullable VmMapping cachedResult;
public ImportGlobNode( public ImportGlobNode(
SourceSection sourceSection, SourceSection sourceSection,
@@ -21,6 +21,7 @@ import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import java.net.URI; import java.net.URI;
import org.jspecify.annotations.Nullable;
import org.pkl.core.SecurityManagerException; import org.pkl.core.SecurityManagerException;
import org.pkl.core.http.HttpClientException; import org.pkl.core.http.HttpClientException;
import org.pkl.core.module.ResolvedModuleKey; import org.pkl.core.module.ResolvedModuleKey;
@@ -28,13 +29,12 @@ import org.pkl.core.packages.PackageLoadError;
import org.pkl.core.runtime.VmContext; import org.pkl.core.runtime.VmContext;
import org.pkl.core.runtime.VmLanguage; import org.pkl.core.runtime.VmLanguage;
import org.pkl.core.runtime.VmTyped; import org.pkl.core.runtime.VmTyped;
import org.pkl.core.util.LateInit;
@NodeInfo(shortName = "import") @NodeInfo(shortName = "import")
public final class ImportNode extends AbstractImportNode { public final class ImportNode extends AbstractImportNode {
private final VmLanguage language; private final VmLanguage language;
@CompilationFinal @LateInit private VmTyped importedModule; @CompilationFinal private @Nullable VmTyped importedModule;
public ImportNode( public ImportNode(
VmLanguage language, VmLanguage language,
@@ -24,6 +24,7 @@ import com.oracle.truffle.api.source.SourceSection;
import java.io.IOException; import java.io.IOException;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import org.graalvm.collections.EconomicMap; import org.graalvm.collections.EconomicMap;
import org.jspecify.annotations.Nullable;
import org.pkl.core.SecurityManagerException; import org.pkl.core.SecurityManagerException;
import org.pkl.core.ast.member.SharedMemberNode; import org.pkl.core.ast.member.SharedMemberNode;
import org.pkl.core.externalreader.ExternalReaderProcessException; import org.pkl.core.externalreader.ExternalReaderProcessException;
@@ -36,12 +37,11 @@ import org.pkl.core.runtime.VmObjectBuilder;
import org.pkl.core.util.GlobResolver; import org.pkl.core.util.GlobResolver;
import org.pkl.core.util.GlobResolver.InvalidGlobPatternException; import org.pkl.core.util.GlobResolver.InvalidGlobPatternException;
import org.pkl.core.util.IoUtils; import org.pkl.core.util.IoUtils;
import org.pkl.core.util.LateInit;
@NodeInfo(shortName = "read*") @NodeInfo(shortName = "read*")
public abstract class ReadGlobNode extends AbstractReadNode { public abstract class ReadGlobNode extends AbstractReadNode {
private final EconomicMap<String, VmMapping> cachedResults = EconomicMap.create(); private final EconomicMap<String, VmMapping> cachedResults = EconomicMap.create();
@Child @LateInit private SharedMemberNode memberNode; @Child private @Nullable SharedMemberNode memberNode;
protected ReadGlobNode(SourceSection sourceSection, ModuleKey currentModule) { protected ReadGlobNode(SourceSection sourceSection, ModuleKey currentModule) {
super(sourceSection, currentModule); super(sourceSection, currentModule);
@@ -67,6 +67,7 @@ public abstract class ReadGlobNode extends AbstractReadNode {
@TruffleBoundary @TruffleBoundary
public Object read(String globPattern) { public Object read(String globPattern) {
var cachedResult = cachedResults.get(globPattern); var cachedResult = cachedResults.get(globPattern);
//noinspection ConstantValue
if (cachedResult != null) return cachedResult; if (cachedResult != null) return cachedResult;
// use same check as for globbed imports (see AstBuilder) // use same check as for globbed imports (see AstBuilder)
@@ -26,6 +26,7 @@ import org.pkl.core.TypeParameter;
import org.pkl.core.ast.VmModifier; import org.pkl.core.ast.VmModifier;
import org.pkl.core.ast.type.TypeNode; import org.pkl.core.ast.type.TypeNode;
import org.pkl.core.runtime.*; import org.pkl.core.runtime.*;
import org.pkl.core.util.LateInit;
public final class ClassMethod extends ClassMember { public final class ClassMethod extends ClassMember {
private final List<TypeParameter> typeParameters; private final List<TypeParameter> typeParameters;
@@ -33,7 +34,7 @@ public final class ClassMethod extends ClassMember {
// null = not deprecated, "" = no/empty message in the @Deprecated body // null = not deprecated, "" = no/empty message in the @Deprecated body
private final @Nullable String deprecation; private final @Nullable String deprecation;
@CompilationFinal private FunctionNode functionNode; @CompilationFinal @LateInit private FunctionNode functionNode;
public ClassMethod( public ClassMethod(
SourceSection sourceSection, SourceSection sourceSection,
@@ -61,6 +62,7 @@ public final class ClassMethod extends ClassMember {
} }
public void initFunctionNode(FunctionNode functionNode) { public void initFunctionNode(FunctionNode functionNode) {
//noinspection ConstantValue
assert this.functionNode == null; assert this.functionNode == null;
this.functionNode = functionNode; this.functionNode = functionNode;
} }
@@ -84,6 +84,7 @@ public final class ClassNode extends ExpressionNode {
// Caching of classes also guarantees that classes are singletons and can be compared by // Caching of classes also guarantees that classes are singletons and can be compared by
// identity, // identity,
// which improves efficiency and performance (for example in shape checks). // which improves efficiency and performance (for example in shape checks).
//noinspection ConstantValue
if (cachedClass != null) return cachedClass; if (cachedClass != null) return cachedClass;
CompilerDirectives.transferToInterpreter(); CompilerDirectives.transferToInterpreter();
@@ -18,7 +18,6 @@ package org.pkl.core.ast.member;
import com.oracle.truffle.api.frame.FrameDescriptor; import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.PklRootNode; import org.pkl.core.ast.PklRootNode;
import org.pkl.core.ast.type.TypeNode; import org.pkl.core.ast.type.TypeNode;
import org.pkl.core.runtime.VmLanguage; import org.pkl.core.runtime.VmLanguage;
@@ -46,7 +45,7 @@ public final class ListingOrMappingTypeCastNode extends PklRootNode {
} }
@Override @Override
public @Nullable String getName() { public String getName() {
return qualifiedName; return qualifiedName;
} }
@@ -23,12 +23,11 @@ import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.type.TypeNode; import org.pkl.core.ast.type.TypeNode;
import org.pkl.core.ast.type.UnresolvedTypeNode; import org.pkl.core.ast.type.UnresolvedTypeNode;
import org.pkl.core.runtime.VmLanguage; import org.pkl.core.runtime.VmLanguage;
import org.pkl.core.util.LateInit;
public final class LocalTypedPropertyNode extends RegularMemberNode { public final class LocalTypedPropertyNode extends RegularMemberNode {
private final VmLanguage language; private final VmLanguage language;
@Child private UnresolvedTypeNode unresolvedTypeNode; @Child private @Nullable UnresolvedTypeNode unresolvedTypeNode;
@Child @LateInit private TypeNode typeNode; @Child private @Nullable TypeNode typeNode;
private @Nullable Object defaultValue; private @Nullable Object defaultValue;
private boolean defaultValueInitialized; private boolean defaultValueInitialized;
@@ -47,6 +46,7 @@ public final class LocalTypedPropertyNode extends RegularMemberNode {
public @Nullable Object getDefaultValue(VirtualFrame frame) { public @Nullable Object getDefaultValue(VirtualFrame frame) {
if (!defaultValueInitialized) { if (!defaultValueInitialized) {
assert typeNode != null;
defaultValue = defaultValue =
typeNode.createDefaultValue( typeNode.createDefaultValue(
frame, language, member.getHeaderSection(), member.getQualifiedName()); frame, language, member.getHeaderSection(), member.getQualifiedName());
@@ -59,6 +59,7 @@ public final class LocalTypedPropertyNode extends RegularMemberNode {
protected Object executeImpl(VirtualFrame frame) { protected Object executeImpl(VirtualFrame frame) {
if (typeNode == null) { if (typeNode == null) {
CompilerDirectives.transferToInterpreter(); CompilerDirectives.transferToInterpreter();
assert unresolvedTypeNode != null;
typeNode = insert(unresolvedTypeNode.execute(frame)); typeNode = insert(unresolvedTypeNode.execute(frame));
unresolvedTypeNode = null; unresolvedTypeNode = null;
} }
@@ -25,7 +25,6 @@ import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.type.TypeNode; import org.pkl.core.ast.type.TypeNode;
import org.pkl.core.ast.type.UnresolvedTypeNode; import org.pkl.core.ast.type.UnresolvedTypeNode;
import org.pkl.core.runtime.*; import org.pkl.core.runtime.*;
import org.pkl.core.util.LateInit;
public final class ObjectMethodNode extends RegularMemberNode { public final class ObjectMethodNode extends RegularMemberNode {
private final VmLanguage language; private final VmLanguage language;
@@ -33,7 +32,7 @@ public final class ObjectMethodNode extends RegularMemberNode {
@Children private final @Nullable UnresolvedTypeNode[] unresolvedParameterTypeNodes; @Children private final @Nullable UnresolvedTypeNode[] unresolvedParameterTypeNodes;
@Child private @Nullable UnresolvedTypeNode unresolvedReturnTypeNode; @Child private @Nullable UnresolvedTypeNode unresolvedReturnTypeNode;
@CompilationFinal @LateInit private FunctionNode functionNode; @CompilationFinal private @Nullable FunctionNode functionNode;
public ObjectMethodNode( public ObjectMethodNode(
VmLanguage language, VmLanguage language,
@@ -19,6 +19,7 @@ import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.runtime.*; import org.pkl.core.runtime.*;
import org.pkl.core.util.LateInit; import org.pkl.core.util.LateInit;
@@ -26,7 +27,7 @@ import org.pkl.core.util.LateInit;
/** Resolves `<type>` to the type's default value in `new <type> { ... }`. */ /** Resolves `<type>` to the type's default value in `new <type> { ... }`. */
public final class GetParentForTypeNode extends ExpressionNode { public final class GetParentForTypeNode extends ExpressionNode {
@Child private UnresolvedTypeNode unresolvedTypeNode; @Child private UnresolvedTypeNode unresolvedTypeNode;
@Child private TypeNode typeNode; @Child private @Nullable TypeNode typeNode;
private final String qualifiedName; private final String qualifiedName;
@CompilationFinal @LateInit Object defaultValue; @CompilationFinal @LateInit Object defaultValue;
@@ -49,6 +50,7 @@ public final class GetParentForTypeNode extends ExpressionNode {
@Override @Override
public Object executeGeneric(VirtualFrame frame) { public Object executeGeneric(VirtualFrame frame) {
//noinspection ConstantValue
if (defaultValue != null) return defaultValue; if (defaultValue != null) return defaultValue;
CompilerDirectives.transferToInterpreterAndInvalidate(); CompilerDirectives.transferToInterpreterAndInvalidate();
@@ -1,5 +1,5 @@
/* /*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. * Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -19,14 +19,14 @@ import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.util.LateInit;
@NodeInfo(shortName = "as") @NodeInfo(shortName = "as")
public final class TypeCastNode extends ExpressionNode { public final class TypeCastNode extends ExpressionNode {
@Child private ExpressionNode valueNode; @Child private ExpressionNode valueNode;
@Child private UnresolvedTypeNode unresolvedTypeNode; @Child private @Nullable UnresolvedTypeNode unresolvedTypeNode;
@Child @LateInit private TypeNode typeNode; @Child private @Nullable TypeNode typeNode;
public TypeCastNode( public TypeCastNode(
SourceSection sourceSection, SourceSection sourceSection,
@@ -43,6 +43,7 @@ public final class TypeCastNode extends ExpressionNode {
// don't compile unresolvedTypeNode.execute() // don't compile unresolvedTypeNode.execute()
// invalidation is done by insert() // invalidation is done by insert()
CompilerDirectives.transferToInterpreter(); CompilerDirectives.transferToInterpreter();
assert unresolvedTypeNode != null;
typeNode = insert(unresolvedTypeNode.execute(frame)); typeNode = insert(unresolvedTypeNode.execute(frame));
unresolvedTypeNode = null; unresolvedTypeNode = null;
} }
@@ -239,7 +239,7 @@ public abstract class TypeNode extends PklNode {
public abstract static class FrameSlotTypeNode extends TypeNode { public abstract static class FrameSlotTypeNode extends TypeNode {
@CompilationFinal protected int slot = -1; @CompilationFinal protected int slot = -1;
@CompilationFinal @Child protected WriteFrameSlotNode writeFrameSlotNode; @CompilationFinal @Child protected @Nullable WriteFrameSlotNode writeFrameSlotNode;
protected FrameSlotTypeNode(SourceSection sourceSection) { protected FrameSlotTypeNode(SourceSection sourceSection) {
super(sourceSection); super(sourceSection);
@@ -26,7 +26,7 @@ import org.pkl.core.runtime.VmLanguage;
@NodeInfo(shortName = "is") @NodeInfo(shortName = "is")
public final class TypeTestNode extends ExpressionNode { public final class TypeTestNode extends ExpressionNode {
@Child private ExpressionNode valueNode; @Child private ExpressionNode valueNode;
@Child private UnresolvedTypeNode unresolvedTypeNode; @Child private @Nullable UnresolvedTypeNode unresolvedTypeNode;
@Child private @Nullable TypeNode typeNode; @Child private @Nullable TypeNode typeNode;
public TypeTestNode( public TypeTestNode(
@@ -49,6 +49,7 @@ public final class TypeTestNode extends ExpressionNode {
// don't compile unresolvedTypeNode.execute() // don't compile unresolvedTypeNode.execute()
// invalidation is done by insert() // invalidation is done by insert()
CompilerDirectives.transferToInterpreter(); CompilerDirectives.transferToInterpreter();
assert unresolvedTypeNode != null;
typeNode = insert(unresolvedTypeNode.execute(frame)); typeNode = insert(unresolvedTypeNode.execute(frame));
unresolvedTypeNode = null; unresolvedTypeNode = null;
} }
@@ -103,7 +103,7 @@ final class ExternalResourceResolverImpl implements ExternalResourceResolver {
readResponses.computeIfAbsent( readResponses.computeIfAbsent(
baseUri, baseUri,
(uri) -> { (uri) -> {
var future = new CompletableFuture<byte[]>(); var future = new CompletableFuture<byte @Nullable []>();
var request = var request =
new ReadResourceRequest(requestIdGenerator.nextLong(), evaluatorId, uri); new ReadResourceRequest(requestIdGenerator.nextLong(), evaluatorId, uri);
try { try {
@@ -46,7 +46,6 @@ import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException; import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.TrustManagerFactory;
import org.pkl.core.SecurityManagerException;
import org.pkl.core.util.ErrorMessages; import org.pkl.core.util.ErrorMessages;
import org.pkl.core.util.Exceptions; import org.pkl.core.util.Exceptions;
@@ -93,7 +92,7 @@ final class JdkHttpClient implements HttpClient {
HttpRequest request, HttpRequest request,
BodyHandler<T> responseBodyHandler, BodyHandler<T> responseBodyHandler,
HttpRequestChecker httpRequestChecker) HttpRequestChecker httpRequestChecker)
throws IOException, SecurityManagerException { throws IOException {
try { try {
return underlying.send(request, responseBodyHandler); return underlying.send(request, responseBodyHandler);
} catch (ConnectException e) { } catch (ConnectException e) {
@@ -84,7 +84,7 @@ public final class Messages {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(@Nullable Object o) {
if (this == o) { if (this == o) {
return true; return true;
} }
@@ -93,12 +93,14 @@ public class PathElement {
} }
/** Returns the element at path {@code basePath}, given a {@link Path}. */ /** Returns the element at path {@code basePath}, given a {@link Path}. */
@SuppressWarnings("DataFlowIssue") // incorrect analysis; this can return null.
public @Nullable TreePathElement getElement(Path basePath) { public @Nullable TreePathElement getElement(Path basePath) {
var path = basePath.normalize(); var path = basePath.normalize();
var element = this; var element = this;
for (var i = 0; i < path.getNameCount(); i++) { for (var i = 0; i < path.getNameCount(); i++) {
var part = path.getName(i).toString(); var part = path.getName(i).toString();
element = element.getChildren().get(part); element = element.getChildren().get(part);
//noinspection ConstantValue
if (element == null) { if (element == null) {
return null; return null;
} }
@@ -183,6 +183,7 @@ public final class ProjectDependenciesManager {
public Map<String, Dependency> getLocalPackageDependencies(PackageUri packageUri) { public Map<String, Dependency> getLocalPackageDependencies(PackageUri packageUri) {
ensureDependenciesInitialized(); ensureDependenciesInitialized();
var dep = localPackageDependencies.get(packageUri); var dep = localPackageDependencies.get(packageUri);
//noinspection ConstantValue
assert dep != null; assert dep != null;
return dep; return dep;
} }
@@ -67,7 +67,7 @@ public abstract class Dependency {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(@Nullable Object o) {
if (this == o) { if (this == o) {
return true; return true;
} }
@@ -107,7 +107,7 @@ public abstract class Dependency {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(@Nullable Object o) {
if (this == o) { if (this == o) {
return true; return true;
} }
@@ -217,6 +217,7 @@ public final class DependencyMetadata {
var map = new HashMap<>(); var map = new HashMap<>();
for (var kv : value) { for (var kv : value) {
var kvObj = (JsObject) kv; var kvObj = (JsObject) kv;
assert kvObj != null;
map.put(parsePObject(kvObj.get("key")), parsePObject(kvObj.get("value"))); map.put(parsePObject(kvObj.get("key")), parsePObject(kvObj.get("value")));
} }
return map; return map;
@@ -294,7 +295,7 @@ public final class DependencyMetadata {
var ret = new ArrayList<String>(arr.size()); var ret = new ArrayList<String>(arr.size());
for (var elem : arr) { for (var elem : arr) {
if (!(elem instanceof String string)) { if (!(elem instanceof String string)) {
throw new FormatException("string", elem.getClass()); throw new FormatException("string", elem != null ? elem.getClass() : Void.class);
} }
ret.add(string); ret.add(string);
} }
@@ -414,7 +415,7 @@ public final class DependencyMetadata {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(@Nullable Object o) {
if (this == o) { if (this == o) {
return true; return true;
} }
@@ -624,8 +625,8 @@ public final class DependencyMetadata {
jsonWriter.endObject(); jsonWriter.endObject();
} }
private void writeGenericObject(Object value) throws IOException { private void writeGenericObject(@Nullable Object value) throws IOException {
if (value instanceof PNull) { if (value == null || value instanceof PNull) {
jsonWriter.nullValue(); jsonWriter.nullValue();
} else if (value instanceof PObject pObject) { } else if (value instanceof PObject pObject) {
writePObject(pObject); writePObject(pObject);
@@ -18,6 +18,7 @@ package org.pkl.core.packages;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.nio.file.Path; import java.nio.file.Path;
import org.jspecify.annotations.Nullable;
import org.pkl.core.PklBugException; import org.pkl.core.PklBugException;
import org.pkl.core.Version; import org.pkl.core.Version;
import org.pkl.core.util.ErrorMessages; import org.pkl.core.util.ErrorMessages;
@@ -98,7 +99,7 @@ public final class PackageAssetUri {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(@Nullable Object o) {
if (this == o) { if (this == o) {
return true; return true;
} }
@@ -118,7 +118,7 @@ public final class PackageUri {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(@Nullable Object o) {
if (this == o) { if (this == o) {
return true; return true;
} }
@@ -1,5 +1,5 @@
/* /*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. * Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@ package org.pkl.core.project;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import org.jspecify.annotations.Nullable;
import org.pkl.core.PklBugException; import org.pkl.core.PklBugException;
import org.pkl.core.packages.PackageUri; import org.pkl.core.packages.PackageUri;
import org.pkl.core.util.ErrorMessages; import org.pkl.core.util.ErrorMessages;
@@ -84,7 +85,7 @@ public record CanonicalPackageUri(URI baseUri, int majorVersion) {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(@Nullable Object o) {
if (this == o) { if (this == o) {
return true; return true;
} }
@@ -199,7 +199,7 @@ public final class Project {
sb.append("\n│"); sb.append("\n│");
} }
sb.append("\n│ "); sb.append("\n│ ");
sb.append(uri.toString()); sb.append(uri);
} }
sb.append("\n└─"); sb.append("\n└─");
} }
@@ -477,7 +477,7 @@ public final class Project {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(@Nullable Object o) {
if (this == o) { if (this == o) {
return true; return true;
} }
@@ -611,7 +611,7 @@ public final class Project {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(@Nullable Object o) {
if (this == o) { if (this == o) {
return true; return true;
} }
@@ -141,6 +141,7 @@ public final class ProjectDependenciesResolver {
private void updateDependency(Dependency dependency) { private void updateDependency(Dependency dependency) {
var canonicalPackageUri = CanonicalPackageUri.fromPackageUri(dependency.getPackageUri()); var canonicalPackageUri = CanonicalPackageUri.fromPackageUri(dependency.getPackageUri());
var currentDependency = resolvedDependencies.get(canonicalPackageUri); var currentDependency = resolvedDependencies.get(canonicalPackageUri);
//noinspection ConstantValue
if (currentDependency == null if (currentDependency == null
|| currentDependency.getVersion().compareTo(dependency.getVersion()) < 0) { || currentDependency.getVersion().compareTo(dependency.getVersion()) < 0) {
EconomicMaps.put(resolvedDependencies, canonicalPackageUri, dependency); EconomicMaps.put(resolvedDependencies, canonicalPackageUri, dependency);
@@ -135,6 +135,7 @@ public final class ProjectDeps {
} }
/** Given a declared dependency, return the resolved dependency. */ /** Given a declared dependency, return the resolved dependency. */
@SuppressWarnings("DataFlowIssue") // incorrect analysis
public @Nullable Dependency get(CanonicalPackageUri canonicalPackageUri) { public @Nullable Dependency get(CanonicalPackageUri canonicalPackageUri) {
return resolvedDependencies.get(canonicalPackageUri); return resolvedDependencies.get(canonicalPackageUri);
} }
@@ -150,7 +151,7 @@ public final class ProjectDeps {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(@Nullable Object o) {
if (this == o) { if (this == o) {
return true; return true;
} }
@@ -51,6 +51,8 @@ public final class FileSystemManager {
public static synchronized FileSystem getFileSystem(URI uri) throws IOException { public static synchronized FileSystem getFileSystem(URI uri) throws IOException {
var fs = fileSystems.get(uri); var fs = fileSystems.get(uri);
// incorrect nullability for `org.graalvm.collections.UnmodifiableEconomicMap`
//noinspection ConstantValue
if (fs != null) { if (fs != null) {
var count = counts.get(fs); var count = counts.get(fs);
assert count != null; assert count != null;
@@ -40,10 +40,10 @@ public final class ModuleInfo {
@LateInit private List<VmTyped> annotations; @LateInit private List<VmTyped> annotations;
@LateInit private VmTyped __mirror; private @Nullable VmTyped __mirror;
private final Object mirrorLock = new Object(); private final Object mirrorLock = new Object();
@LateInit private ModuleSchema __moduleSchema; private @Nullable ModuleSchema __moduleSchema;
private final Object moduleSchemaLock = new Object(); private final Object moduleSchemaLock = new Object();
public ModuleInfo( public ModuleInfo(
@@ -65,12 +65,12 @@ public final class ModuleInfo {
} }
public void initAnnotations(List<VmTyped> annotations) { public void initAnnotations(List<VmTyped> annotations) {
//noinspection ConstantValue
assert this.annotations == null; assert this.annotations == null;
this.annotations = annotations; this.annotations = annotations;
} }
public List<VmTyped> getAnnotations() { public List<VmTyped> getAnnotations() {
assert annotations != null;
return annotations; return annotations;
} }
@@ -101,8 +101,14 @@ public final class StackTraceRenderer {
if (hint != null) { if (hint != null) {
out.append(AnsiTheme.ERROR_MESSAGE_HINT, hint); out.append(AnsiTheme.ERROR_MESSAGE_HINT, hint);
} else { } else {
assert hintBuilder != null; out.append(
out.append(AnsiTheme.ERROR_MESSAGE_HINT, () -> hintBuilder.accept(out, true)); AnsiTheme.ERROR_MESSAGE_HINT,
() -> {
// nullaway needs this assertion
//noinspection ConstantValue
assert hintBuilder != null;
hintBuilder.accept(out, true);
});
} }
out.append('\n'); out.append('\n');
} }
@@ -491,11 +491,11 @@ public final class TestRunner {
sb.append("\n Expected: "); sb.append("\n Expected: ");
appendLocation(sb, expectedLocation); appendLocation(sb, expectedLocation);
sb.append("\n "); sb.append("\n ");
sb.append(AnsiTheme.TEST_EXAMPLE_OUTPUT, expectedValue.replaceAll("\n", "\n ")); sb.append(AnsiTheme.TEST_EXAMPLE_OUTPUT, expectedValue.replace("\n", "\n "));
sb.append("\n Actual: "); sb.append("\n Actual: ");
appendLocation(sb, actualLocation); appendLocation(sb, actualLocation);
sb.append("\n "); sb.append("\n ");
sb.append(AnsiTheme.TEST_EXAMPLE_OUTPUT, actualValue.replaceAll("\n", "\n ")); sb.append(AnsiTheme.TEST_EXAMPLE_OUTPUT, actualValue.replace("\n", "\n "));
}); });
return new Failure("Example Failure", sb.toString()); return new Failure("Example Failure", sb.toString());
} }
@@ -107,6 +107,7 @@ public final class VmContext {
} }
public void initialize(Holder holder) { public void initialize(Holder holder) {
//noinspection ConstantValue
assert this.holder == null; assert this.holder == null;
this.holder = holder; this.holder = holder;
} }
@@ -19,6 +19,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.frame.MaterializedFrame; import com.oracle.truffle.api.frame.MaterializedFrame;
import java.util.Objects; import java.util.Objects;
import org.graalvm.collections.UnmodifiableEconomicMap; import org.graalvm.collections.UnmodifiableEconomicMap;
import org.jspecify.annotations.Nullable;
import org.pkl.core.PClassInfo; import org.pkl.core.PClassInfo;
import org.pkl.core.PObject; import org.pkl.core.PObject;
import org.pkl.core.ast.member.ObjectMember; import org.pkl.core.ast.member.ObjectMember;
@@ -75,12 +76,14 @@ public final class VmDynamic extends VmObject {
@Override @Override
@TruffleBoundary @TruffleBoundary
public PObject export() { public PObject export() {
assert forced : "Value was not forced prior to export";
var properties = var properties =
CollectionUtils.<String, Object>newLinkedHashMap(EconomicMaps.size(cachedValues)); CollectionUtils.<String, Object>newLinkedHashMap(EconomicMaps.size(cachedValues));
iterateMemberValues( iterateAlreadyForcedMemberValues(
(key, member, value) -> { (key, member, value) -> {
properties.put(key.toString(), VmValue.exportNullable(value)); properties.put(key.toString(), VmValue.export(value));
return true; return true;
}); });
@@ -99,9 +102,8 @@ public final class VmDynamic extends VmObject {
@Override @Override
@TruffleBoundary @TruffleBoundary
public boolean equals(Object obj) { public boolean equals(@Nullable Object obj) {
if (this == obj) // noinspection Contract if (this == obj) return true;
return true;
if (!(obj instanceof VmDynamic other)) return false; if (!(obj instanceof VmDynamic other)) return false;
// could use shallow force, but deep force is cached // could use shallow force, but deep force is cached
@@ -115,6 +117,7 @@ public final class VmDynamic extends VmObject {
if (isHiddenOrLocalProperty(key)) continue; if (isHiddenOrLocalProperty(key)) continue;
var value = cursor.getValue(); var value = cursor.getValue();
//noinspection ConstantValue
assert value != null; assert value != null;
var otherValue = other.getCachedValue(key); var otherValue = other.getCachedValue(key);
if (!value.equals(otherValue)) return false; if (!value.equals(otherValue)) return false;
@@ -137,6 +140,7 @@ public final class VmDynamic extends VmObject {
if (isHiddenOrLocalProperty(key)) continue; if (isHiddenOrLocalProperty(key)) continue;
var value = cursor.getValue(); var value = cursor.getValue();
//noinspection ConstantValue
assert value != null; assert value != null;
result += key.hashCode() ^ value.hashCode(); result += key.hashCode() ^ value.hashCode();
} }
@@ -87,13 +87,15 @@ public final class VmListing extends VmListingOrMapping {
@Override @Override
@TruffleBoundary @TruffleBoundary
public List<Object> export() { public List<Object> export() {
assert forced : "Value was not forced prior to export";
var properties = new ArrayList<>(EconomicMaps.size(cachedValues)); var properties = new ArrayList<>(EconomicMaps.size(cachedValues));
iterateMemberValues( iterateAlreadyForcedMemberValues(
(key, prop, value) -> { (key, prop, value) -> {
if (isDefaultProperty(key)) return true; if (isDefaultProperty(key)) return true;
properties.add(VmValue.exportNullable(value)); properties.add(VmValue.export(value));
return true; return true;
}); });
@@ -127,6 +129,7 @@ public final class VmListing extends VmListingOrMapping {
if (key instanceof Identifier) continue; if (key instanceof Identifier) continue;
var value = cursor.getValue(); var value = cursor.getValue();
//noinspection ConstantValue
assert value != null; assert value != null;
var otherValue = other.getCachedValue(key); var otherValue = other.getCachedValue(key);
if (!value.equals(otherValue)) return false; if (!value.equals(otherValue)) return false;
@@ -149,6 +152,7 @@ public final class VmListing extends VmListingOrMapping {
if (key instanceof Identifier) continue; if (key instanceof Identifier) continue;
var value = cursor.getValue(); var value = cursor.getValue();
//noinspection ConstantValue
assert value != null; assert value != null;
result = 31 * result + value.hashCode(); result = 31 * result + value.hashCode();
} }
@@ -147,6 +147,7 @@ public final class VmMap extends VmValue implements Iterable<Map.Entry<Object, O
for (var key : other.keyOrder) { for (var key : other.keyOrder) {
var value = other.map.get(key); var value = other.map.get(key);
assert value != null;
if (!map.containsKey(key)) { if (!map.containsKey(key)) {
keyOrderBuilder.append(key); keyOrderBuilder.append(key);
@@ -21,19 +21,18 @@ import com.oracle.truffle.api.frame.MaterializedFrame;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import org.graalvm.collections.UnmodifiableEconomicMap; import org.graalvm.collections.UnmodifiableEconomicMap;
import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.member.ListingOrMappingTypeCastNode; import org.pkl.core.ast.member.ListingOrMappingTypeCastNode;
import org.pkl.core.ast.member.ObjectMember; import org.pkl.core.ast.member.ObjectMember;
import org.pkl.core.util.CollectionUtils; import org.pkl.core.util.CollectionUtils;
import org.pkl.core.util.EconomicMaps; import org.pkl.core.util.EconomicMaps;
import org.pkl.core.util.LateInit;
import org.pkl.core.util.MutableLong; import org.pkl.core.util.MutableLong;
public final class VmMapping extends VmListingOrMapping { public final class VmMapping extends VmListingOrMapping {
private long cachedLength = -1; private long cachedLength = -1;
@GuardedBy("this") @GuardedBy("this")
@LateInit private @Nullable VmSet __allKeys;
private VmSet __allKeys;
private static final class EmptyHolder { private static final class EmptyHolder {
private static final VmMapping EMPTY = private static final VmMapping EMPTY =
@@ -94,13 +93,15 @@ public final class VmMapping extends VmListingOrMapping {
@Override @Override
@TruffleBoundary @TruffleBoundary
public Map<Object, Object> export() { public Map<Object, Object> export() {
assert forced : "Value was not forced prior to export";
var properties = CollectionUtils.newLinkedHashMap(EconomicMaps.size(cachedValues)); var properties = CollectionUtils.newLinkedHashMap(EconomicMaps.size(cachedValues));
iterateMemberValues( iterateAlreadyForcedMemberValues(
(key, prop, value) -> { (key, prop, value) -> {
if (isDefaultProperty(key)) return true; if (isDefaultProperty(key)) return true;
properties.put(VmValue.export(key), VmValue.exportNullable(value)); properties.put(VmValue.export(key), VmValue.export(value));
return true; return true;
}); });
@@ -119,9 +120,8 @@ public final class VmMapping extends VmListingOrMapping {
@Override @Override
@TruffleBoundary @TruffleBoundary
public boolean equals(Object obj) { public boolean equals(@Nullable Object obj) {
if (this == obj) // noinspection Contract if (this == obj) return true;
return true;
if (!(obj instanceof VmMapping other)) return false; if (!(obj instanceof VmMapping other)) return false;
// could use shallow force, but deep force is cached // could use shallow force, but deep force is cached
@@ -135,6 +135,7 @@ public final class VmMapping extends VmListingOrMapping {
if (key instanceof Identifier) continue; if (key instanceof Identifier) continue;
var value = cursor.getValue(); var value = cursor.getValue();
//noinspection ConstantValue
assert value != null; assert value != null;
var otherValue = other.getCachedValue(key); var otherValue = other.getCachedValue(key);
if (!value.equals(otherValue)) return false; if (!value.equals(otherValue)) return false;
@@ -157,6 +158,7 @@ public final class VmMapping extends VmListingOrMapping {
if (key instanceof Identifier) continue; if (key instanceof Identifier) continue;
var value = cursor.getValue(); var value = cursor.getValue();
//noinspection ConstantValue
assert value != null; assert value != null;
result += key.hashCode() ^ value.hashCode(); result += key.hashCode() ^ value.hashCode();
} }
@@ -34,7 +34,7 @@ public abstract class VmObject extends VmObjectLike {
protected final EconomicMap<Object, Object> cachedValues; protected final EconomicMap<Object, Object> cachedValues;
protected int cachedHash; protected int cachedHash;
private boolean forced; protected boolean forced;
public VmObject( public VmObject(
MaterializedFrame enclosingFrame, MaterializedFrame enclosingFrame,
@@ -204,17 +204,20 @@ public abstract class VmObject extends VmObjectLike {
/** /**
* Exports this object's members. Skips local members, hidden members, class definitions, and type * Exports this object's members. Skips local members, hidden members, class definitions, and type
* aliases. Members that haven't been forced have a `null` value. * aliases.
*
* <p>Assumes that this object has already been forced.
*/ */
@TruffleBoundary @TruffleBoundary
protected final Map<String, Object> exportMembers() { protected final Map<String, Object> exportMembers() {
var result = CollectionUtils.<String, Object>newLinkedHashMap(EconomicMaps.size(cachedValues)); Map<String, Object> result = CollectionUtils.newLinkedHashMap(EconomicMaps.size(cachedValues));
assert forced : "Value was not forced prior to export";
iterateMemberValues( iterateAlreadyForcedMemberValues(
(key, member, value) -> { (key, member, value) -> {
if (member.isClass() || member.isTypeAlias()) return true; if (member.isClass() || member.isTypeAlias()) return true;
result.put(key.toString(), VmValue.exportNullable(value)); result.put(key.toString(), VmValue.export(value));
return true; return true;
}); });
@@ -30,6 +30,7 @@ import org.pkl.core.Composite;
import org.pkl.core.PClass; import org.pkl.core.PClass;
import org.pkl.core.PClassInfo; import org.pkl.core.PClassInfo;
import org.pkl.core.PType; import org.pkl.core.PType;
import org.pkl.core.PklBugException;
import org.pkl.core.Reference; import org.pkl.core.Reference;
import org.pkl.core.TypeAlias; import org.pkl.core.TypeAlias;
import org.pkl.core.util.paguro.RrbTree; import org.pkl.core.util.paguro.RrbTree;
@@ -218,6 +219,15 @@ public final class VmReference extends VmValue {
normalizeTypes(prop.getType(), clazz.getPClass().getModuleClass(), result); normalizeTypes(prop.getType(), clazz.getPClass().getModuleClass(), result);
} }
private static PClassInfo<?> getClassInfo(Object value) {
if (value instanceof VmValue vmValue) return vmValue.getVmClass().getPClassInfo();
if (value instanceof String) return PClassInfo.String;
if (value instanceof Boolean) return PClassInfo.Boolean;
if (value instanceof Long) return PClassInfo.Int;
if (value instanceof Double) return PClassInfo.Float;
throw new PklBugException("Not a Pkl value: " + value.getClass());
}
@SuppressWarnings("DuplicatedCode") @SuppressWarnings("DuplicatedCode")
private static void getCandidateSubscriptType(PType type, Object key, Set<PType> result) { private static void getCandidateSubscriptType(PType type, Object key, Set<PType> result) {
if (type == PType.UNKNOWN) { if (type == PType.UNKNOWN) {
@@ -244,8 +254,7 @@ public final class VmReference extends VmValue {
var keyTypes = normalizeTypes(typeArgs.get(0), clazz.getPClass().getModuleClass()); var keyTypes = normalizeTypes(typeArgs.get(0), clazz.getPClass().getModuleClass());
for (var kt : iterateTypes(keyTypes)) { for (var kt : iterateTypes(keyTypes)) {
if (kt == PType.UNKNOWN if (kt == PType.UNKNOWN
|| (kt instanceof PType.Class klazz || (kt instanceof PType.Class klazz && klazz.getPClass().getInfo() == getClassInfo(key))
&& klazz.getPClass().getInfo() == PClassInfo.forValue(VmValue.export(key)))
|| (kt instanceof PType.StringLiteral stringLiteral || (kt instanceof PType.StringLiteral stringLiteral
&& stringLiteral.getLiteral().equals(key))) { && stringLiteral.getLiteral().equals(key))) {
normalizeTypes(typeArgs.get(1), clazz.getPClass().getModuleClass(), result); normalizeTypes(typeArgs.get(1), clazz.getPClass().getModuleClass(), result);
@@ -34,7 +34,6 @@ import org.pkl.core.ast.type.TypeNode;
import org.pkl.core.ast.type.TypeNode.ConstrainedTypeNode; import org.pkl.core.ast.type.TypeNode.ConstrainedTypeNode;
import org.pkl.core.ast.type.TypeNode.TypeVariableNode; import org.pkl.core.ast.type.TypeNode.TypeVariableNode;
import org.pkl.core.ast.type.TypeNode.UnknownTypeNode; import org.pkl.core.ast.type.TypeNode.UnknownTypeNode;
import org.pkl.core.util.LateInit;
public final class VmTypeAlias extends VmValue { public final class VmTypeAlias extends VmValue {
private final SourceSection sourceSection; private final SourceSection sourceSection;
@@ -48,17 +47,15 @@ public final class VmTypeAlias extends VmValue {
private final List<TypeParameter> typeParameters; private final List<TypeParameter> typeParameters;
private final MaterializedFrame enclosingFrame; private final MaterializedFrame enclosingFrame;
@LateInit private TypeNode typeNode; private @Nullable TypeNode typeNode;
@LateInit
@GuardedBy("pTypeAliasLock") @GuardedBy("pTypeAliasLock")
private TypeAlias __pTypeAlias; private @Nullable TypeAlias __pTypeAlias;
private final Object pTypeAliasLock = new Object(); private final Object pTypeAliasLock = new Object();
@LateInit
@GuardedBy("mirrorLock") @GuardedBy("mirrorLock")
private VmTyped __mirror; private @Nullable VmTyped __mirror;
private final Object mirrorLock = new Object(); private final Object mirrorLock = new Object();
@@ -86,7 +83,6 @@ public final class VmTypeAlias extends VmValue {
} }
public void initTypeCheckNode(TypeNode typeNode) { public void initTypeCheckNode(TypeNode typeNode) {
assert this.typeNode == null;
this.typeNode = typeNode; this.typeNode = typeNode;
} }
@@ -100,6 +96,7 @@ public final class VmTypeAlias extends VmValue {
*/ */
@TruffleBoundary @TruffleBoundary
public SourceSection getBaseTypeSection() { public SourceSection getBaseTypeSection() {
assert typeNode != null;
if (typeNode instanceof ConstrainedTypeNode constrainedTypeNode) { if (typeNode instanceof ConstrainedTypeNode constrainedTypeNode) {
return constrainedTypeNode.getBaseTypeSection(); return constrainedTypeNode.getBaseTypeSection();
} }
@@ -116,6 +113,7 @@ public final class VmTypeAlias extends VmValue {
*/ */
@TruffleBoundary @TruffleBoundary
public SourceSection getConstraintSection() { public SourceSection getConstraintSection() {
assert typeNode != null;
if (typeNode instanceof ConstrainedTypeNode) { if (typeNode instanceof ConstrainedTypeNode) {
return ((ConstrainedTypeNode) typeNode).getFirstConstraintSection(); return ((ConstrainedTypeNode) typeNode).getFirstConstraintSection();
} }
@@ -169,6 +167,7 @@ public final class VmTypeAlias extends VmValue {
} }
public TypeNode getTypeNode() { public TypeNode getTypeNode() {
assert typeNode != null;
return typeNode; return typeNode;
} }
@@ -178,6 +177,7 @@ public final class VmTypeAlias extends VmValue {
@TruffleBoundary @TruffleBoundary
public TypeNode instantiate(TypeNode[] typeArgumentNodes) { public TypeNode instantiate(TypeNode[] typeArgumentNodes) {
assert typeNode != null;
// Cloning the type node means that the entire type check remains within a single root node, // Cloning the type node means that the entire type check remains within a single root node,
// which should be good for interpreted and compiled performance alike: // which should be good for interpreted and compiled performance alike:
// * Fewer root nodes to call // * Fewer root nodes to call
@@ -265,6 +265,7 @@ public final class VmTypeAlias extends VmValue {
} }
public VmTyped getTypeMirror() { public VmTyped getTypeMirror() {
assert typeNode != null;
return typeNode.getMirror(); return typeNode.getMirror();
} }
@@ -29,10 +29,9 @@ import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.expression.unary.ImportNode; import org.pkl.core.ast.expression.unary.ImportNode;
import org.pkl.core.ast.member.ObjectMember; import org.pkl.core.ast.member.ObjectMember;
import org.pkl.core.util.EconomicMaps; import org.pkl.core.util.EconomicMaps;
import org.pkl.core.util.LateInit;
public final class VmTyped extends VmObject { public final class VmTyped extends VmObject {
@CompilationFinal @LateInit private VmClass clazz; @CompilationFinal private @Nullable VmClass clazz;
public VmTyped( public VmTyped(
MaterializedFrame enclosingFrame, MaterializedFrame enclosingFrame,
@@ -162,6 +161,8 @@ public final class VmTyped extends VmObject {
@Override @Override
@TruffleBoundary @TruffleBoundary
public Composite export() { public Composite export() {
assert forced : "Value was not forced prior to export";
assert clazz != null;
if (!isModuleObject()) { if (!isModuleObject()) {
return new PObject(clazz.getPClassInfo(), exportMembers()); return new PObject(clazz.getPClassInfo(), exportMembers());
} }
@@ -190,6 +191,7 @@ public final class VmTyped extends VmObject {
if (this == obj) return true; if (this == obj) return true;
if (!(obj instanceof VmTyped other)) return false; if (!(obj instanceof VmTyped other)) return false;
assert clazz != null;
if (clazz != other.clazz) return false; if (clazz != other.clazz) return false;
// could use shallow force, but deep force is cached // could use shallow force, but deep force is cached
force(false); force(false);
@@ -210,6 +212,7 @@ public final class VmTyped extends VmObject {
public int hashCode() { public int hashCode() {
if (cachedHash != 0) return cachedHash; if (cachedHash != 0) return cachedHash;
assert clazz != null;
force(false); force(false);
var result = 0; var result = 0;
@@ -94,6 +94,7 @@ public final class VmObjectFactory<E> {
? TypeCheckedPropertyNodeGen.create(null, new FrameDescriptor(), member, bodyNode) ? TypeCheckedPropertyNodeGen.create(null, new FrameDescriptor(), member, bodyNode)
: new UntypedObjectMemberNode(null, new FrameDescriptor(), member, bodyNode); : new UntypedObjectMemberNode(null, new FrameDescriptor(), member, bodyNode);
member.initMemberNode(node); member.initMemberNode(node);
//noinspection ConstantValue
if (members.put(identifier, member) != null) { if (members.put(identifier, member) != null) {
throw new VmExceptionBuilder() throw new VmExceptionBuilder()
.bug( .bug(
@@ -110,7 +111,6 @@ public final class VmObjectFactory<E> {
@TruffleBoundary @TruffleBoundary
public VmTyped create(@Nullable E extraStorage) { public VmTyped create(@Nullable E extraStorage) {
var clazz = classSupplier.get(); var clazz = classSupplier.get();
assert clazz != null;
var result = var result =
new VmTyped(VmUtils.createEmptyMaterializedFrame(), clazz.getPrototype(), clazz, members); new VmTyped(VmUtils.createEmptyMaterializedFrame(), clazz.getPrototype(), clazz, members);
@@ -46,7 +46,7 @@ import org.pkl.core.stdlib.ExternalPropertyNode;
* CLI args) would lead to incorrect behavior, as a single base module instance is shared across all * CLI args) would lead to incorrect behavior, as a single base module instance is shared across all
* language contexts, and properties (whether external or not) are evaluated just once. * language contexts, and properties (whether external or not) are evaluated just once.
*/ */
@SuppressWarnings("UnusedParameters") @SuppressWarnings("unused")
public final class BaseNodes { public final class BaseNodes {
private BaseNodes() {} private BaseNodes() {}
@@ -64,6 +64,7 @@ public final class BaseNodes {
} }
} }
@SuppressWarnings("unused")
public abstract static class Regex extends ExternalMethod1Node { public abstract static class Regex extends ExternalMethod1Node {
// cache Regex object to avoid repeated java.util.Pattern.compile() // cache Regex object to avoid repeated java.util.Pattern.compile()
@Specialization(guards = "pattern.equals(cachedPattern)") @Specialization(guards = "pattern.equals(cachedPattern)")
@@ -20,6 +20,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.LoopNode; import com.oracle.truffle.api.nodes.LoopNode;
import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.expression.binary.*; import org.pkl.core.ast.expression.binary.*;
import org.pkl.core.ast.internal.IsInstanceOfNode; import org.pkl.core.ast.internal.IsInstanceOfNode;
import org.pkl.core.ast.internal.IsInstanceOfNodeGen; import org.pkl.core.ast.internal.IsInstanceOfNodeGen;
@@ -33,7 +34,6 @@ import org.pkl.core.stdlib.base.CollectionNodes.CompareByNode;
import org.pkl.core.stdlib.base.CollectionNodes.CompareNode; import org.pkl.core.stdlib.base.CollectionNodes.CompareNode;
import org.pkl.core.stdlib.base.CollectionNodes.CompareWithNode; import org.pkl.core.stdlib.base.CollectionNodes.CompareWithNode;
import org.pkl.core.util.EconomicSets; import org.pkl.core.util.EconomicSets;
import org.pkl.core.util.LateInit;
// duplication between ListNodes and SetNodes is "intentional" // duplication between ListNodes and SetNodes is "intentional"
// (sharing nodes between VmCollection subtypes results in // (sharing nodes between VmCollection subtypes results in
@@ -1336,7 +1336,7 @@ public final class ListNodes {
public abstract static class toBytes extends ExternalMethod0Node { public abstract static class toBytes extends ExternalMethod0Node {
@Child @LateInit private TypeNode typeNode; @Child private @Nullable TypeNode typeNode;
private TypeNode getTypeNode() { private TypeNode getTypeNode() {
if (typeNode == null) { if (typeNode == null) {
@@ -28,6 +28,7 @@ import org.pkl.core.runtime.*;
import org.pkl.core.stdlib.ExternalMethod1Node; import org.pkl.core.stdlib.ExternalMethod1Node;
import org.pkl.core.stdlib.PklConverter; import org.pkl.core.stdlib.PklConverter;
import org.pkl.core.util.EconomicMaps; import org.pkl.core.util.EconomicMaps;
import org.pkl.core.util.LateInit;
import org.pkl.core.util.json.JsonHandler; import org.pkl.core.util.json.JsonHandler;
import org.pkl.core.util.json.JsonParser; import org.pkl.core.util.json.JsonParser;
import org.pkl.core.util.json.ParseException; import org.pkl.core.util.json.ParseException;
@@ -71,6 +72,8 @@ public final class ParserNodes {
private final Deque<Object> currPath = new ArrayDeque<>(); private final Deque<Object> currPath = new ArrayDeque<>();
@LateInit private Object value;
public Handler(PklConverter converter, boolean useMapping) { public Handler(PklConverter converter, boolean useMapping) {
this.converter = converter; this.converter = converter;
this.useMapping = useMapping; this.useMapping = useMapping;
@@ -78,8 +81,6 @@ public final class ParserNodes {
currPath.push(VmValueConverter.TOP_LEVEL_VALUE); currPath.push(VmValueConverter.TOP_LEVEL_VALUE);
} }
private Object value;
@Override @Override
public void endNull() { public void endNull() {
value = VmNull.withoutDefault(); value = VmNull.withoutDefault();
@@ -617,7 +617,7 @@ public final class RendererNodes {
// - All element types are resolved also. // - All element types are resolved also.
// - All string literal types are combined into a single String case. // - All string literal types are combined into a single String case.
var hasString = false; var hasString = false;
var elements = new ArrayList<TypeNode>(); var elements = new ArrayList<@Nullable TypeNode>();
for (var t : ((UnionTypeNode) type).getElementTypeNodes()) { for (var t : ((UnionTypeNode) type).getElementTypeNodes()) {
var resolved = resolveType(t); var resolved = resolveType(t);
if (resolved instanceof StringLiteralTypeNode || resolved instanceof StringTypeNode) { if (resolved instanceof StringLiteralTypeNode || resolved instanceof StringTypeNode) {
@@ -21,6 +21,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.graalvm.collections.EconomicMap; import org.graalvm.collections.EconomicMap;
import org.jspecify.annotations.Nullable;
import org.pkl.core.TestResults; import org.pkl.core.TestResults;
import org.pkl.core.TestResults.Error; import org.pkl.core.TestResults.Error;
import org.pkl.core.TestResults.TestResult; import org.pkl.core.TestResults.TestResult;
@@ -59,8 +60,6 @@ public final class JUnitReporter implements TestReporter {
var totalTests = allTestResults.stream().mapToLong(TestResults::totalTests).sum(); var totalTests = allTestResults.stream().mapToLong(TestResults::totalTests).sum();
var totalFailures = allTestResults.stream().mapToLong(TestResults::totalFailures).sum(); var totalFailures = allTestResults.stream().mapToLong(TestResults::totalFailures).sum();
assert aggregateSuiteName != null;
var attrs = var attrs =
buildAttributes( buildAttributes(
"name", aggregateSuiteName, "name", aggregateSuiteName,
@@ -206,12 +205,15 @@ public final class JUnitReporter implements TestReporter {
members.size() - 4); members.size() - 4);
} }
private VmMapping buildAttributes(Object... attributes) { private VmMapping buildAttributes(@Nullable Object... attributes) {
EconomicMap<Object, ObjectMember> attrs = EconomicMaps.create(attributes.length); EconomicMap<Object, ObjectMember> attrs = EconomicMaps.create(attributes.length);
for (int i = 0; i < attributes.length; i += 2) { for (var i = 0; i < attributes.length; i += 2) {
attrs.put( var key = attributes[i];
attributes[i], var value = attributes[i + 1];
VmUtils.createSyntheticObjectEntry(attributes[i].toString(), attributes[i + 1])); if (key == null || value == null) {
continue;
}
attrs.put(key, VmUtils.createSyntheticObjectEntry(key.toString(), value));
} }
return new VmMapping( return new VmMapping(
VmUtils.createEmptyMaterializedFrame(), BaseModule.getMappingClass().getPrototype(), attrs); VmUtils.createEmptyMaterializedFrame(), BaseModule.getMappingClass().getPrototype(), attrs);
@@ -1,5 +1,5 @@
/* /*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. * Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@ package org.pkl.core.util;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import org.jspecify.annotations.Nullable;
public final class CollectionUtils { public final class CollectionUtils {
private static final float LOAD_FACTOR = 0.75f; private static final float LOAD_FACTOR = 0.75f;
@@ -45,7 +46,8 @@ public final class CollectionUtils {
} }
@TruffleBoundary @TruffleBoundary
public static <K, V> LinkedHashMap<K, V> newLinkedHashMap(int expectedSize) { public static <K extends @Nullable Object, V extends @Nullable Object>
LinkedHashMap<K, V> newLinkedHashMap(int expectedSize) {
return new LinkedHashMap<>((int) (expectedSize / LOAD_FACTOR) + 1, LOAD_FACTOR); return new LinkedHashMap<>((int) (expectedSize / LOAD_FACTOR) + 1, LOAD_FACTOR);
} }
@@ -81,11 +81,13 @@ public final class EconomicMaps {
return result; return result;
} }
@SuppressWarnings("DataFlowIssue")
@TruffleBoundary @TruffleBoundary
public static <K, V> @Nullable V get(UnmodifiableEconomicMap<K, V> self, K key) { public static <K, V> @Nullable V get(UnmodifiableEconomicMap<K, V> self, K key) {
return self.get(key); return self.get(key);
} }
@SuppressWarnings("DataFlowIssue")
@TruffleBoundary @TruffleBoundary
public static <K, V> @Nullable V get(UnmodifiableEconomicMap<K, V> self, K key, V defaultValue) { public static <K, V> @Nullable V get(UnmodifiableEconomicMap<K, V> self, K key, V defaultValue) {
return self.get(key, defaultValue); return self.get(key, defaultValue);
@@ -1,5 +1,5 @@
/* /*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. * Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -381,6 +381,7 @@ public final class JsonParser {
} }
private void startCapture() { private void startCapture() {
//noinspection ConstantValue
if (captureBuffer == null) { if (captureBuffer == null) {
captureBuffer = new StringBuilder(); captureBuffer = new StringBuilder();
} }
@@ -25,7 +25,7 @@ import org.jspecify.annotations.Nullable;
/** /**
* Writes a JSON (<a href="http://www.ietf.org/rfc/rfc7159.txt">RFC 7159</a>) encoded value to a * Writes a JSON (<a href="http://www.ietf.org/rfc/rfc7159.txt">RFC 7159</a>) encoded value to a
* stream, one token at a time. The stream includes both literal values (strings, numbers, booleans * stream, one token at a time. The stream includes both literal values (strings, numbers, booleans
* and nulls) as well as the begin and end delimiters of objects and arrays. * and nulls) as well as the beginning and ending delimiters of objects and arrays.
* *
* <h3>Encoding JSON</h3> * <h3>Encoding JSON</h3>
* *
@@ -195,7 +195,7 @@ public final class JsonWriter implements Closeable, Flushable {
/** /**
* Sets the indentation string to be repeated for each level of indentation in the encoded * Sets the indentation string to be repeated for each level of indentation in the encoded
* document. If {@code indent.isEmpty()} the encoded document will be compact. Otherwise the * document. If {@code indent.isEmpty()} the encoded document will be compact. Otherwise, the
* encoded document will be more human-readable. * encoded document will be more human-readable.
* *
* @param indent a string containing only whitespace. * @param indent a string containing only whitespace.
@@ -418,6 +418,8 @@ public final class JsonWriter implements Closeable, Flushable {
public JsonWriter value(boolean value) throws IOException { public JsonWriter value(boolean value) throws IOException {
writeDeferredName(); writeDeferredName();
beforeValue(); beforeValue();
// avoid method dispatch
//noinspection SimplifiableConditionalExpression
out.write(value ? "true" : "false"); out.write(value ? "true" : "false");
return this; return this;
} }
@@ -433,6 +435,8 @@ public final class JsonWriter implements Closeable, Flushable {
} }
writeDeferredName(); writeDeferredName();
beforeValue(); beforeValue();
// avoid method dispatch
//noinspection SimplifiableConditionalExpression
out.write(value ? "true" : "false"); out.write(value ? "true" : "false");
return this; return this;
} }
@@ -922,7 +922,10 @@ public abstract class RrbTree<E> implements BaseList<E>, Indented {
/** {@inheritDoc} */ /** {@inheritDoc} */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public boolean equals(Object other) { public boolean equals(@Nullable Object other) {
if (other == null) {
return false;
}
if (this == other) { if (this == other) {
return true; return true;
} }