From ba883a4f7e411714c05cc6ce7032232004bb5db3 Mon Sep 17 00:00:00 2001 From: translatenix <119817707+translatenix@users.noreply.github.com> Date: Wed, 24 Apr 2024 11:14:56 -0700 Subject: [PATCH] Fix caching of globbed reads Problem: The result of a globbed read depends not only on the glob pattern but also on the current module URI. However, ResourceManager does not take this into account when caching globbed reads, causing wrong results. Changes: - Cache globbed reads per read expression. This is also how globbed imports are cached, except that import URIs are constant. - Make ReadGlobNode and ImportGlobNode code as similar as possible. - Reduce code duplication by inheriting from AbstractReadNode. - Add some tests. --- .../expression/unary/AbstractImportNode.java | 7 +- .../expression/unary/AbstractReadNode.java | 25 ++-- .../unary/ImportGlobElementNode.java | 5 +- .../ast/expression/unary/ImportGlobNode.java | 71 +++++----- .../core/ast/expression/unary/ImportNode.java | 4 +- .../expression/unary/ReadGlobElementNode.java | 2 +- .../ast/expression/unary/ReadGlobNode.java | 70 ++++++---- .../org/pkl/core/runtime/ResourceManager.java | 76 ++-------- .../input-helper/basic/read/child/module2.pkl | 2 + .../basic/read/child/resource.txt | 1 + .../input-helper/basic/read/module1.pkl | 2 + .../input-helper/basic/read/resource.txt | 1 + .../LanguageSnippetTests/input/basic/read.pkl | 22 ++- .../input/basic/readGlob.pkl | 17 ++- .../output/basic/read.pcf | 132 ++++++++---------- .../output/basic/readGlob.pcf | 24 +++- 16 files changed, 231 insertions(+), 230 deletions(-) create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input-helper/basic/read/child/module2.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input-helper/basic/read/child/resource.txt create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input-helper/basic/read/module1.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input-helper/basic/read/resource.txt diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/unary/AbstractImportNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/unary/AbstractImportNode.java index 054382b57..ae3fc06f2 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/unary/AbstractImportNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/unary/AbstractImportNode.java @@ -18,16 +18,19 @@ import com.oracle.truffle.api.source.SourceSection; import java.net.URI; import org.pkl.core.ast.ExpressionNode; +import org.pkl.core.module.ResolvedModuleKey; public abstract class AbstractImportNode extends ExpressionNode { + protected final ResolvedModuleKey currentModule; protected final URI importUri; - AbstractImportNode(SourceSection sourceSection, URI importUri) { + AbstractImportNode(SourceSection sourceSection, ResolvedModuleKey currentModule, URI importUri) { super(sourceSection); + this.currentModule = currentModule; this.importUri = importUri; } - public URI getImportUri() { + public final URI getImportUri() { return importUri; } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/unary/AbstractReadNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/unary/AbstractReadNode.java index 0e5c891d2..9d6a1ce49 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/unary/AbstractReadNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/unary/AbstractReadNode.java @@ -30,30 +30,33 @@ import org.pkl.core.util.Nullable; public abstract class AbstractReadNode extends UnaryExpressionNode { - private final ModuleKey moduleKey; + protected final ModuleKey currentModule; - protected AbstractReadNode(SourceSection sourceSection, ModuleKey moduleKey) { + protected AbstractReadNode(SourceSection sourceSection, ModuleKey currentModule) { super(sourceSection); - this.moduleKey = moduleKey; + this.currentModule = currentModule; } @TruffleBoundary - protected @Nullable Object doRead(String resourceUri, VmContext context, Node readNode) { - var resolvedUri = resolveResource(moduleKey, resourceUri); - return context.getResourceManager().read(resolvedUri, readNode).orElse(null); - } - - private URI resolveResource(ModuleKey moduleKey, String resourceUri) { - URI parsedUri; + protected final URI parseUri(String resourceUri) { try { - parsedUri = IoUtils.toUri(resourceUri); + return IoUtils.toUri(resourceUri); } catch (URISyntaxException e) { throw exceptionBuilder() .evalError("invalidResourceUri", resourceUri) .withHint(e.getReason()) .build(); } + } + @TruffleBoundary + protected final @Nullable Object doRead(String resourceUri, VmContext context, Node readNode) { + var resolvedUri = resolveResource(currentModule, resourceUri); + return context.getResourceManager().read(resolvedUri, readNode).orElse(null); + } + + private URI resolveResource(ModuleKey moduleKey, String resourceUri) { + var parsedUri = parseUri(resourceUri); var context = VmContext.get(this); URI resolvedUri; try { diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/unary/ImportGlobElementNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/unary/ImportGlobElementNode.java index 8873ac775..93cc3de15 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/unary/ImportGlobElementNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/unary/ImportGlobElementNode.java @@ -36,7 +36,8 @@ public final class ImportGlobElementNode extends ExpressionNode { private final VmLanguage language; private final ResolvedModuleKey currentModule; - public ImportGlobElementNode(SourceSection sourceSection, VmLanguage language, ResolvedModuleKey currentModule) { + public ImportGlobElementNode( + SourceSection sourceSection, VmLanguage language, ResolvedModuleKey currentModule) { super(sourceSection); this.language = language; this.currentModule = currentModule; @@ -48,7 +49,7 @@ public Object executeGeneric(VirtualFrame frame) { var path = (String) VmUtils.getMemberKey(frame); return importModule(mapping, path); } - + @TruffleBoundary private VmTyped importModule(VmObjectLike mapping, String path) { @SuppressWarnings("unchecked") diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/unary/ImportGlobNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/unary/ImportGlobNode.java index 2893e14b3..f9fba61d9 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/unary/ImportGlobNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/unary/ImportGlobNode.java @@ -38,11 +38,10 @@ @NodeInfo(shortName = "import*") public class ImportGlobNode extends AbstractImportNode { - private final ResolvedModuleKey currentModule; private final String globPattern; private final SharedMemberNode importGlobElementNode; - @CompilationFinal @LateInit private VmMapping importsMapping; + @CompilationFinal @LateInit private VmMapping cachedResult; public ImportGlobNode( VmLanguage language, @@ -50,8 +49,7 @@ public ImportGlobNode( ResolvedModuleKey currentModule, URI importUri, String globPattern) { - super(sourceSection, importUri); - this.currentModule = currentModule; + super(sourceSection, currentModule, importUri); this.globPattern = globPattern; importGlobElementNode = new SharedMemberNode( @@ -65,42 +63,41 @@ public ImportGlobNode( @Override public Object executeGeneric(VirtualFrame frame) { - if (importsMapping == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - var context = VmContext.get(this); - try { - var moduleKey = context.getModuleResolver().resolve(importUri); - var securityManager = VmContext.get(this).getSecurityManager(); - if (!moduleKey.isGlobbable()) { - throw exceptionBuilder() - .evalError("cannotGlobUri", importUri, importUri.getScheme()) - .build(); - } - var resolvedElements = - GlobResolver.resolveGlob( - securityManager, - moduleKey, - currentModule.getOriginal(), - currentModule.getUri(), - globPattern); - var builder = new VmObjectBuilder(); - for (var entry : resolvedElements.entrySet()) { - builder.addEntry(entry.getKey(), importGlobElementNode); - } - importsMapping = builder.toMapping(resolvedElements); - } catch (IOException e) { - throw exceptionBuilder().evalError("ioErrorResolvingGlob", importUri).withCause(e).build(); - } catch (SecurityManagerException | HttpClientInitException e) { - throw exceptionBuilder().withCause(e).build(); - } catch (PackageLoadError e) { - throw exceptionBuilder().adhocEvalError(e.getMessage()).build(); - } catch (InvalidGlobPatternException e) { + if (cachedResult != null) return cachedResult; + + CompilerDirectives.transferToInterpreterAndInvalidate(); + var context = VmContext.get(this); + try { + var moduleKey = context.getModuleResolver().resolve(importUri); + if (!moduleKey.isGlobbable()) { throw exceptionBuilder() - .evalError("invalidGlobPattern", globPattern) - .withHint(e.getMessage()) + .evalError("cannotGlobUri", importUri, importUri.getScheme()) .build(); } + var resolvedElements = + GlobResolver.resolveGlob( + context.getSecurityManager(), + moduleKey, + currentModule.getOriginal(), + currentModule.getUri(), + globPattern); + var builder = new VmObjectBuilder(); + for (var entry : resolvedElements.entrySet()) { + builder.addEntry(entry.getKey(), importGlobElementNode); + } + cachedResult = builder.toMapping(resolvedElements); + return cachedResult; + } catch (IOException e) { + throw exceptionBuilder().evalError("ioErrorResolvingGlob", importUri).withCause(e).build(); + } catch (SecurityManagerException | HttpClientInitException e) { + throw exceptionBuilder().withCause(e).build(); + } catch (PackageLoadError e) { + throw exceptionBuilder().adhocEvalError(e.getMessage()).build(); + } catch (InvalidGlobPatternException e) { + throw exceptionBuilder() + .evalError("invalidGlobPattern", globPattern) + .withHint(e.getMessage()) + .build(); } - return importsMapping; } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/unary/ImportNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/unary/ImportNode.java index f8fdd4a8c..94ce45b0a 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/unary/ImportNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/unary/ImportNode.java @@ -33,7 +33,6 @@ @NodeInfo(shortName = "import") public final class ImportNode extends AbstractImportNode { private final VmLanguage language; - private final ResolvedModuleKey currentModule; @CompilationFinal @LateInit private VmTyped importedModule; @@ -42,9 +41,8 @@ public ImportNode( SourceSection sourceSection, ResolvedModuleKey currentModule, URI importUri) { - super(sourceSection, importUri); + super(sourceSection, currentModule, importUri); this.language = language; - this.currentModule = currentModule; assert importUri.isAbsolute(); } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/unary/ReadGlobElementNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/unary/ReadGlobElementNode.java index d4e394c1f..fb782b854 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/unary/ReadGlobElementNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/unary/ReadGlobElementNode.java @@ -37,7 +37,7 @@ public Object executeGeneric(VirtualFrame frame) { var path = (String) VmUtils.getMemberKey(frame); return readResource(mapping, path); } - + @TruffleBoundary private Object readResource(VmObjectLike mapping, String path) { @SuppressWarnings("unchecked") diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/unary/ReadGlobNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/unary/ReadGlobNode.java index eac2ed8e3..070c1a3ea 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/unary/ReadGlobNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/unary/ReadGlobNode.java @@ -20,24 +20,27 @@ import com.oracle.truffle.api.frame.FrameDescriptor; import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.source.SourceSection; -import java.net.URI; -import java.net.URISyntaxException; +import java.io.IOException; +import org.graalvm.collections.EconomicMap; +import org.pkl.core.SecurityManagerException; import org.pkl.core.ast.member.SharedMemberNode; +import org.pkl.core.http.HttpClientInitException; import org.pkl.core.module.ModuleKey; import org.pkl.core.runtime.VmContext; import org.pkl.core.runtime.VmLanguage; +import org.pkl.core.runtime.VmMapping; import org.pkl.core.runtime.VmObjectBuilder; -import org.pkl.core.util.IoUtils; +import org.pkl.core.util.GlobResolver; +import org.pkl.core.util.GlobResolver.InvalidGlobPatternException; @NodeInfo(shortName = "read*") -public abstract class ReadGlobNode extends UnaryExpressionNode { - private final ModuleKey currentModule; +public abstract class ReadGlobNode extends AbstractReadNode { private final SharedMemberNode readGlobElementNode; + private final EconomicMap cachedResults = EconomicMap.create(); protected ReadGlobNode( VmLanguage language, SourceSection sourceSection, ModuleKey currentModule) { - super(sourceSection); - this.currentModule = currentModule; + super(sourceSection, currentModule); readGlobElementNode = new SharedMemberNode( sourceSection, @@ -51,30 +54,43 @@ protected ReadGlobNode( @Specialization @TruffleBoundary public Object read(String globPattern) { - var context = VmContext.get(this); - var globUri = toUri(globPattern); - var resolvedElements = - context - .getResourceManager() - .resolveGlob(globUri, currentModule.getUri(), currentModule, this, globPattern); - var builder = new VmObjectBuilder(); - for (var entry : resolvedElements.entrySet()) { - builder.addEntry(entry.getKey(), readGlobElementNode); - } - return builder.toMapping(resolvedElements); - } + var cachedResult = cachedResults.get(globPattern); + if (cachedResult != null) return cachedResult; - private URI toUri(String globPattern) { + // use same check as for globbed imports (see AstBuilder) + if (globPattern.startsWith("...")) { + throw exceptionBuilder().evalError("cannotGlobTripleDots").build(); + } + var globUri = parseUri(globPattern); + var context = VmContext.get(this); try { - var globUri = IoUtils.toUri(globPattern); - if (IoUtils.parseTripleDotPath(globUri) != null) { - throw exceptionBuilder().evalError("cannotGlobTripleDots").build(); + var resolvedUri = currentModule.resolveUri(globUri); + var reader = context.getResourceManager().getReader(resolvedUri, this); + if (!reader.isGlobbable()) { + throw exceptionBuilder().evalError("cannotGlobUri", globUri, globUri.getScheme()).build(); + } + var resolvedElements = + GlobResolver.resolveGlob( + context.getSecurityManager(), + reader, + currentModule, + currentModule.getUri(), + globPattern); + var builder = new VmObjectBuilder(); + for (var entry : resolvedElements.entrySet()) { + builder.addEntry(entry.getKey(), readGlobElementNode); } - return globUri; - } catch (URISyntaxException e) { + cachedResult = builder.toMapping(resolvedElements); + cachedResults.put(globPattern, cachedResult); + return cachedResult; + } catch (IOException e) { + throw exceptionBuilder().evalError("ioErrorResolvingGlob", globPattern).withCause(e).build(); + } catch (SecurityManagerException | HttpClientInitException e) { + throw exceptionBuilder().withCause(e).build(); + } catch (InvalidGlobPatternException e) { throw exceptionBuilder() - .evalError("invalidResourceUri", globPattern) - .withHint(e.getReason()) + .evalError("invalidGlobPattern", globPattern) + .withHint(e.getMessage()) .build(); } } diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/ResourceManager.java b/pkl-core/src/main/java/org/pkl/core/runtime/ResourceManager.java index 5f839cf81..7bcec1b37 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/ResourceManager.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/ResourceManager.java @@ -27,14 +27,10 @@ import org.pkl.core.SecurityManager; import org.pkl.core.SecurityManagerException; import org.pkl.core.http.HttpClientInitException; -import org.pkl.core.module.ModuleKey; import org.pkl.core.packages.PackageLoadError; import org.pkl.core.resource.Resource; import org.pkl.core.resource.ResourceReader; import org.pkl.core.stdlib.VmObjectFactory; -import org.pkl.core.util.GlobResolver; -import org.pkl.core.util.GlobResolver.InvalidGlobPatternException; -import org.pkl.core.util.GlobResolver.ResolvedGlobElement; public final class ResourceManager { private final Map resourceReaders = new HashMap<>(); @@ -44,8 +40,6 @@ public final class ResourceManager { // cache resources indefinitely to make resource reads deterministic private final Map> resources = new HashMap<>(); - private final Map> resolvedGlobs = new HashMap<>(); - public ResourceManager(SecurityManager securityManager, Collection readers) { this.securityManager = securityManager; @@ -60,60 +54,16 @@ public ResourceManager(SecurityManager securityManager, CollectionThe glob URI must be absolute. For example: {@code "file:///foo/bar/*.pkl"}. - */ @TruffleBoundary - public Map resolveGlob( - URI globUri, - URI enclosingUri, - ModuleKey enclosingModuleKey, - Node readNode, - String globPattern) { - return resolvedGlobs.computeIfAbsent( - globUri.normalize(), - uri -> { - var scheme = uri.getScheme(); - URI resolvedUri; - try { - resolvedUri = enclosingModuleKey.resolveUri(globUri); - } catch (SecurityManagerException | IOException e) { - throw new VmExceptionBuilder().withLocation(readNode).withCause(e).build(); - } - try { - var reader = resourceReaders.get(resolvedUri.getScheme()); - if (reader == null) { - throw new VmExceptionBuilder() - .withLocation(readNode) - .evalError("noResourceReaderRegistered", scheme) - .build(); - } - if (!reader.isGlobbable()) { - throw new VmExceptionBuilder() - .evalError("cannotGlobUri", uri, scheme) - .withLocation(readNode) - .build(); - } - return GlobResolver.resolveGlob( - securityManager, reader, enclosingModuleKey, enclosingUri, globPattern); - } catch (InvalidGlobPatternException e) { - throw new VmExceptionBuilder() - .evalError("invalidGlobPattern", globPattern) - .withHint(e.getMessage()) - .withLocation(readNode) - .build(); - } catch (SecurityManagerException | HttpClientInitException e) { - throw new VmExceptionBuilder().withCause(e).withLocation(readNode).build(); - } catch (IOException e) { - throw new VmExceptionBuilder() - .evalError("ioErrorResolvingGlob", globPattern) - .withCause(e) - .withLocation(readNode) - .build(); - } - }); + public ResourceReader getReader(URI resourceUri, Node readNode) { + var reader = resourceReaders.get(resourceUri.getScheme()); + if (reader == null) { + throw new VmExceptionBuilder() + .withLocation(readNode) + .evalError("noResourceReaderRegistered", resourceUri.getScheme()) + .build(); + } + return reader; } @TruffleBoundary @@ -127,13 +77,7 @@ public Optional read(URI resourceUri, Node readNode) { throw new VmExceptionBuilder().withCause(e).withLocation(readNode).build(); } - var reader = resourceReaders.get(uri.getScheme()); - if (reader == null) { - throw new VmExceptionBuilder() - .withLocation(readNode) - .evalError("noResourceReaderRegistered", resourceUri.getScheme()) - .build(); - } + var reader = getReader(resourceUri, readNode); Optional resource; try { diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input-helper/basic/read/child/module2.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/basic/read/child/module2.pkl new file mode 100644 index 000000000..141cb9532 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/basic/read/child/module2.pkl @@ -0,0 +1,2 @@ +normalRead = read("resource.txt") +globbedRead = read*("*.txt") diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input-helper/basic/read/child/resource.txt b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/basic/read/child/resource.txt new file mode 100644 index 000000000..cdf568206 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/basic/read/child/resource.txt @@ -0,0 +1 @@ +child resource diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input-helper/basic/read/module1.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/basic/read/module1.pkl new file mode 100644 index 000000000..141cb9532 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/basic/read/module1.pkl @@ -0,0 +1,2 @@ +normalRead = read("resource.txt") +globbedRead = read*("*.txt") diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input-helper/basic/read/resource.txt b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/basic/read/resource.txt new file mode 100644 index 000000000..91e75c679 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/basic/read/resource.txt @@ -0,0 +1 @@ +resource diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/basic/read.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/basic/read.pkl index dd0da33fc..8a6918138 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/input/basic/read.pkl +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/basic/read.pkl @@ -22,14 +22,32 @@ examples { } ["read file"] { - read("read.pkl") + read("globtest/file1.txt") module.catch(() -> read("other.txt")) - read?("read.pkl") + read?("globtest/file1.txt") read?("other.txt") } + + ["read triple-dot file"] { + read(".../input-helper/basic/read/resource.txt") + read?(".../input-helper/basic/read/resource.txt") + } ["read non-allowed resource"] { module.catch(() -> read("forbidden:resource")) module.catch(() -> read?("forbidden:resource")) } + + ["use read expression with non-constant resource URI"] { + local function doRead(uri) = read(uri) + doRead("globtest/file1.txt") + doRead("globtest/file2.txt") + } + + ["read different resources with same relative resource URI"] { + local module1 = import(".../input-helper/basic/read/module1.pkl") + local module2 = import(".../input-helper/basic/read/child/module2.pkl") + module1.normalRead + module2.normalRead + } } diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/basic/readGlob.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/basic/readGlob.pkl index 238abc2f9..8f7b8adb4 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/input/basic/readGlob.pkl +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/basic/readGlob.pkl @@ -31,10 +31,17 @@ examples { read*("package://localhost:0/birds@0.5.0#/**.pkl") } - ["non-constant glob pattern"] { - local function readGlob(pattern) = read*(pattern) - readGlob("globtest/file*.txt") - readGlob("globtest/file1.txt") - readGlob("globtest/file2.txt") + ["use read expression with non-constant glob pattern"] { + local function doRead(pattern) = read*(pattern) + doRead("globtest/file*.txt") + doRead("globtest/file1.txt") + doRead("globtest/file2.txt") + } + + ["read different resources with same glob pattern"] { + local module1 = import(".../input-helper/basic/read/module1.pkl") + local module2 = import(".../input-helper/basic/read/child/module2.pkl") + module1.globbedRead + module2.globbedRead } } diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/basic/read.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/basic/read.pcf index 590d34bf9..733f2e77e 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/basic/read.pcf +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/basic/read.pcf @@ -17,94 +17,80 @@ examples { } ["read file"] { new { - uri = "file:///$snippetsDir/input/basic/read.pkl" + uri = "file:///$snippetsDir/input/basic/globtest/file1.txt" text = """ - amends "../snippetTest.pkl" - - examples { - ["read env variable"] { - read("env:NAME1") - read("env:NAME2") - module.catch(() -> read("env:OTHER")) - - read?("env:NAME1") - read?("env:NAME2") - read?("env:OTHER") - } - - ["read external property"] { - read("prop:name1") - read("prop:name2") - module.catch(() -> read("prop:other")) - - read?("prop:name1") - read?("prop:name2") - read?("prop:other") - } - - ["read file"] { - read("read.pkl") - module.catch(() -> read("other.txt")) - read?("read.pkl") - read?("other.txt") - } - - ["read non-allowed resource"] { - module.catch(() -> read("forbidden:resource")) - module.catch(() -> read?("forbidden:resource")) - } - } + file1 """ - base64 = "YW1lbmRzICIuLi9zbmlwcGV0VGVzdC5wa2wiCgpleGFtcGxlcyB7CiAgWyJyZWFkIGVudiB2YXJpYWJsZSJdIHsKICAgIHJlYWQoImVudjpOQU1FMSIpCiAgICByZWFkKCJlbnY6TkFNRTIiKQogICAgbW9kdWxlLmNhdGNoKCgpIC0+IHJlYWQoImVudjpPVEhFUiIpKQoKICAgIHJlYWQ/KCJlbnY6TkFNRTEiKQogICAgcmVhZD8oImVudjpOQU1FMiIpCiAgICByZWFkPygiZW52Ok9USEVSIikKICB9CgogIFsicmVhZCBleHRlcm5hbCBwcm9wZXJ0eSJdIHsKICAgIHJlYWQoInByb3A6bmFtZTEiKQogICAgcmVhZCgicHJvcDpuYW1lMiIpCiAgICBtb2R1bGUuY2F0Y2goKCkgLT4gcmVhZCgicHJvcDpvdGhlciIpKQoKICAgIHJlYWQ/KCJwcm9wOm5hbWUxIikKICAgIHJlYWQ/KCJwcm9wOm5hbWUyIikKICAgIHJlYWQ/KCJwcm9wOm90aGVyIikKICB9CgogIFsicmVhZCBmaWxlIl0gewogICAgcmVhZCgicmVhZC5wa2wiKQogICAgbW9kdWxlLmNhdGNoKCgpIC0+IHJlYWQoIm90aGVyLnR4dCIpKQogICAgcmVhZD8oInJlYWQucGtsIikKICAgIHJlYWQ/KCJvdGhlci50eHQiKQogIH0KCiAgWyJyZWFkIG5vbi1hbGxvd2VkIHJlc291cmNlIl0gewogICAgbW9kdWxlLmNhdGNoKCgpIC0+IHJlYWQoImZvcmJpZGRlbjpyZXNvdXJjZSIpKQogICAgbW9kdWxlLmNhdGNoKCgpIC0+IHJlYWQ/KCJmb3JiaWRkZW46cmVzb3VyY2UiKSkKICB9Cn0K" + base64 = "ZmlsZTEK" } "Cannot find resource `other.txt`." new { - uri = "file:///$snippetsDir/input/basic/read.pkl" + uri = "file:///$snippetsDir/input/basic/globtest/file1.txt" text = """ - amends "../snippetTest.pkl" + file1 - examples { - ["read env variable"] { - read("env:NAME1") - read("env:NAME2") - module.catch(() -> read("env:OTHER")) - - read?("env:NAME1") - read?("env:NAME2") - read?("env:OTHER") - } - - ["read external property"] { - read("prop:name1") - read("prop:name2") - module.catch(() -> read("prop:other")) - - read?("prop:name1") - read?("prop:name2") - read?("prop:other") - } - - ["read file"] { - read("read.pkl") - module.catch(() -> read("other.txt")) - read?("read.pkl") - read?("other.txt") - } + """ + base64 = "ZmlsZTEK" + } + null + } + ["read triple-dot file"] { + new { + uri = "file:///$snippetsDir/input-helper/basic/read/resource.txt" + text = """ + resource - ["read non-allowed resource"] { - module.catch(() -> read("forbidden:resource")) - module.catch(() -> read?("forbidden:resource")) - } - } + """ + base64 = "cmVzb3VyY2UK" + } + new { + uri = "file:///$snippetsDir/input-helper/basic/read/resource.txt" + text = """ + resource """ - base64 = "YW1lbmRzICIuLi9zbmlwcGV0VGVzdC5wa2wiCgpleGFtcGxlcyB7CiAgWyJyZWFkIGVudiB2YXJpYWJsZSJdIHsKICAgIHJlYWQoImVudjpOQU1FMSIpCiAgICByZWFkKCJlbnY6TkFNRTIiKQogICAgbW9kdWxlLmNhdGNoKCgpIC0+IHJlYWQoImVudjpPVEhFUiIpKQoKICAgIHJlYWQ/KCJlbnY6TkFNRTEiKQogICAgcmVhZD8oImVudjpOQU1FMiIpCiAgICByZWFkPygiZW52Ok9USEVSIikKICB9CgogIFsicmVhZCBleHRlcm5hbCBwcm9wZXJ0eSJdIHsKICAgIHJlYWQoInByb3A6bmFtZTEiKQogICAgcmVhZCgicHJvcDpuYW1lMiIpCiAgICBtb2R1bGUuY2F0Y2goKCkgLT4gcmVhZCgicHJvcDpvdGhlciIpKQoKICAgIHJlYWQ/KCJwcm9wOm5hbWUxIikKICAgIHJlYWQ/KCJwcm9wOm5hbWUyIikKICAgIHJlYWQ/KCJwcm9wOm90aGVyIikKICB9CgogIFsicmVhZCBmaWxlIl0gewogICAgcmVhZCgicmVhZC5wa2wiKQogICAgbW9kdWxlLmNhdGNoKCgpIC0+IHJlYWQoIm90aGVyLnR4dCIpKQogICAgcmVhZD8oInJlYWQucGtsIikKICAgIHJlYWQ/KCJvdGhlci50eHQiKQogIH0KCiAgWyJyZWFkIG5vbi1hbGxvd2VkIHJlc291cmNlIl0gewogICAgbW9kdWxlLmNhdGNoKCgpIC0+IHJlYWQoImZvcmJpZGRlbjpyZXNvdXJjZSIpKQogICAgbW9kdWxlLmNhdGNoKCgpIC0+IHJlYWQ/KCJmb3JiaWRkZW46cmVzb3VyY2UiKSkKICB9Cn0K" + base64 = "cmVzb3VyY2UK" } - null } ["read non-allowed resource"] { "Refusing to read resource `forbidden:resource` because it does not match any entry in the resource allowlist (`--allowed-resources`)." "Refusing to read resource `forbidden:resource` because it does not match any entry in the resource allowlist (`--allowed-resources`)." } + ["use read expression with non-constant resource URI"] { + new { + uri = "file:///$snippetsDir/input/basic/globtest/file1.txt" + text = """ + file1 + + """ + base64 = "ZmlsZTEK" + } + new { + uri = "file:///$snippetsDir/input/basic/globtest/file2.txt" + text = """ + file2 + + """ + base64 = "ZmlsZTIK" + } + } + ["read different resources with same relative resource URI"] { + new { + uri = "file:///$snippetsDir/input-helper/basic/read/resource.txt" + text = """ + resource + + """ + base64 = "cmVzb3VyY2UK" + } + new { + uri = "file:///$snippetsDir/input-helper/basic/read/child/resource.txt" + text = """ + child resource + + """ + base64 = "Y2hpbGQgcmVzb3VyY2UK" + } + } } diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/basic/readGlob.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/basic/readGlob.pcf index c29ffd1eb..6fedc7045 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/basic/readGlob.pcf +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/basic/readGlob.pcf @@ -176,7 +176,7 @@ examples { } } } - ["non-constant glob pattern"] { + ["use read expression with non-constant glob pattern"] { new { ["globtest/file1.txt"] { uri = "file:///$snippetsDir/input/basic/globtest/file1.txt" @@ -216,4 +216,26 @@ examples { } } } + ["read different resources with same glob pattern"] { + new { + ["resource.txt"] { + uri = "file:///$snippetsDir/input-helper/basic/read/resource.txt" + text = """ + resource + + """ + base64 = "cmVzb3VyY2UK" + } + } + new { + ["resource.txt"] { + uri = "file:///$snippetsDir/input-helper/basic/read/child/resource.txt" + text = """ + child resource + + """ + base64 = "Y2hpbGQgcmVzb3VyY2UK" + } + } + } }