Fix bug where reusing a pklbinary#Renderer could result in incorrect output (#1525)

This commit is contained in:
Jen Basch
2026-04-15 23:12:36 -07:00
committed by GitHub
parent a8500b6b03
commit eeb0970dc4
2 changed files with 107 additions and 54 deletions
@@ -24,37 +24,24 @@ import org.pkl.core.runtime.VmPklBinaryEncoder;
import org.pkl.core.runtime.VmTyped;
import org.pkl.core.stdlib.ExternalMethod1Node;
import org.pkl.core.stdlib.PklConverter;
import org.pkl.core.util.Nullable;
public final class RendererNodes {
private abstract static class RenderMethod extends ExternalMethod1Node {
private @Nullable MessageBufferPacker messagePacker;
protected MessageBufferPacker getMessagePacker() {
if (messagePacker == null) {
messagePacker = MessagePack.newDefaultBufferPacker();
}
messagePacker.clear();
return messagePacker;
}
}
public abstract static class renderDocument extends RenderMethod {
public abstract static class renderDocument extends ExternalMethod1Node {
@Specialization
@TruffleBoundary
protected VmBytes eval(VmTyped self, Object value) {
var packer = getMessagePacker();
var packer = MessagePack.newDefaultBufferPacker();
createRenderer(self, packer).renderDocument(value);
return new VmBytes(packer.toByteArray());
}
}
public abstract static class renderValue extends RenderMethod {
public abstract static class renderValue extends ExternalMethod1Node {
@Specialization
@TruffleBoundary
protected VmBytes eval(VmTyped self, Object value) {
var packer = getMessagePacker();
var packer = MessagePack.newDefaultBufferPacker();
createRenderer(self, packer).renderValue(value);
return new VmBytes(packer.toByteArray());
}
@@ -24,6 +24,7 @@ import java.nio.file.Path
import java.util.*
import java.util.concurrent.TimeUnit
import java.util.regex.Pattern
import kotlin.io.encoding.Base64
import kotlin.io.path.createParentDirectories
import kotlin.io.path.writeText
import org.assertj.core.api.Assertions.assertThat
@@ -45,6 +46,8 @@ import org.pkl.core.module.ModuleKeyFactories
import org.pkl.core.module.ModuleKeyFactory
import org.pkl.core.module.ResolvedModuleKey
import org.pkl.core.project.Project
import org.pkl.core.resource.Resource
import org.pkl.core.resource.ResourceReader
import org.pkl.core.util.IoUtils
class EvaluatorTest {
@@ -434,8 +437,9 @@ class EvaluatorTest {
}
val evaluator = evaluatorBuilder.setProjectDependencies(project.dependencies).build()
val output =
evaluator.use { it.evaluateOutputText(uri("custom:/org/pkl/core/project/project5/main.pkl")) }
val output = evaluator.use {
it.evaluateOutputText(uri("custom:/org/pkl/core/project/project5/main.pkl"))
}
assertThat(output)
.isEqualTo(
"""
@@ -629,6 +633,68 @@ class EvaluatorTest {
assertThat((evaluator as EvaluatorImpl).isInstrumentationEverUsed()).isFalse
}
@Test
fun `nested pkl-binary rendiring produces correct results`() {
val evaluator =
with(EvaluatorBuilder.preconfigured()) {
allowedResources.add(Pattern.compile("b64:"))
addResourceReader(
object : ResourceReader {
override fun getUriScheme() = "b64"
override fun hasHierarchicalUris() = false
override fun isGlobbable() = false
override fun read(uri: URI): Optional<in Any> {
val req = PklBinaryDecoder.decode(Base64.decode(uri.schemeSpecificPart)) as PObject
val kind = req.getProperty("kind") as String
return Optional.of(
Resource(
uri,
when (kind) {
"enc" ->
Base64.encode((req.getProperty("input") as String).encodeToByteArray())
.encodeToByteArray()
"dec" -> Base64.decode(req.getProperty("input") as String)
else -> throw RuntimeException("unknown request kind $kind")
},
)
)
}
}
)
build()
}
evaluator.use {
it.evaluate(
text(
"""
import "pkl:pklbinary"
abstract class Base {
fixed kind: String
input: String
local encodedRequest = new pklbinary.Renderer {}.renderValue(this).base64
hidden fixed requestUri: String = "b64:\(encodedRequest)"
hidden fixed result: Resource = read(requestUri) as Resource
}
class Enc extends Base {
fixed kind: "enc"
}
class Dec extends Base {
fixed kind: "dec"
}
local enc = new Enc { input = "hello world" }
local dec = new Dec { input = enc.result.text }
valid = if (enc.input == dec.result.text) true else throw("pklbinary.Renderer reuse bug")
"""
.trimIndent()
)
)
}
}
private fun checkModule(module: PModule) {
assertThat(module.properties.size).isEqualTo(2)
assertThat(module.getProperty("name")).isEqualTo("pigeon")