diff --git a/pkl-core/src/main/java/org/pkl/core/Analyzer.java b/pkl-core/src/main/java/org/pkl/core/Analyzer.java index 9a27e6e8..345e9e88 100644 --- a/pkl-core/src/main/java/org/pkl/core/Analyzer.java +++ b/pkl-core/src/main/java/org/pkl/core/Analyzer.java @@ -1,5 +1,5 @@ /* - * Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,6 @@ package org.pkl.core; import java.io.IOException; import java.net.URI; -import java.net.URISyntaxException; import java.nio.file.Path; import java.util.Collection; import java.util.List; @@ -78,7 +77,6 @@ public class Analyzer { return VmImportAnalyzer.analyze(sources, vmContext); } catch (SecurityManagerException | IOException - | URISyntaxException | PackageLoadError | HttpClientInitException e) { throw new PklException(e.getMessage(), e); diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmImportAnalyzer.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmImportAnalyzer.java index 7a239a4a..dfbcbc98 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmImportAnalyzer.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmImportAnalyzer.java @@ -1,5 +1,5 @@ /* - * Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,9 +16,12 @@ package org.pkl.core.runtime; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import java.io.FileNotFoundException; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; +import java.nio.file.NoSuchFileException; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -31,22 +34,22 @@ import org.pkl.core.SecurityManagerException; import org.pkl.core.ast.builder.ImportsAndReadsParser; import org.pkl.core.ast.builder.ImportsAndReadsParser.Entry; import org.pkl.core.externalreader.ExternalReaderProcessException; +import org.pkl.core.module.ResolvedModuleKey; +import org.pkl.core.packages.PackageLoadError; import org.pkl.core.util.GlobResolver; import org.pkl.core.util.GlobResolver.InvalidGlobPatternException; -import org.pkl.core.util.GlobResolver.ResolvedGlobElement; import org.pkl.core.util.IoUtils; public class VmImportAnalyzer { @TruffleBoundary public static ImportGraph analyze(URI[] moduleUris, VmContext context) - throws IOException, - URISyntaxException, - SecurityManagerException, - ExternalReaderProcessException { + throws IOException, SecurityManagerException { var imports = new TreeMap>(); var resolvedImports = new TreeMap(); for (var moduleUri : moduleUris) { - analyzeSingle(moduleUri, context, imports, resolvedImports); + var moduleKey = context.getModuleResolver().resolve(moduleUri); + var resolvedModuleKey = moduleKey.resolve(context.getSecurityManager()); + analyzeSingle(moduleUri, resolvedModuleKey, context, imports, resolvedImports); } return new ImportGraph(imports, resolvedImports); } @@ -54,37 +57,33 @@ public class VmImportAnalyzer { @TruffleBoundary private static void analyzeSingle( URI moduleUri, + ResolvedModuleKey resolvedModuleKey, VmContext context, Map> imports, - Map resolvedImports) - throws IOException, - URISyntaxException, - SecurityManagerException, - ExternalReaderProcessException { + Map resolvedImports) { var moduleResolver = context.getModuleResolver(); var securityManager = context.getSecurityManager(); - var importsInModule = collectImports(moduleUri, moduleResolver, securityManager); - + var collectedImports = collectImports(resolvedModuleKey, moduleResolver, securityManager); + var importsInModule = new TreeSet(); + for (var imprt : collectedImports) { + importsInModule.add(imprt.toImport()); + } imports.put(moduleUri, importsInModule); - resolvedImports.put( - moduleUri, moduleResolver.resolve(moduleUri).resolve(securityManager).getUri()); - for (var imprt : importsInModule) { - if (imports.containsKey(imprt.uri())) { + resolvedImports.put(moduleUri, resolvedModuleKey.getUri()); + for (var imprt : collectedImports) { + if (imports.containsKey(imprt.moduleUri)) { continue; } - analyzeSingle(imprt.uri(), context, imports, resolvedImports); + analyzeSingle(imprt.moduleUri, imprt.resolvedModuleKey, context, imports, resolvedImports); } } - private static Set collectImports( - URI moduleUri, ModuleResolver moduleResolver, SecurityManager securityManager) - throws IOException, - URISyntaxException, - SecurityManagerException, - ExternalReaderProcessException { - var moduleKey = moduleResolver.resolve(moduleUri); - var resolvedModuleKey = moduleKey.resolve(securityManager); + private static Set collectImports( + ResolvedModuleKey resolvedModuleKey, + ModuleResolver moduleResolver, + SecurityManager securityManager) { List importsAndReads; + var moduleKey = resolvedModuleKey.getOriginal(); try { importsAndReads = ImportsAndReadsParser.parse(moduleKey, resolvedModuleKey); } catch (VmException err) { @@ -92,19 +91,21 @@ public class VmImportAnalyzer { .evalError("cannotAnalyzeBecauseSyntaxError", moduleKey.getUri()) .wrapping(err) .build(); + } catch (IOException err) { + throw new VmExceptionBuilder().evalError("ioErrorLoadingModule", moduleKey.getUri()).build(); } if (importsAndReads == null) { return Set.of(); } - var result = new TreeSet(); + var result = new HashSet(); for (var entry : importsAndReads) { if (!entry.isModule()) { continue; } - if (entry.isGlob()) { - var theModuleKey = - moduleResolver.resolve(moduleKey.resolveUri(IoUtils.toUri(entry.stringValue()))); - try { + try { + if (entry.isGlob()) { + var theModuleKey = + moduleResolver.resolve(moduleKey.resolveUri(IoUtils.toUri(entry.stringValue()))); var elements = GlobResolver.resolveGlob( securityManager, @@ -112,24 +113,60 @@ public class VmImportAnalyzer { moduleKey, moduleKey.getUri(), entry.stringValue()); - var globImports = - elements.values().stream() - .map(ResolvedGlobElement::uri) - .map(ImportGraph.Import::new) - .toList(); - result.addAll(globImports); - } catch (InvalidGlobPatternException e) { - throw new VmExceptionBuilder() - .evalError("invalidGlobPattern", entry.stringValue()) - .withSourceSection(entry.sourceSection()) - .build(); + for (var globElement : elements.values()) { + var moduleUri = globElement.uri(); + var mk = moduleResolver.resolve(moduleUri); + var rmk = mk.resolve(securityManager); + result.add(new ImportEntry(moduleUri, rmk)); + } + } else { + var resolvedUri = + IoUtils.resolve(securityManager, moduleKey, IoUtils.toUri(entry.stringValue())); + var mKey = moduleResolver.resolve(resolvedUri); + var rmk = mKey.resolve(securityManager); + result.add(new ImportEntry(resolvedUri, rmk)); } - } else { - var resolvedUri = - IoUtils.resolve(securityManager, moduleKey, IoUtils.toUri(entry.stringValue())); - result.add(new Import(resolvedUri)); + } catch (InvalidGlobPatternException e) { + throw new VmExceptionBuilder() + .evalError("invalidGlobPattern", entry.stringValue()) + .withSourceSection(entry.sourceSection()) + .build(); + } catch (FileNotFoundException | NoSuchFileException e) { + throw new VmExceptionBuilder() + .evalError("cannotFindModule", entry.stringValue()) + .withSourceSection(entry.sourceSection()) + .build(); + } catch (URISyntaxException e) { + throw new VmExceptionBuilder() + .evalError("invalidModuleUri", entry.stringValue()) + .withHint(e.getReason()) + .withSourceSection(entry.sourceSection()) + .build(); + } catch (IOException e) { + throw new VmExceptionBuilder() + .evalError("ioErrorLoadingModule", entry.stringValue()) + .withCause(e) + .withSourceSection(entry.sourceSection()) + .build(); + } catch (SecurityManagerException | PackageLoadError e) { + throw new VmExceptionBuilder() + .withSourceSection(entry.sourceSection()) + .withCause(e) + .build(); + } catch (ExternalReaderProcessException e) { + throw new VmExceptionBuilder() + .withSourceSection(entry.sourceSection()) + .evalError("externalReaderFailure") + .withCause(e) + .build(); } } return result; } + + private record ImportEntry(URI moduleUri, ResolvedModuleKey resolvedModuleKey) { + private Import toImport() { + return new Import(resolvedModuleKey.getOriginal().getUri()); + } + } } diff --git a/pkl-core/src/main/java/org/pkl/core/stdlib/analyze/AnalyzeNodes.java b/pkl-core/src/main/java/org/pkl/core/stdlib/analyze/AnalyzeNodes.java index fdfaad1c..ed9ecbf6 100644 --- a/pkl-core/src/main/java/org/pkl/core/stdlib/analyze/AnalyzeNodes.java +++ b/pkl-core/src/main/java/org/pkl/core/stdlib/analyze/AnalyzeNodes.java @@ -1,5 +1,5 @@ /* - * Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,6 @@ import java.net.URISyntaxException; import org.pkl.core.ImportGraph; import org.pkl.core.ImportGraph.Import; import org.pkl.core.SecurityManagerException; -import org.pkl.core.externalreader.ExternalReaderProcessException; import org.pkl.core.packages.PackageLoadError; import org.pkl.core.runtime.AnalyzeModule; import org.pkl.core.runtime.VmContext; @@ -92,11 +91,7 @@ public final class AnalyzeNodes { try { var results = VmImportAnalyzer.analyze(uris, context); return importGraphFactory.create(results); - } catch (IOException - | URISyntaxException - | SecurityManagerException - | PackageLoadError - | ExternalReaderProcessException e) { + } catch (IOException | SecurityManagerException | PackageLoadError e) { throw exceptionBuilder().withCause(e).build(); } } diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input-helper/analyze/cannotFindModule.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/analyze/cannotFindModule.pkl new file mode 100644 index 00000000..827a45d8 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/analyze/cannotFindModule.pkl @@ -0,0 +1 @@ +import "bogusModule.pkl" diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input-helper/analyze/invalidGlob.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/analyze/invalidGlob.pkl new file mode 100644 index 00000000..dfe0ab8c --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/analyze/invalidGlob.pkl @@ -0,0 +1 @@ +import* ".../**.pkl" diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/errors/analyzeImportsCannotFindModule.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/analyzeImportsCannotFindModule.pkl new file mode 100644 index 00000000..ebbc9b26 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/analyzeImportsCannotFindModule.pkl @@ -0,0 +1,5 @@ +import "pkl:analyze" +import "pkl:reflect" +import ".../input-helper/analyze/cannotFindModule.pkl" + +res = analyze.importGraph(Set(reflect.Module(cannotFindModule).uri)) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/errors/analyzeImportsInvalidGlob.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/analyzeImportsInvalidGlob.pkl new file mode 100644 index 00000000..eaea8916 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/analyzeImportsInvalidGlob.pkl @@ -0,0 +1,5 @@ +import "pkl:analyze" +import "pkl:reflect" +import ".../input-helper/analyze/invalidGlob.pkl" + +res = analyze.importGraph(Set(reflect.Module(invalidGlob).uri)) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/analyzeImportsCannotFindModule.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/analyzeImportsCannotFindModule.err new file mode 100644 index 00000000..442dd18b --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/analyzeImportsCannotFindModule.err @@ -0,0 +1,14 @@ +–– Pkl Error –– +Cannot find module `bogusModule.pkl`. + +x | import "bogusModule.pkl" + ^^^^^^^^^^^^^^^^^ +at pkl.analyze#importGraph (file:///$snippetsDir/input-helper/analyze/cannotFindModule.pkl) + +x | res = analyze.importGraph(Set(reflect.Module(cannotFindModule).uri)) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +at analyzeImportsCannotFindModule#res (file:///$snippetsDir/input/errors/analyzeImportsCannotFindModule.pkl) + +xxx | text = renderer.renderDocument(value) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +at pkl.base#Module.output.text (pkl:base) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/analyzeImportsInvalidGlob.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/analyzeImportsInvalidGlob.err new file mode 100644 index 00000000..289fce99 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/analyzeImportsInvalidGlob.err @@ -0,0 +1,14 @@ +–– Pkl Error –– +Cannot combine glob imports with triple-dot module URIs. + +x | import* ".../**.pkl" + ^^^^^^^^^^^^^^^^^^^^ +at analyzeImportsInvalidGlob#invalidGlob (file:///$snippetsDir/input-helper/analyze/invalidGlob.pkl) + +x | res = analyze.importGraph(Set(reflect.Module(invalidGlob).uri)) + ^^^^^^^^^^^ +at analyzeImportsInvalidGlob#res (file:///$snippetsDir/input/errors/analyzeImportsInvalidGlob.pkl) + +xxx | text = renderer.renderDocument(value) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +at pkl.base#Module.output.text (pkl:base)