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 4cdb73b2c..7c5e64b84 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" + } + } + } }