mirror of
https://github.com/apple/pkl.git
synced 2026-04-23 08:48:36 +02:00
Fix globbed read/import bugs (#449)
* Split MemberNode into (Regular/Shared)MemberNode SharedMemberNode enables generating non-constant object members at run time without generating an unbounded number of Truffle root nodes. * Invert shouldRunTypeCheck to match its name * Introduce VmObjectBuilder Introduce VmObjectBuilder, a uniform way to build `VmObject`s whose `ObjectMember`s are determined at run time. Replace some manual object building code with VmObjectBuilder. Add some assertions to fix IntelliJ warnings. * Improve implementation of globbed read/import nodes - Leverage SharedMemberNode to have a single Truffle root node per globbed read/import expression instead of one root node per resolved glob element. - Remove caching in ReadGlobNode because it only works correctly if glob pattern is a string *constant*. - Remove caching in StaticReadNode (now ReadGlobElementNode/ImportGlobElementNode) because it seems unnecessary and the implementation doesn't look quite right. * Simplify code * Fix ClassCastException when reflecting on globbed import * 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.
This commit is contained in:
@@ -20,35 +20,22 @@ import com.oracle.truffle.api.frame.VirtualFrame;
|
|||||||
import com.oracle.truffle.api.source.SourceSection;
|
import com.oracle.truffle.api.source.SourceSection;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import org.pkl.core.ast.member.DefaultPropertyBodyNode;
|
import org.pkl.core.ast.member.DefaultPropertyBodyNode;
|
||||||
import org.pkl.core.ast.member.Member;
|
|
||||||
import org.pkl.core.runtime.VmExceptionBuilder;
|
import org.pkl.core.runtime.VmExceptionBuilder;
|
||||||
import org.pkl.core.runtime.VmLanguage;
|
import org.pkl.core.runtime.VmLanguage;
|
||||||
import org.pkl.core.runtime.VmUtils;
|
import org.pkl.core.runtime.VmUtils;
|
||||||
import org.pkl.core.util.Nullable;
|
import org.pkl.core.util.Nullable;
|
||||||
|
|
||||||
public abstract class MemberNode extends PklRootNode {
|
public abstract class MemberNode extends PklRootNode {
|
||||||
protected final Member member;
|
|
||||||
@Child protected ExpressionNode bodyNode;
|
@Child protected ExpressionNode bodyNode;
|
||||||
|
|
||||||
protected MemberNode(
|
protected MemberNode(
|
||||||
@Nullable VmLanguage language,
|
@Nullable VmLanguage language, FrameDescriptor descriptor, ExpressionNode bodyNode) {
|
||||||
FrameDescriptor descriptor,
|
|
||||||
Member member,
|
|
||||||
ExpressionNode bodyNode) {
|
|
||||||
|
|
||||||
super(language, descriptor);
|
super(language, descriptor);
|
||||||
this.member = member;
|
|
||||||
this.bodyNode = bodyNode;
|
this.bodyNode = bodyNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public abstract SourceSection getHeaderSection();
|
||||||
public final SourceSection getSourceSection() {
|
|
||||||
return member.getSourceSection();
|
|
||||||
}
|
|
||||||
|
|
||||||
public final SourceSection getHeaderSection() {
|
|
||||||
return member.getHeaderSection();
|
|
||||||
}
|
|
||||||
|
|
||||||
public final SourceSection getBodySection() {
|
public final SourceSection getBodySection() {
|
||||||
return bodyNode.getSourceSection();
|
return bodyNode.getSourceSection();
|
||||||
@@ -58,11 +45,6 @@ public abstract class MemberNode extends PklRootNode {
|
|||||||
return bodyNode;
|
return bodyNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public final String getName() {
|
|
||||||
return member.getQualifiedName();
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void replaceBody(Function<ExpressionNode, ExpressionNode> replacer) {
|
public final void replaceBody(Function<ExpressionNode, ExpressionNode> replacer) {
|
||||||
bodyNode = insert(replacer.apply(bodyNode));
|
bodyNode = insert(replacer.apply(bodyNode));
|
||||||
}
|
}
|
||||||
@@ -72,7 +54,7 @@ public abstract class MemberNode extends PklRootNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected final VmExceptionBuilder exceptionBuilder() {
|
protected final VmExceptionBuilder exceptionBuilder() {
|
||||||
return new VmExceptionBuilder().withSourceSection(member.getHeaderSection());
|
return new VmExceptionBuilder().withSourceSection(getHeaderSection());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -83,9 +65,9 @@ public abstract class MemberNode extends PklRootNode {
|
|||||||
* org.pkl.core.runtime.VmUtils#SKIP_TYPECHECK_MARKER}. IDEA: might be more appropriate to only
|
* org.pkl.core.runtime.VmUtils#SKIP_TYPECHECK_MARKER}. IDEA: might be more appropriate to only
|
||||||
* skip constraints check
|
* skip constraints check
|
||||||
*/
|
*/
|
||||||
protected final boolean shouldRunTypecheck(VirtualFrame frame) {
|
protected final boolean shouldRunTypeCheck(VirtualFrame frame) {
|
||||||
return frame.getArguments().length == 4
|
return frame.getArguments().length != 4
|
||||||
&& frame.getArguments()[3] == VmUtils.SKIP_TYPECHECK_MARKER;
|
|| frame.getArguments()[3] != VmUtils.SKIP_TYPECHECK_MARKER;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isUndefined() {
|
public boolean isUndefined() {
|
||||||
|
|||||||
@@ -2218,8 +2218,7 @@ public final class AstBuilder extends AbstractAstBuilder<Object> {
|
|||||||
return ReadOrNullNodeGen.create(createSourceSection(ctx), moduleKey, visitExpr(exprCtx));
|
return ReadOrNullNodeGen.create(createSourceSection(ctx), moduleKey, visitExpr(exprCtx));
|
||||||
}
|
}
|
||||||
assert tokenType == PklLexer.READ_GLOB;
|
assert tokenType == PklLexer.READ_GLOB;
|
||||||
return ReadGlobNodeGen.create(
|
return ReadGlobNodeGen.create(createSourceSection(ctx), moduleKey, visitExpr(exprCtx));
|
||||||
language, createSourceSection(ctx), moduleKey, visitExpr(exprCtx));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -2660,8 +2659,7 @@ public final class AstBuilder extends AbstractAstBuilder<Object> {
|
|||||||
}
|
}
|
||||||
var resolvedUri = resolveImport(importUri, importUriCtx);
|
var resolvedUri = resolveImport(importUri, importUriCtx);
|
||||||
if (isGlobImport) {
|
if (isGlobImport) {
|
||||||
return new ImportGlobNode(
|
return new ImportGlobNode(section, moduleInfo.getResolvedModuleKey(), resolvedUri, importUri);
|
||||||
language, section, moduleInfo.getResolvedModuleKey(), resolvedUri, importUri);
|
|
||||||
}
|
}
|
||||||
return new ImportNode(language, section, moduleInfo.getResolvedModuleKey(), resolvedUri);
|
return new ImportNode(language, section, moduleInfo.getResolvedModuleKey(), resolvedUri);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,16 +18,19 @@ package org.pkl.core.ast.expression.unary;
|
|||||||
import com.oracle.truffle.api.source.SourceSection;
|
import com.oracle.truffle.api.source.SourceSection;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import org.pkl.core.ast.ExpressionNode;
|
import org.pkl.core.ast.ExpressionNode;
|
||||||
|
import org.pkl.core.module.ResolvedModuleKey;
|
||||||
|
|
||||||
public abstract class AbstractImportNode extends ExpressionNode {
|
public abstract class AbstractImportNode extends ExpressionNode {
|
||||||
|
protected final ResolvedModuleKey currentModule;
|
||||||
protected final URI importUri;
|
protected final URI importUri;
|
||||||
|
|
||||||
AbstractImportNode(SourceSection sourceSection, URI importUri) {
|
AbstractImportNode(SourceSection sourceSection, ResolvedModuleKey currentModule, URI importUri) {
|
||||||
super(sourceSection);
|
super(sourceSection);
|
||||||
|
this.currentModule = currentModule;
|
||||||
this.importUri = importUri;
|
this.importUri = importUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
public URI getImportUri() {
|
public final URI getImportUri() {
|
||||||
return importUri;
|
return importUri;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,30 +30,33 @@ import org.pkl.core.util.IoUtils;
|
|||||||
import org.pkl.core.util.Nullable;
|
import org.pkl.core.util.Nullable;
|
||||||
|
|
||||||
public abstract class AbstractReadNode extends UnaryExpressionNode {
|
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);
|
super(sourceSection);
|
||||||
this.moduleKey = moduleKey;
|
this.currentModule = currentModule;
|
||||||
}
|
}
|
||||||
|
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
protected @Nullable Object doRead(String resourceUri, VmContext context, Node readNode) {
|
protected final URI parseUri(String resourceUri) {
|
||||||
var resolvedUri = resolveResource(moduleKey, resourceUri);
|
|
||||||
return context.getResourceManager().read(resolvedUri, readNode).orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private URI resolveResource(ModuleKey moduleKey, String resourceUri) {
|
|
||||||
URI parsedUri;
|
|
||||||
try {
|
try {
|
||||||
parsedUri = IoUtils.toUri(resourceUri);
|
return IoUtils.toUri(resourceUri);
|
||||||
} catch (URISyntaxException e) {
|
} catch (URISyntaxException e) {
|
||||||
throw exceptionBuilder()
|
throw exceptionBuilder()
|
||||||
.evalError("invalidResourceUri", resourceUri)
|
.evalError("invalidResourceUri", resourceUri)
|
||||||
.withHint(e.getReason())
|
.withHint(e.getReason())
|
||||||
.build();
|
.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);
|
var context = VmContext.get(this);
|
||||||
URI resolvedUri;
|
URI resolvedUri;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2024 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.pkl.core.ast.expression.unary;
|
||||||
|
|
||||||
|
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||||
|
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||||
|
import com.oracle.truffle.api.source.SourceSection;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.pkl.core.SecurityManagerException;
|
||||||
|
import org.pkl.core.ast.ExpressionNode;
|
||||||
|
import org.pkl.core.http.HttpClientInitException;
|
||||||
|
import org.pkl.core.module.ResolvedModuleKey;
|
||||||
|
import org.pkl.core.packages.PackageLoadError;
|
||||||
|
import org.pkl.core.runtime.VmContext;
|
||||||
|
import org.pkl.core.runtime.VmLanguage;
|
||||||
|
import org.pkl.core.runtime.VmObjectLike;
|
||||||
|
import org.pkl.core.runtime.VmTyped;
|
||||||
|
import org.pkl.core.runtime.VmUtils;
|
||||||
|
import org.pkl.core.util.GlobResolver.ResolvedGlobElement;
|
||||||
|
|
||||||
|
/** Used by {@link ReadGlobNode}. */
|
||||||
|
public final class ImportGlobMemberBodyNode extends ExpressionNode {
|
||||||
|
private final VmLanguage language;
|
||||||
|
private final ResolvedModuleKey currentModule;
|
||||||
|
|
||||||
|
public ImportGlobMemberBodyNode(
|
||||||
|
SourceSection sourceSection, VmLanguage language, ResolvedModuleKey currentModule) {
|
||||||
|
super(sourceSection);
|
||||||
|
this.language = language;
|
||||||
|
this.currentModule = currentModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object executeGeneric(VirtualFrame frame) {
|
||||||
|
var mapping = VmUtils.getObjectReceiver(frame);
|
||||||
|
var path = (String) VmUtils.getMemberKey(frame);
|
||||||
|
return importModule(mapping, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TruffleBoundary
|
||||||
|
private VmTyped importModule(VmObjectLike mapping, String path) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
var globElements = (Map<String, ResolvedGlobElement>) mapping.getExtraStorage();
|
||||||
|
var importUri = globElements.get(path).getUri();
|
||||||
|
var context = VmContext.get(this);
|
||||||
|
try {
|
||||||
|
context.getSecurityManager().checkImportModule(currentModule.getUri(), importUri);
|
||||||
|
var moduleToImport = context.getModuleResolver().resolve(importUri, this);
|
||||||
|
return language.loadModule(moduleToImport, this);
|
||||||
|
} catch (SecurityManagerException | PackageLoadError | HttpClientInitException e) {
|
||||||
|
throw exceptionBuilder().withCause(e).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,114 +17,93 @@ package org.pkl.core.ast.expression.unary;
|
|||||||
|
|
||||||
import com.oracle.truffle.api.CompilerDirectives;
|
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.CompilerDirectives.TruffleBoundary;
|
|
||||||
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.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.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.List;
|
|
||||||
import org.graalvm.collections.EconomicMap;
|
|
||||||
import org.pkl.core.SecurityManagerException;
|
import org.pkl.core.SecurityManagerException;
|
||||||
import org.pkl.core.ast.VmModifier;
|
import org.pkl.core.ast.member.SharedMemberNode;
|
||||||
import org.pkl.core.ast.member.ObjectMember;
|
|
||||||
import org.pkl.core.ast.member.UntypedObjectMemberNode;
|
|
||||||
import org.pkl.core.http.HttpClientInitException;
|
import org.pkl.core.http.HttpClientInitException;
|
||||||
import org.pkl.core.module.ResolvedModuleKey;
|
import org.pkl.core.module.ResolvedModuleKey;
|
||||||
import org.pkl.core.packages.PackageLoadError;
|
import org.pkl.core.packages.PackageLoadError;
|
||||||
import org.pkl.core.runtime.BaseModule;
|
|
||||||
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.VmMapping;
|
import org.pkl.core.runtime.VmMapping;
|
||||||
import org.pkl.core.runtime.VmUtils;
|
import org.pkl.core.runtime.VmObjectBuilder;
|
||||||
import org.pkl.core.util.EconomicMaps;
|
|
||||||
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.GlobResolver.ResolvedGlobElement;
|
|
||||||
import org.pkl.core.util.LateInit;
|
import org.pkl.core.util.LateInit;
|
||||||
|
|
||||||
@NodeInfo(shortName = "import*")
|
@NodeInfo(shortName = "import*")
|
||||||
public class ImportGlobNode extends AbstractImportNode {
|
public class ImportGlobNode extends AbstractImportNode {
|
||||||
private final VmLanguage language;
|
|
||||||
|
|
||||||
private final ResolvedModuleKey currentModule;
|
|
||||||
|
|
||||||
private final String globPattern;
|
private final String globPattern;
|
||||||
|
@Child @LateInit private SharedMemberNode memberNode;
|
||||||
@CompilationFinal @LateInit private VmMapping importedMapping;
|
@CompilationFinal @LateInit private VmMapping cachedResult;
|
||||||
|
|
||||||
public ImportGlobNode(
|
public ImportGlobNode(
|
||||||
VmLanguage language,
|
|
||||||
SourceSection sourceSection,
|
SourceSection sourceSection,
|
||||||
ResolvedModuleKey currentModule,
|
ResolvedModuleKey currentModule,
|
||||||
URI importUri,
|
URI importUri,
|
||||||
String globPattern) {
|
String globPattern) {
|
||||||
super(sourceSection, importUri);
|
super(sourceSection, currentModule, importUri);
|
||||||
this.language = language;
|
|
||||||
this.currentModule = currentModule;
|
|
||||||
this.globPattern = globPattern;
|
this.globPattern = globPattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
@TruffleBoundary
|
private SharedMemberNode getMemberNode() {
|
||||||
private EconomicMap<Object, ObjectMember> buildMembers(
|
if (memberNode == null) {
|
||||||
FrameDescriptor frameDescriptor, List<ResolvedGlobElement> uris) {
|
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||||
var members = EconomicMaps.<Object, ObjectMember>create();
|
var language = VmLanguage.get(this);
|
||||||
for (var entry : uris) {
|
memberNode =
|
||||||
var readNode =
|
new SharedMemberNode(
|
||||||
new ImportNode(
|
sourceSection,
|
||||||
language, VmUtils.unavailableSourceSection(), currentModule, entry.getUri());
|
sourceSection,
|
||||||
var member =
|
"",
|
||||||
new ObjectMember(
|
language,
|
||||||
VmUtils.unavailableSourceSection(),
|
new FrameDescriptor(),
|
||||||
VmUtils.unavailableSourceSection(),
|
new ImportGlobMemberBodyNode(sourceSection, language, currentModule));
|
||||||
VmModifier.ENTRY,
|
|
||||||
null,
|
|
||||||
"");
|
|
||||||
var memberNode = new UntypedObjectMemberNode(language, frameDescriptor, member, readNode);
|
|
||||||
member.initMemberNode(memberNode);
|
|
||||||
EconomicMaps.put(members, entry.getPath(), member);
|
|
||||||
}
|
}
|
||||||
return members;
|
return memberNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object executeGeneric(VirtualFrame frame) {
|
public Object executeGeneric(VirtualFrame frame) {
|
||||||
if (importedMapping == null) {
|
if (cachedResult != null) return cachedResult;
|
||||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
|
||||||
var context = VmContext.get(this);
|
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||||
try {
|
var context = VmContext.get(this);
|
||||||
var moduleKey = context.getModuleResolver().resolve(importUri);
|
try {
|
||||||
var securityManager = VmContext.get(this).getSecurityManager();
|
var moduleKey = context.getModuleResolver().resolve(importUri);
|
||||||
if (!moduleKey.isGlobbable()) {
|
if (!moduleKey.isGlobbable()) {
|
||||||
throw exceptionBuilder()
|
|
||||||
.evalError("cannotGlobUri", importUri, importUri.getScheme())
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
var uris =
|
|
||||||
GlobResolver.resolveGlob(
|
|
||||||
securityManager,
|
|
||||||
moduleKey,
|
|
||||||
currentModule.getOriginal(),
|
|
||||||
currentModule.getUri(),
|
|
||||||
globPattern);
|
|
||||||
var members = buildMembers(frame.getFrameDescriptor(), uris);
|
|
||||||
importedMapping =
|
|
||||||
new VmMapping(
|
|
||||||
frame.materialize(), BaseModule.getMappingClass().getPrototype(), members);
|
|
||||||
} 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()
|
throw exceptionBuilder()
|
||||||
.evalError("invalidGlobPattern", globPattern)
|
.evalError("cannotGlobUri", importUri, importUri.getScheme())
|
||||||
.withHint(e.getMessage())
|
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
var resolvedElements =
|
||||||
|
GlobResolver.resolveGlob(
|
||||||
|
context.getSecurityManager(),
|
||||||
|
moduleKey,
|
||||||
|
currentModule.getOriginal(),
|
||||||
|
currentModule.getUri(),
|
||||||
|
globPattern);
|
||||||
|
var builder = new VmObjectBuilder(resolvedElements.size());
|
||||||
|
for (var entry : resolvedElements.entrySet()) {
|
||||||
|
builder.addEntry(entry.getKey(), getMemberNode());
|
||||||
|
}
|
||||||
|
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 importedMapping;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ 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;
|
||||||
private final ResolvedModuleKey currentModule;
|
|
||||||
|
|
||||||
@CompilationFinal @LateInit private VmTyped importedModule;
|
@CompilationFinal @LateInit private VmTyped importedModule;
|
||||||
|
|
||||||
@@ -42,9 +41,8 @@ public final class ImportNode extends AbstractImportNode {
|
|||||||
SourceSection sourceSection,
|
SourceSection sourceSection,
|
||||||
ResolvedModuleKey currentModule,
|
ResolvedModuleKey currentModule,
|
||||||
URI importUri) {
|
URI importUri) {
|
||||||
super(sourceSection, importUri);
|
super(sourceSection, currentModule, importUri);
|
||||||
this.language = language;
|
this.language = language;
|
||||||
this.currentModule = currentModule;
|
|
||||||
|
|
||||||
assert importUri.isAbsolute();
|
assert importUri.isAbsolute();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2024 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.pkl.core.ast.expression.unary;
|
||||||
|
|
||||||
|
import com.oracle.truffle.api.CompilerDirectives;
|
||||||
|
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||||
|
import com.oracle.truffle.api.source.SourceSection;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.pkl.core.ast.ExpressionNode;
|
||||||
|
import org.pkl.core.runtime.VmContext;
|
||||||
|
import org.pkl.core.runtime.VmObjectLike;
|
||||||
|
import org.pkl.core.runtime.VmUtils;
|
||||||
|
import org.pkl.core.util.GlobResolver.ResolvedGlobElement;
|
||||||
|
|
||||||
|
/** Used by {@link ReadGlobNode}. */
|
||||||
|
public class ReadGlobMemberBodyNode extends ExpressionNode {
|
||||||
|
public ReadGlobMemberBodyNode(SourceSection sourceSection) {
|
||||||
|
super(sourceSection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object executeGeneric(VirtualFrame frame) {
|
||||||
|
var mapping = VmUtils.getObjectReceiver(frame);
|
||||||
|
var path = (String) VmUtils.getMemberKey(frame);
|
||||||
|
return readResource(mapping, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object readResource(VmObjectLike mapping, String path) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
var globElements = (Map<String, ResolvedGlobElement>) mapping.getExtraStorage();
|
||||||
|
var resourceUri = VmUtils.getMapValue(globElements, path).getUri();
|
||||||
|
var resource = VmContext.get(this).getResourceManager().read(resourceUri, this).orElse(null);
|
||||||
|
if (resource == null) {
|
||||||
|
CompilerDirectives.transferToInterpreter();
|
||||||
|
throw exceptionBuilder().evalError("cannotFindResource", resourceUri).build();
|
||||||
|
}
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,96 +16,91 @@
|
|||||||
package org.pkl.core.ast.expression.unary;
|
package org.pkl.core.ast.expression.unary;
|
||||||
|
|
||||||
import com.oracle.truffle.api.CompilerDirectives;
|
import com.oracle.truffle.api.CompilerDirectives;
|
||||||
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
|
|
||||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
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.FrameDescriptor;
|
import com.oracle.truffle.api.frame.FrameDescriptor;
|
||||||
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.io.IOException;
|
||||||
import java.net.URISyntaxException;
|
|
||||||
import java.util.List;
|
|
||||||
import org.graalvm.collections.EconomicMap;
|
import org.graalvm.collections.EconomicMap;
|
||||||
import org.pkl.core.ast.VmModifier;
|
import org.pkl.core.SecurityManagerException;
|
||||||
import org.pkl.core.ast.member.ObjectMember;
|
import org.pkl.core.ast.member.SharedMemberNode;
|
||||||
import org.pkl.core.ast.member.UntypedObjectMemberNode;
|
import org.pkl.core.http.HttpClientInitException;
|
||||||
import org.pkl.core.module.ModuleKey;
|
import org.pkl.core.module.ModuleKey;
|
||||||
import org.pkl.core.runtime.BaseModule;
|
|
||||||
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.VmMapping;
|
import org.pkl.core.runtime.VmMapping;
|
||||||
import org.pkl.core.runtime.VmUtils;
|
import org.pkl.core.runtime.VmObjectBuilder;
|
||||||
import org.pkl.core.util.EconomicMaps;
|
import org.pkl.core.util.GlobResolver;
|
||||||
import org.pkl.core.util.GlobResolver.ResolvedGlobElement;
|
import org.pkl.core.util.GlobResolver.InvalidGlobPatternException;
|
||||||
import org.pkl.core.util.IoUtils;
|
|
||||||
import org.pkl.core.util.LateInit;
|
import org.pkl.core.util.LateInit;
|
||||||
|
|
||||||
@NodeInfo(shortName = "read*")
|
@NodeInfo(shortName = "read*")
|
||||||
public abstract class ReadGlobNode extends UnaryExpressionNode {
|
public abstract class ReadGlobNode extends AbstractReadNode {
|
||||||
private final VmLanguage language;
|
private final EconomicMap<String, VmMapping> cachedResults = EconomicMap.create();
|
||||||
private final ModuleKey currentModule;
|
@Child @LateInit private SharedMemberNode memberNode;
|
||||||
|
|
||||||
@CompilationFinal @LateInit VmMapping readResult;
|
protected ReadGlobNode(SourceSection sourceSection, ModuleKey currentModule) {
|
||||||
|
super(sourceSection, currentModule);
|
||||||
protected ReadGlobNode(
|
|
||||||
VmLanguage language, SourceSection sourceSection, ModuleKey currentModule) {
|
|
||||||
super(sourceSection);
|
|
||||||
this.currentModule = currentModule;
|
|
||||||
this.language = language;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@TruffleBoundary
|
private SharedMemberNode getMemberNode() {
|
||||||
private URI doResolveUri(String globExpression) {
|
if (memberNode == null) {
|
||||||
try {
|
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||||
var globUri = IoUtils.toUri(globExpression);
|
var language = VmLanguage.get(this);
|
||||||
var tripleDotImport = IoUtils.parseTripleDotPath(globUri);
|
memberNode =
|
||||||
if (tripleDotImport != null) {
|
new SharedMemberNode(
|
||||||
throw exceptionBuilder().evalError("cannotGlobTripleDots").build();
|
sourceSection,
|
||||||
}
|
sourceSection,
|
||||||
return globUri;
|
"",
|
||||||
} catch (URISyntaxException e) {
|
language,
|
||||||
throw exceptionBuilder()
|
new FrameDescriptor(),
|
||||||
.evalError("invalidResourceUri", globExpression)
|
new ReadGlobMemberBodyNode(sourceSection));
|
||||||
.withHint(e.getReason())
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
}
|
return memberNode;
|
||||||
|
|
||||||
@TruffleBoundary
|
|
||||||
private EconomicMap<Object, ObjectMember> buildMembers(
|
|
||||||
FrameDescriptor frameDescriptor, List<ResolvedGlobElement> uris) {
|
|
||||||
var members = EconomicMaps.<Object, ObjectMember>create();
|
|
||||||
for (var entry : uris) {
|
|
||||||
var readNode = new StaticReadNode(entry.getUri());
|
|
||||||
var member =
|
|
||||||
new ObjectMember(
|
|
||||||
VmUtils.unavailableSourceSection(),
|
|
||||||
VmUtils.unavailableSourceSection(),
|
|
||||||
VmModifier.ENTRY,
|
|
||||||
null,
|
|
||||||
"");
|
|
||||||
var memberNode = new UntypedObjectMemberNode(language, frameDescriptor, member, readNode);
|
|
||||||
member.initMemberNode(memberNode);
|
|
||||||
EconomicMaps.put(members, entry.getPath(), member);
|
|
||||||
}
|
|
||||||
return members;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Specialization
|
@Specialization
|
||||||
public Object read(VirtualFrame frame, String globPattern) {
|
@TruffleBoundary
|
||||||
if (readResult == null) {
|
public Object read(String globPattern) {
|
||||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
var cachedResult = cachedResults.get(globPattern);
|
||||||
var context = VmContext.get(this);
|
if (cachedResult != null) return cachedResult;
|
||||||
var resolvedUri = doResolveUri(globPattern);
|
|
||||||
var uris =
|
// use same check as for globbed imports (see AstBuilder)
|
||||||
context
|
if (globPattern.startsWith("...")) {
|
||||||
.getResourceManager()
|
throw exceptionBuilder().evalError("cannotGlobTripleDots").build();
|
||||||
.resolveGlob(resolvedUri, currentModule.getUri(), currentModule, this, globPattern);
|
}
|
||||||
var members = buildMembers(frame.getFrameDescriptor(), uris);
|
var globUri = parseUri(globPattern);
|
||||||
readResult =
|
var context = VmContext.get(this);
|
||||||
new VmMapping(frame.materialize(), BaseModule.getMappingClass().getPrototype(), members);
|
try {
|
||||||
|
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(resolvedElements.size());
|
||||||
|
for (var entry : resolvedElements.entrySet()) {
|
||||||
|
builder.addEntry(entry.getKey(), getMemberNode());
|
||||||
|
}
|
||||||
|
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("invalidGlobPattern", globPattern)
|
||||||
|
.withHint(e.getMessage())
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
return readResult;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright © 2024 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.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.pkl.core.ast.expression.unary;
|
|
||||||
|
|
||||||
import com.oracle.truffle.api.CompilerDirectives;
|
|
||||||
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
|
|
||||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
|
||||||
import java.net.URI;
|
|
||||||
import org.pkl.core.runtime.VmContext;
|
|
||||||
import org.pkl.core.runtime.VmUtils;
|
|
||||||
import org.pkl.core.util.LateInit;
|
|
||||||
|
|
||||||
/** Used by {@link ReadGlobNode}. */
|
|
||||||
public class StaticReadNode extends UnaryExpressionNode {
|
|
||||||
private final URI resourceUri;
|
|
||||||
|
|
||||||
@CompilationFinal @LateInit private Object readResult;
|
|
||||||
|
|
||||||
public StaticReadNode(URI resourceUri) {
|
|
||||||
super(VmUtils.unavailableSourceSection());
|
|
||||||
assert resourceUri.isAbsolute();
|
|
||||||
this.resourceUri = resourceUri;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object executeGeneric(VirtualFrame frame) {
|
|
||||||
if (readResult == null) {
|
|
||||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
|
||||||
var context = VmContext.get(this);
|
|
||||||
readResult = context.getResourceManager().read(resourceUri, this).orElse(null);
|
|
||||||
if (readResult == null) {
|
|
||||||
throw exceptionBuilder().evalError("cannotFindResource", resourceUri).build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return readResult;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -27,7 +27,6 @@ import org.pkl.core.PClass;
|
|||||||
import org.pkl.core.PType;
|
import org.pkl.core.PType;
|
||||||
import org.pkl.core.TypeParameter;
|
import org.pkl.core.TypeParameter;
|
||||||
import org.pkl.core.ast.ExpressionNode;
|
import org.pkl.core.ast.ExpressionNode;
|
||||||
import org.pkl.core.ast.MemberNode;
|
|
||||||
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.ast.type.VmTypeMismatchException;
|
import org.pkl.core.ast.type.VmTypeMismatchException;
|
||||||
@@ -36,7 +35,7 @@ import org.pkl.core.util.CollectionUtils;
|
|||||||
import org.pkl.core.util.Nullable;
|
import org.pkl.core.util.Nullable;
|
||||||
import org.pkl.core.util.Pair;
|
import org.pkl.core.util.Pair;
|
||||||
|
|
||||||
public final class FunctionNode extends MemberNode {
|
public final class FunctionNode extends RegularMemberNode {
|
||||||
// Every function (and property) call passes two implicit arguments at positions
|
// Every function (and property) call passes two implicit arguments at positions
|
||||||
// frame.getArguments()[0] and [1]:
|
// frame.getArguments()[0] and [1]:
|
||||||
// - the receiver (target) of the call, of type Object (see VmUtils.getReceiver())
|
// - the receiver (target) of the call, of type Object (see VmUtils.getReceiver())
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import com.oracle.truffle.api.CompilerDirectives;
|
|||||||
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 org.pkl.core.ast.ExpressionNode;
|
import org.pkl.core.ast.ExpressionNode;
|
||||||
import org.pkl.core.ast.MemberNode;
|
|
||||||
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.ast.type.VmTypeMismatchException;
|
import org.pkl.core.ast.type.VmTypeMismatchException;
|
||||||
@@ -28,7 +27,7 @@ import org.pkl.core.runtime.VmLanguage;
|
|||||||
import org.pkl.core.util.LateInit;
|
import org.pkl.core.util.LateInit;
|
||||||
import org.pkl.core.util.Nullable;
|
import org.pkl.core.util.Nullable;
|
||||||
|
|
||||||
public final class LocalTypedPropertyNode extends MemberNode {
|
public final class LocalTypedPropertyNode extends RegularMemberNode {
|
||||||
private final VmLanguage language;
|
private final VmLanguage language;
|
||||||
@Child private UnresolvedTypeNode unresolvedTypeNode;
|
@Child private UnresolvedTypeNode unresolvedTypeNode;
|
||||||
@Child @LateInit private TypeNode typeNode;
|
@Child @LateInit private TypeNode typeNode;
|
||||||
|
|||||||
@@ -21,14 +21,13 @@ import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
|
|||||||
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 org.pkl.core.ast.ExpressionNode;
|
import org.pkl.core.ast.ExpressionNode;
|
||||||
import org.pkl.core.ast.MemberNode;
|
|
||||||
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;
|
import org.pkl.core.util.LateInit;
|
||||||
import org.pkl.core.util.Nullable;
|
import org.pkl.core.util.Nullable;
|
||||||
|
|
||||||
public final class ObjectMethodNode extends MemberNode {
|
public final class ObjectMethodNode extends RegularMemberNode {
|
||||||
private final VmLanguage language;
|
private final VmLanguage language;
|
||||||
private final int parameterCount;
|
private final int parameterCount;
|
||||||
@Children private final @Nullable UnresolvedTypeNode[] unresolvedParameterTypeNodes;
|
@Children private final @Nullable UnresolvedTypeNode[] unresolvedParameterTypeNodes;
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2024 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.pkl.core.ast.member;
|
||||||
|
|
||||||
|
import com.oracle.truffle.api.frame.FrameDescriptor;
|
||||||
|
import com.oracle.truffle.api.source.SourceSection;
|
||||||
|
import org.pkl.core.ast.ExpressionNode;
|
||||||
|
import org.pkl.core.ast.MemberNode;
|
||||||
|
import org.pkl.core.runtime.VmLanguage;
|
||||||
|
import org.pkl.core.util.Nullable;
|
||||||
|
|
||||||
|
/** A {@code MemberNode} that belongs to a single {@link Member}. */
|
||||||
|
public abstract class RegularMemberNode extends MemberNode {
|
||||||
|
protected final Member member;
|
||||||
|
|
||||||
|
protected RegularMemberNode(
|
||||||
|
@Nullable VmLanguage language,
|
||||||
|
FrameDescriptor descriptor,
|
||||||
|
Member member,
|
||||||
|
ExpressionNode bodyNode) {
|
||||||
|
|
||||||
|
super(language, descriptor, bodyNode);
|
||||||
|
this.member = member;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final SourceSection getSourceSection() {
|
||||||
|
return member.getSourceSection();
|
||||||
|
}
|
||||||
|
|
||||||
|
public final SourceSection getHeaderSection() {
|
||||||
|
return member.getHeaderSection();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final String getName() {
|
||||||
|
return member.getQualifiedName();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2024 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.pkl.core.ast.member;
|
||||||
|
|
||||||
|
import com.oracle.truffle.api.frame.FrameDescriptor;
|
||||||
|
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||||
|
import com.oracle.truffle.api.source.SourceSection;
|
||||||
|
import org.pkl.core.ast.ExpressionNode;
|
||||||
|
import org.pkl.core.ast.MemberNode;
|
||||||
|
import org.pkl.core.runtime.VmLanguage;
|
||||||
|
import org.pkl.core.util.Nullable;
|
||||||
|
|
||||||
|
/** A {@code MemberNode} that is shared between multiple {@linkplain Member members}. */
|
||||||
|
public class SharedMemberNode extends MemberNode {
|
||||||
|
private final SourceSection sourceSection;
|
||||||
|
private final SourceSection headerSection;
|
||||||
|
private final @Nullable String qualifiedName;
|
||||||
|
|
||||||
|
public SharedMemberNode(
|
||||||
|
SourceSection sourceSection,
|
||||||
|
SourceSection headerSection,
|
||||||
|
@Nullable String qualifiedName,
|
||||||
|
@Nullable VmLanguage language,
|
||||||
|
FrameDescriptor descriptor,
|
||||||
|
ExpressionNode bodyNode) {
|
||||||
|
|
||||||
|
super(language, descriptor, bodyNode);
|
||||||
|
this.sourceSection = sourceSection;
|
||||||
|
this.headerSection = headerSection;
|
||||||
|
this.qualifiedName = qualifiedName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SourceSection getSourceSection() {
|
||||||
|
return sourceSection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SourceSection getHeaderSection() {
|
||||||
|
return headerSection;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable String getName() {
|
||||||
|
return qualifiedName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object execute(VirtualFrame frame) {
|
||||||
|
return executeBody(frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,13 +23,12 @@ 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.IndirectCallNode;
|
import com.oracle.truffle.api.nodes.IndirectCallNode;
|
||||||
import org.pkl.core.ast.ExpressionNode;
|
import org.pkl.core.ast.ExpressionNode;
|
||||||
import org.pkl.core.ast.MemberNode;
|
|
||||||
import org.pkl.core.ast.expression.primary.GetOwnerNode;
|
import org.pkl.core.ast.expression.primary.GetOwnerNode;
|
||||||
import org.pkl.core.runtime.*;
|
import org.pkl.core.runtime.*;
|
||||||
import org.pkl.core.util.Nullable;
|
import org.pkl.core.util.Nullable;
|
||||||
|
|
||||||
/** A property definition that does not have a type annotation but should be type-checked. */
|
/** A property definition that does not have a type annotation but should be type-checked. */
|
||||||
public abstract class TypeCheckedPropertyNode extends MemberNode {
|
public abstract class TypeCheckedPropertyNode extends RegularMemberNode {
|
||||||
@Child @Executed protected ExpressionNode ownerNode = new GetOwnerNode();
|
@Child @Executed protected ExpressionNode ownerNode = new GetOwnerNode();
|
||||||
|
|
||||||
protected TypeCheckedPropertyNode(
|
protected TypeCheckedPropertyNode(
|
||||||
@@ -55,7 +54,7 @@ public abstract class TypeCheckedPropertyNode extends MemberNode {
|
|||||||
var result = executeBody(frame);
|
var result = executeBody(frame);
|
||||||
|
|
||||||
// TODO: propagate SUPER_CALL_MARKER to disable constraint (but not type) check
|
// TODO: propagate SUPER_CALL_MARKER to disable constraint (but not type) check
|
||||||
if (callNode != null && !shouldRunTypecheck(frame)) {
|
if (callNode != null && shouldRunTypeCheck(frame)) {
|
||||||
callNode.call(VmUtils.getReceiverOrNull(frame), property.getOwner(), result);
|
callNode.call(VmUtils.getReceiverOrNull(frame), property.getOwner(), result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +67,7 @@ public abstract class TypeCheckedPropertyNode extends MemberNode {
|
|||||||
|
|
||||||
var result = executeBody(frame);
|
var result = executeBody(frame);
|
||||||
|
|
||||||
if (!shouldRunTypecheck(frame)) {
|
if (shouldRunTypeCheck(frame)) {
|
||||||
var property = getProperty(owner.getVmClass());
|
var property = getProperty(owner.getVmClass());
|
||||||
var typeAnnNode = property.getTypeNode();
|
var typeAnnNode = property.getTypeNode();
|
||||||
if (typeAnnNode != null) {
|
if (typeAnnNode != null) {
|
||||||
|
|||||||
@@ -20,12 +20,11 @@ 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.nodes.DirectCallNode;
|
import com.oracle.truffle.api.nodes.DirectCallNode;
|
||||||
import org.pkl.core.ast.ExpressionNode;
|
import org.pkl.core.ast.ExpressionNode;
|
||||||
import org.pkl.core.ast.MemberNode;
|
|
||||||
import org.pkl.core.runtime.VmLanguage;
|
import org.pkl.core.runtime.VmLanguage;
|
||||||
import org.pkl.core.runtime.VmUtils;
|
import org.pkl.core.runtime.VmUtils;
|
||||||
|
|
||||||
/** A property definition that has a type annotation. */
|
/** A property definition that has a type annotation. */
|
||||||
public final class TypedPropertyNode extends MemberNode {
|
public final class TypedPropertyNode extends RegularMemberNode {
|
||||||
@Child private DirectCallNode typeCheckCallNode;
|
@Child private DirectCallNode typeCheckCallNode;
|
||||||
|
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
@@ -46,7 +45,7 @@ public final class TypedPropertyNode extends MemberNode {
|
|||||||
@Override
|
@Override
|
||||||
public Object execute(VirtualFrame frame) {
|
public Object execute(VirtualFrame frame) {
|
||||||
var propertyValue = executeBody(frame);
|
var propertyValue = executeBody(frame);
|
||||||
if (!shouldRunTypecheck(frame)) {
|
if (shouldRunTypeCheck(frame)) {
|
||||||
typeCheckCallNode.call(VmUtils.getReceiver(frame), VmUtils.getOwner(frame), propertyValue);
|
typeCheckCallNode.call(VmUtils.getReceiver(frame), VmUtils.getOwner(frame), propertyValue);
|
||||||
}
|
}
|
||||||
return propertyValue;
|
return propertyValue;
|
||||||
|
|||||||
@@ -18,11 +18,10 @@ 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 org.pkl.core.ast.ExpressionNode;
|
import org.pkl.core.ast.ExpressionNode;
|
||||||
import org.pkl.core.ast.MemberNode;
|
|
||||||
import org.pkl.core.runtime.VmLanguage;
|
import org.pkl.core.runtime.VmLanguage;
|
||||||
import org.pkl.core.util.Nullable;
|
import org.pkl.core.util.Nullable;
|
||||||
|
|
||||||
public final class UntypedObjectMemberNode extends MemberNode {
|
public final class UntypedObjectMemberNode extends RegularMemberNode {
|
||||||
public UntypedObjectMemberNode(
|
public UntypedObjectMemberNode(
|
||||||
@Nullable VmLanguage language,
|
@Nullable VmLanguage language,
|
||||||
FrameDescriptor descriptor,
|
FrameDescriptor descriptor,
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ import java.util.stream.Collectors;
|
|||||||
import org.pkl.core.Release;
|
import org.pkl.core.Release;
|
||||||
import org.pkl.core.SecurityManager;
|
import org.pkl.core.SecurityManager;
|
||||||
import org.pkl.core.SecurityManagerException;
|
import org.pkl.core.SecurityManagerException;
|
||||||
import org.pkl.core.ast.expression.unary.ImportNode;
|
|
||||||
import org.pkl.core.module.ModuleKey;
|
import org.pkl.core.module.ModuleKey;
|
||||||
import org.pkl.core.module.ModuleKeys;
|
import org.pkl.core.module.ModuleKeys;
|
||||||
import org.pkl.core.module.ResolvedModuleKey;
|
import org.pkl.core.module.ResolvedModuleKey;
|
||||||
@@ -77,7 +76,7 @@ public final class ModuleCache {
|
|||||||
ModuleResolver moduleResolver,
|
ModuleResolver moduleResolver,
|
||||||
Supplier<VmTyped> moduleInstantiator,
|
Supplier<VmTyped> moduleInstantiator,
|
||||||
ModuleInitializer moduleInitializer,
|
ModuleInitializer moduleInitializer,
|
||||||
@Nullable ImportNode importNode) {
|
@Nullable Node importNode) {
|
||||||
|
|
||||||
if (ModuleKeys.isStdLibModule(moduleKey)) {
|
if (ModuleKeys.isStdLibModule(moduleKey)) {
|
||||||
var moduleName = moduleKey.getUri().getSchemeSpecificPart();
|
var moduleName = moduleKey.getUri().getSchemeSpecificPart();
|
||||||
@@ -162,7 +161,7 @@ public final class ModuleCache {
|
|||||||
ModuleResolver moduleResolver,
|
ModuleResolver moduleResolver,
|
||||||
Supplier<VmTyped> moduleInstantiator,
|
Supplier<VmTyped> moduleInstantiator,
|
||||||
ModuleInitializer moduleInitializer,
|
ModuleInitializer moduleInitializer,
|
||||||
@Nullable ImportNode importNode) {
|
@Nullable Node importNode) {
|
||||||
|
|
||||||
VmTyped module = moduleInstantiator.get();
|
VmTyped module = moduleInstantiator.get();
|
||||||
|
|
||||||
@@ -189,7 +188,7 @@ public final class ModuleCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ResolvedModuleKey resolve(
|
private ResolvedModuleKey resolve(
|
||||||
ModuleKey module, SecurityManager securityManager, @Nullable ImportNode importNode) {
|
ModuleKey module, SecurityManager securityManager, @Nullable Node importNode) {
|
||||||
try {
|
try {
|
||||||
return module.resolve(securityManager);
|
return module.resolve(securityManager);
|
||||||
} catch (SecurityManagerException | PackageLoadError e) {
|
} catch (SecurityManagerException | PackageLoadError e) {
|
||||||
|
|||||||
@@ -22,8 +22,7 @@ import org.pkl.core.ModuleSchema;
|
|||||||
import org.pkl.core.PClass;
|
import org.pkl.core.PClass;
|
||||||
import org.pkl.core.TypeAlias;
|
import org.pkl.core.TypeAlias;
|
||||||
import org.pkl.core.ast.MemberNode;
|
import org.pkl.core.ast.MemberNode;
|
||||||
import org.pkl.core.ast.expression.unary.ImportGlobNode;
|
import org.pkl.core.ast.expression.unary.AbstractImportNode;
|
||||||
import org.pkl.core.ast.expression.unary.ImportNode;
|
|
||||||
import org.pkl.core.module.ModuleKey;
|
import org.pkl.core.module.ModuleKey;
|
||||||
import org.pkl.core.module.ResolvedModuleKey;
|
import org.pkl.core.module.ResolvedModuleKey;
|
||||||
import org.pkl.core.util.EconomicMaps;
|
import org.pkl.core.util.EconomicMaps;
|
||||||
@@ -132,11 +131,8 @@ public final class ModuleInfo {
|
|||||||
if (propertyDef.isImport()) {
|
if (propertyDef.isImport()) {
|
||||||
MemberNode memberNode = propertyDef.getMemberNode();
|
MemberNode memberNode = propertyDef.getMemberNode();
|
||||||
assert memberNode != null; // import is never a constant
|
assert memberNode != null; // import is never a constant
|
||||||
var importNode = memberNode.getBodyNode();
|
var importNode = (AbstractImportNode) memberNode.getBodyNode();
|
||||||
var importUri =
|
var importUri = importNode.getImportUri();
|
||||||
importNode instanceof ImportNode
|
|
||||||
? ((ImportNode) importNode).getImportUri()
|
|
||||||
: ((ImportGlobNode) importNode).getImportUri();
|
|
||||||
imports.put(propertyDef.getName().toString(), importUri);
|
imports.put(propertyDef.getName().toString(), importUri);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,20 +22,15 @@ import java.net.URI;
|
|||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import org.pkl.core.SecurityManager;
|
import org.pkl.core.SecurityManager;
|
||||||
import org.pkl.core.SecurityManagerException;
|
import org.pkl.core.SecurityManagerException;
|
||||||
import org.pkl.core.http.HttpClientInitException;
|
import org.pkl.core.http.HttpClientInitException;
|
||||||
import org.pkl.core.module.ModuleKey;
|
|
||||||
import org.pkl.core.packages.PackageLoadError;
|
import org.pkl.core.packages.PackageLoadError;
|
||||||
import org.pkl.core.resource.Resource;
|
import org.pkl.core.resource.Resource;
|
||||||
import org.pkl.core.resource.ResourceReader;
|
import org.pkl.core.resource.ResourceReader;
|
||||||
import org.pkl.core.stdlib.VmObjectFactory;
|
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 {
|
public final class ResourceManager {
|
||||||
private final Map<String, ResourceReader> resourceReaders = new HashMap<>();
|
private final Map<String, ResourceReader> resourceReaders = new HashMap<>();
|
||||||
@@ -45,8 +40,6 @@ public final class ResourceManager {
|
|||||||
// cache resources indefinitely to make resource reads deterministic
|
// cache resources indefinitely to make resource reads deterministic
|
||||||
private final Map<URI, Optional<Object>> resources = new HashMap<>();
|
private final Map<URI, Optional<Object>> resources = new HashMap<>();
|
||||||
|
|
||||||
private final Map<URI, List<ResolvedGlobElement>> globExpressions = new HashMap<>();
|
|
||||||
|
|
||||||
public ResourceManager(SecurityManager securityManager, Collection<ResourceReader> readers) {
|
public ResourceManager(SecurityManager securityManager, Collection<ResourceReader> readers) {
|
||||||
this.securityManager = securityManager;
|
this.securityManager = securityManager;
|
||||||
|
|
||||||
@@ -61,61 +54,16 @@ public final class ResourceManager {
|
|||||||
.addProperty("base64", Resource::getBase64);
|
.addProperty("base64", Resource::getBase64);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolves the glob URI into a set of URIs.
|
|
||||||
*
|
|
||||||
* <p>The glob URI must be absolute. For example: {@code "file:///foo/bar/*.pkl"}.
|
|
||||||
*/
|
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
public List<ResolvedGlobElement> resolveGlob(
|
public ResourceReader getReader(URI resourceUri, Node readNode) {
|
||||||
URI globUri,
|
var reader = resourceReaders.get(resourceUri.getScheme());
|
||||||
URI enclosingUri,
|
if (reader == null) {
|
||||||
ModuleKey enclosingModuleKey,
|
throw new VmExceptionBuilder()
|
||||||
Node readNode,
|
.withLocation(readNode)
|
||||||
String globExpression) {
|
.evalError("noResourceReaderRegistered", resourceUri.getScheme())
|
||||||
return globExpressions.computeIfAbsent(
|
.build();
|
||||||
globUri.normalize(),
|
}
|
||||||
uri -> {
|
return reader;
|
||||||
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();
|
|
||||||
}
|
|
||||||
var securityManager = VmContext.get(readNode).getSecurityManager();
|
|
||||||
return GlobResolver.resolveGlob(
|
|
||||||
securityManager, reader, enclosingModuleKey, enclosingUri, globExpression);
|
|
||||||
} catch (InvalidGlobPatternException e) {
|
|
||||||
throw new VmExceptionBuilder()
|
|
||||||
.evalError("invalidGlobPattern", globExpression)
|
|
||||||
.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", globExpression)
|
|
||||||
.withCause(e)
|
|
||||||
.withLocation(readNode)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
@@ -129,13 +77,7 @@ public final class ResourceManager {
|
|||||||
throw new VmExceptionBuilder().withCause(e).withLocation(readNode).build();
|
throw new VmExceptionBuilder().withCause(e).withLocation(readNode).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
var reader = resourceReaders.get(uri.getScheme());
|
var reader = getReader(resourceUri, readNode);
|
||||||
if (reader == null) {
|
|
||||||
throw new VmExceptionBuilder()
|
|
||||||
.withLocation(readNode)
|
|
||||||
.evalError("noResourceReaderRegistered", resourceUri.getScheme())
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional<Object> resource;
|
Optional<Object> resource;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import com.oracle.truffle.api.TruffleLanguage.ContextPolicy;
|
|||||||
import com.oracle.truffle.api.nodes.Node;
|
import com.oracle.truffle.api.nodes.Node;
|
||||||
import com.oracle.truffle.api.source.Source;
|
import com.oracle.truffle.api.source.Source;
|
||||||
import org.pkl.core.ast.builder.AstBuilder;
|
import org.pkl.core.ast.builder.AstBuilder;
|
||||||
import org.pkl.core.ast.expression.unary.ImportNode;
|
|
||||||
import org.pkl.core.module.ModuleKey;
|
import org.pkl.core.module.ModuleKey;
|
||||||
import org.pkl.core.module.ResolvedModuleKey;
|
import org.pkl.core.module.ResolvedModuleKey;
|
||||||
import org.pkl.core.parser.LexParseException;
|
import org.pkl.core.parser.LexParseException;
|
||||||
@@ -73,7 +72,7 @@ public final class VmLanguage extends TruffleLanguage<VmContext> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
public VmTyped loadModule(ModuleKey moduleKey, ImportNode importNode) {
|
public VmTyped loadModule(ModuleKey moduleKey, @Nullable Node importNode) {
|
||||||
var context = VmContext.get(null);
|
var context = VmContext.get(null);
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|||||||
@@ -382,6 +382,20 @@ public final class VmList extends VmCollection {
|
|||||||
return VmSet.create(rrbt);
|
return VmSet.create(rrbt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TruffleBoundary
|
||||||
|
public VmListing toListing() {
|
||||||
|
var builder = new VmObjectBuilder(rrbt.size());
|
||||||
|
for (var elem : rrbt) builder.addElement(elem);
|
||||||
|
return builder.toListing();
|
||||||
|
}
|
||||||
|
|
||||||
|
@TruffleBoundary
|
||||||
|
public VmDynamic toDynamic() {
|
||||||
|
var builder = new VmObjectBuilder(rrbt.size());
|
||||||
|
for (var elem : rrbt) builder.addElement(elem);
|
||||||
|
return builder.toDynamic();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
public void force(boolean allowUndefinedValues) {
|
public void force(boolean allowUndefinedValues) {
|
||||||
|
|||||||
@@ -225,6 +225,28 @@ public final class VmMap extends VmValue implements Iterable<Map.Entry<Object, O
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public VmMapping toMapping() {
|
||||||
|
var builder = new VmObjectBuilder(getLength());
|
||||||
|
for (var entry : this) {
|
||||||
|
builder.addEntry(VmUtils.getKey(entry), VmUtils.getValue(entry));
|
||||||
|
}
|
||||||
|
return builder.toMapping();
|
||||||
|
}
|
||||||
|
|
||||||
|
public VmDynamic toDynamic() {
|
||||||
|
var builder = new VmObjectBuilder(getLength());
|
||||||
|
for (var entry : this) {
|
||||||
|
var key = VmUtils.getKey(entry);
|
||||||
|
var value = VmUtils.getValue(entry);
|
||||||
|
if (key instanceof String) {
|
||||||
|
builder.addProperty(Identifier.get((String) key), value);
|
||||||
|
} else {
|
||||||
|
builder.addEntry(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder.toDynamic();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
public Map<Object, Object> export() {
|
public Map<Object, Object> export() {
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2024 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.pkl.core.runtime;
|
||||||
|
|
||||||
|
import org.graalvm.collections.EconomicMap;
|
||||||
|
import org.pkl.core.ast.VmModifier;
|
||||||
|
import org.pkl.core.ast.member.ObjectMember;
|
||||||
|
import org.pkl.core.ast.member.SharedMemberNode;
|
||||||
|
import org.pkl.core.util.EconomicMaps;
|
||||||
|
|
||||||
|
/** A builder for {@link VmObject}s whose {@link ObjectMember}s are determined at run time. */
|
||||||
|
public final class VmObjectBuilder {
|
||||||
|
private final EconomicMap<Object, ObjectMember> members;
|
||||||
|
private int elementCount = 0;
|
||||||
|
|
||||||
|
public VmObjectBuilder(int initialSize) {
|
||||||
|
members = EconomicMaps.create(initialSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public VmObjectBuilder addProperty(Identifier name, Object value) {
|
||||||
|
EconomicMaps.put(members, name, VmUtils.createSyntheticObjectProperty(name, "", value));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public VmObjectBuilder addElement(Object value) {
|
||||||
|
EconomicMaps.put(
|
||||||
|
members, (long) elementCount++, VmUtils.createSyntheticObjectElement("", value));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public VmObjectBuilder addEntry(Object key, Object value) {
|
||||||
|
EconomicMaps.put(members, key, VmUtils.createSyntheticObjectEntry("", value));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public VmObjectBuilder addEntry(Object key, SharedMemberNode valueNode) {
|
||||||
|
var entry =
|
||||||
|
new ObjectMember(
|
||||||
|
valueNode.getSourceSection(), valueNode.getHeaderSection(), VmModifier.ENTRY, null, "");
|
||||||
|
entry.initMemberNode(valueNode);
|
||||||
|
EconomicMaps.put(members, key, entry);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public VmListing toListing() {
|
||||||
|
return new VmListing(
|
||||||
|
VmUtils.createEmptyMaterializedFrame(),
|
||||||
|
BaseModule.getListingClass().getPrototype(),
|
||||||
|
members,
|
||||||
|
elementCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public VmMapping toMapping() {
|
||||||
|
return new VmMapping(
|
||||||
|
VmUtils.createEmptyMaterializedFrame(),
|
||||||
|
BaseModule.getMappingClass().getPrototype(),
|
||||||
|
members);
|
||||||
|
}
|
||||||
|
|
||||||
|
public VmMapping toMapping(Object extraStorage) {
|
||||||
|
var result = toMapping();
|
||||||
|
result.setExtraStorage(extraStorage);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public VmDynamic toDynamic() {
|
||||||
|
return new VmDynamic(
|
||||||
|
VmUtils.createEmptyMaterializedFrame(),
|
||||||
|
BaseModule.getDynamicClass().getPrototype(),
|
||||||
|
members,
|
||||||
|
elementCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -282,6 +282,20 @@ public final class VmSet extends VmCollection {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TruffleBoundary
|
||||||
|
public VmListing toListing() {
|
||||||
|
var builder = new VmObjectBuilder(elementOrder.size());
|
||||||
|
for (var elem : elementOrder) builder.addElement(elem);
|
||||||
|
return builder.toListing();
|
||||||
|
}
|
||||||
|
|
||||||
|
@TruffleBoundary
|
||||||
|
public VmDynamic toDynamic() {
|
||||||
|
var builder = new VmObjectBuilder(elementOrder.size());
|
||||||
|
for (var elem : elementOrder) builder.addElement(elem);
|
||||||
|
return builder.toDynamic();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
public void force(boolean allowUndefinedValues) {
|
public void force(boolean allowUndefinedValues) {
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ import org.pkl.core.util.EconomicMaps;
|
|||||||
import org.pkl.core.util.Nullable;
|
import org.pkl.core.util.Nullable;
|
||||||
|
|
||||||
public final class VmUtils {
|
public final class VmUtils {
|
||||||
/** See {@link MemberNode#shouldRunTypecheck(VirtualFrame)}. */
|
/** See {@link MemberNode#shouldRunTypeCheck(VirtualFrame)}. */
|
||||||
@SuppressWarnings("JavadocReference")
|
@SuppressWarnings("JavadocReference")
|
||||||
public static final Object SKIP_TYPECHECK_MARKER = new Object();
|
public static final Object SKIP_TYPECHECK_MARKER = new Object();
|
||||||
|
|
||||||
@@ -844,4 +844,9 @@ public final class VmUtils {
|
|||||||
public static int findAuxiliarySlot(VirtualFrame frame, Object identifier) {
|
public static int findAuxiliarySlot(VirtualFrame frame, Object identifier) {
|
||||||
return frame.getFrameDescriptor().getAuxiliarySlots().getOrDefault(identifier, -1);
|
return frame.getFrameDescriptor().getAuxiliarySlots().getOrDefault(identifier, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TruffleBoundary
|
||||||
|
public static <K, V> V getMapValue(Map<K, V> map, K key) {
|
||||||
|
return map.get(key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,18 +19,15 @@ import com.oracle.truffle.api.CompilerDirectives;
|
|||||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
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.nodes.LoopNode;
|
import com.oracle.truffle.api.nodes.LoopNode;
|
||||||
import org.graalvm.collections.EconomicMap;
|
|
||||||
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;
|
||||||
import org.pkl.core.ast.lambda.*;
|
import org.pkl.core.ast.lambda.*;
|
||||||
import org.pkl.core.ast.member.ObjectMember;
|
|
||||||
import org.pkl.core.runtime.*;
|
import org.pkl.core.runtime.*;
|
||||||
import org.pkl.core.stdlib.*;
|
import org.pkl.core.stdlib.*;
|
||||||
import org.pkl.core.stdlib.base.CollectionNodes.CompareByNode;
|
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.EconomicMaps;
|
|
||||||
import org.pkl.core.util.EconomicSets;
|
import org.pkl.core.util.EconomicSets;
|
||||||
|
|
||||||
// duplication between ListNodes and SetNodes is "intentional"
|
// duplication between ListNodes and SetNodes is "intentional"
|
||||||
@@ -1295,11 +1292,7 @@ public final class ListNodes {
|
|||||||
@Specialization
|
@Specialization
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
protected VmListing eval(VmList self) {
|
protected VmListing eval(VmList self) {
|
||||||
return new VmListing(
|
return self.toListing();
|
||||||
VmUtils.createEmptyMaterializedFrame(),
|
|
||||||
BaseModule.getListingClass().getPrototype(),
|
|
||||||
toObjectMembers(self),
|
|
||||||
self.getLength());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1307,22 +1300,7 @@ public final class ListNodes {
|
|||||||
@Specialization
|
@Specialization
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
protected VmDynamic eval(VmList self) {
|
protected VmDynamic eval(VmList self) {
|
||||||
return new VmDynamic(
|
return self.toDynamic();
|
||||||
VmUtils.createEmptyMaterializedFrame(),
|
|
||||||
BaseModule.getDynamicClass().getPrototype(),
|
|
||||||
toObjectMembers(self),
|
|
||||||
self.getLength());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static EconomicMap<Object, ObjectMember> toObjectMembers(VmList self) {
|
|
||||||
var result = EconomicMaps.<Object, ObjectMember>create(self.getLength());
|
|
||||||
|
|
||||||
for (long idx = 0; idx < self.getLength(); idx++) {
|
|
||||||
EconomicMaps.put(
|
|
||||||
result, idx, VmUtils.createSyntheticObjectElement(String.valueOf(idx), self.get(idx)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,13 +21,11 @@ import com.oracle.truffle.api.dsl.Specialization;
|
|||||||
import com.oracle.truffle.api.nodes.LoopNode;
|
import com.oracle.truffle.api.nodes.LoopNode;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.pkl.core.ast.lambda.*;
|
import org.pkl.core.ast.lambda.*;
|
||||||
import org.pkl.core.ast.member.ObjectMember;
|
|
||||||
import org.pkl.core.runtime.*;
|
import org.pkl.core.runtime.*;
|
||||||
import org.pkl.core.stdlib.ExternalMethod0Node;
|
import org.pkl.core.stdlib.ExternalMethod0Node;
|
||||||
import org.pkl.core.stdlib.ExternalMethod1Node;
|
import org.pkl.core.stdlib.ExternalMethod1Node;
|
||||||
import org.pkl.core.stdlib.ExternalMethod2Node;
|
import org.pkl.core.stdlib.ExternalMethod2Node;
|
||||||
import org.pkl.core.stdlib.ExternalPropertyNode;
|
import org.pkl.core.stdlib.ExternalPropertyNode;
|
||||||
import org.pkl.core.util.EconomicMaps;
|
|
||||||
import org.pkl.core.util.MutableReference;
|
import org.pkl.core.util.MutableReference;
|
||||||
|
|
||||||
public final class MapNodes {
|
public final class MapNodes {
|
||||||
@@ -238,28 +236,7 @@ public final class MapNodes {
|
|||||||
public abstract static class toDynamic extends ExternalMethod0Node {
|
public abstract static class toDynamic extends ExternalMethod0Node {
|
||||||
@Specialization
|
@Specialization
|
||||||
protected VmDynamic eval(VmMap self) {
|
protected VmDynamic eval(VmMap self) {
|
||||||
var members = EconomicMaps.<Object, ObjectMember>create(self.getLength());
|
return self.toDynamic();
|
||||||
|
|
||||||
for (var entry : self) {
|
|
||||||
var key = VmUtils.getKey(entry);
|
|
||||||
|
|
||||||
if (key instanceof String string) {
|
|
||||||
var name = Identifier.get(string);
|
|
||||||
EconomicMaps.put(
|
|
||||||
members,
|
|
||||||
name,
|
|
||||||
VmUtils.createSyntheticObjectProperty(name, "", VmUtils.getValue(entry)));
|
|
||||||
} else {
|
|
||||||
EconomicMaps.put(
|
|
||||||
members, key, VmUtils.createSyntheticObjectEntry("", VmUtils.getValue(entry)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new VmDynamic(
|
|
||||||
VmUtils.createEmptyMaterializedFrame(),
|
|
||||||
BaseModule.getDynamicClass().getPrototype(),
|
|
||||||
members,
|
|
||||||
0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,19 +264,7 @@ public final class MapNodes {
|
|||||||
public abstract static class toMapping extends ExternalMethod0Node {
|
public abstract static class toMapping extends ExternalMethod0Node {
|
||||||
@Specialization
|
@Specialization
|
||||||
protected VmMapping eval(VmMap self) {
|
protected VmMapping eval(VmMap self) {
|
||||||
var members = EconomicMaps.<Object, ObjectMember>create(self.getLength());
|
return self.toMapping();
|
||||||
|
|
||||||
for (var entry : self) {
|
|
||||||
EconomicMaps.put(
|
|
||||||
members,
|
|
||||||
VmUtils.getKey(entry),
|
|
||||||
VmUtils.createSyntheticObjectEntry("", VmUtils.getValue(entry)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new VmMapping(
|
|
||||||
VmUtils.createEmptyMaterializedFrame(),
|
|
||||||
BaseModule.getMappingClass().getPrototype(),
|
|
||||||
members);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import com.oracle.truffle.api.CompilerDirectives;
|
|||||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
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.nodes.LoopNode;
|
import com.oracle.truffle.api.nodes.LoopNode;
|
||||||
import org.graalvm.collections.EconomicMap;
|
|
||||||
import org.pkl.core.ast.expression.binary.GreaterThanNode;
|
import org.pkl.core.ast.expression.binary.GreaterThanNode;
|
||||||
import org.pkl.core.ast.expression.binary.GreaterThanNodeGen;
|
import org.pkl.core.ast.expression.binary.GreaterThanNodeGen;
|
||||||
import org.pkl.core.ast.expression.binary.LessThanNode;
|
import org.pkl.core.ast.expression.binary.LessThanNode;
|
||||||
@@ -27,13 +26,11 @@ import org.pkl.core.ast.expression.binary.LessThanNodeGen;
|
|||||||
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;
|
||||||
import org.pkl.core.ast.lambda.*;
|
import org.pkl.core.ast.lambda.*;
|
||||||
import org.pkl.core.ast.member.ObjectMember;
|
|
||||||
import org.pkl.core.runtime.*;
|
import org.pkl.core.runtime.*;
|
||||||
import org.pkl.core.stdlib.*;
|
import org.pkl.core.stdlib.*;
|
||||||
import org.pkl.core.stdlib.base.CollectionNodes.CompareByNode;
|
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.EconomicMaps;
|
|
||||||
|
|
||||||
// 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
|
||||||
@@ -1052,11 +1049,7 @@ public final class SetNodes {
|
|||||||
@Specialization
|
@Specialization
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
protected VmListing eval(VmSet self) {
|
protected VmListing eval(VmSet self) {
|
||||||
return new VmListing(
|
return self.toListing();
|
||||||
VmUtils.createEmptyMaterializedFrame(),
|
|
||||||
BaseModule.getListingClass().getPrototype(),
|
|
||||||
toObjectMembers(self),
|
|
||||||
self.getLength());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1064,11 +1057,7 @@ public final class SetNodes {
|
|||||||
@Specialization
|
@Specialization
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
protected VmDynamic eval(VmSet self) {
|
protected VmDynamic eval(VmSet self) {
|
||||||
return new VmDynamic(
|
return self.toDynamic();
|
||||||
VmUtils.createEmptyMaterializedFrame(),
|
|
||||||
BaseModule.getDynamicClass().getPrototype(),
|
|
||||||
toObjectMembers(self),
|
|
||||||
self.getLength());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1110,17 +1099,4 @@ public final class SetNodes {
|
|||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static EconomicMap<Object, ObjectMember> toObjectMembers(VmSet self) {
|
|
||||||
var result = EconomicMaps.<Object, ObjectMember>create(self.getLength());
|
|
||||||
|
|
||||||
long idx = 0;
|
|
||||||
for (var element : self) {
|
|
||||||
EconomicMaps.put(
|
|
||||||
result, idx, VmUtils.createSyntheticObjectElement(String.valueOf(idx), element));
|
|
||||||
idx += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import java.net.URISyntaxException;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
@@ -250,7 +251,7 @@ public final class GlobResolver {
|
|||||||
ReaderBase reader,
|
ReaderBase reader,
|
||||||
URI globUri,
|
URI globUri,
|
||||||
Pattern pattern,
|
Pattern pattern,
|
||||||
ArrayList<ResolvedGlobElement> result)
|
Map<String, ResolvedGlobElement> result)
|
||||||
throws IOException, SecurityManagerException {
|
throws IOException, SecurityManagerException {
|
||||||
var elements = reader.listElements(securityManager, globUri);
|
var elements = reader.listElements(securityManager, globUri);
|
||||||
for (var elem : sorted(elements)) {
|
for (var elem : sorted(elements)) {
|
||||||
@@ -261,7 +262,8 @@ public final class GlobResolver {
|
|||||||
throw new IllegalArgumentException(e.getMessage(), e);
|
throw new IllegalArgumentException(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
if (pattern.matcher(resolvedUri.toString()).matches()) {
|
if (pattern.matcher(resolvedUri.toString()).matches()) {
|
||||||
result.add(new ResolvedGlobElement(resolvedUri.toString(), resolvedUri, false));
|
var path = resolvedUri.toString();
|
||||||
|
result.put(path, new ResolvedGlobElement(path, resolvedUri, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -372,7 +374,7 @@ public final class GlobResolver {
|
|||||||
URI baseUri,
|
URI baseUri,
|
||||||
String expandedGlobPatternSoFar,
|
String expandedGlobPatternSoFar,
|
||||||
boolean hasAbsoluteGlob,
|
boolean hasAbsoluteGlob,
|
||||||
List<ResolvedGlobElement> result,
|
Map<String, ResolvedGlobElement> result,
|
||||||
MutableLong listElementCallCount)
|
MutableLong listElementCallCount)
|
||||||
throws IOException, SecurityManagerException, InvalidGlobPatternException {
|
throws IOException, SecurityManagerException, InvalidGlobPatternException {
|
||||||
var isLeaf = idx == globPatternParts.length - 1;
|
var isLeaf = idx == globPatternParts.length - 1;
|
||||||
@@ -384,7 +386,7 @@ public final class GlobResolver {
|
|||||||
if (reader.hasElement(securityManager, newBaseUri)) {
|
if (reader.hasElement(securityManager, newBaseUri)) {
|
||||||
// Note: isDirectory is not meaningful here. Setting it to false is a way to skip setting
|
// Note: isDirectory is not meaningful here. Setting it to false is a way to skip setting
|
||||||
// it.
|
// it.
|
||||||
result.add(new ResolvedGlobElement(newPath, newBaseUri, false));
|
result.put(newPath, new ResolvedGlobElement(newPath, newBaseUri, false));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var newBaseUri = IoUtils.resolve(reader, baseUri, patternPart + "/");
|
var newBaseUri = IoUtils.resolve(reader, baseUri, patternPart + "/");
|
||||||
@@ -414,7 +416,7 @@ public final class GlobResolver {
|
|||||||
listElementCallCount);
|
listElementCallCount);
|
||||||
for (var element : matchedElements) {
|
for (var element : matchedElements) {
|
||||||
if (isLeaf) {
|
if (isLeaf) {
|
||||||
result.add(element);
|
result.put(element.getPath(), element);
|
||||||
} else if (element.isDirectory()) {
|
} else if (element.isDirectory()) {
|
||||||
resolveHierarchicalGlob(
|
resolveHierarchicalGlob(
|
||||||
securityManager,
|
securityManager,
|
||||||
@@ -465,7 +467,7 @@ public final class GlobResolver {
|
|||||||
* <p>Each pair is the expanded form of the glob pattern, paired with its resolved absolute URI.
|
* <p>Each pair is the expanded form of the glob pattern, paired with its resolved absolute URI.
|
||||||
*/
|
*/
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
public static List<ResolvedGlobElement> resolveGlob(
|
public static Map<String, ResolvedGlobElement> resolveGlob(
|
||||||
SecurityManager securityManager,
|
SecurityManager securityManager,
|
||||||
ReaderBase reader,
|
ReaderBase reader,
|
||||||
ModuleKey enclosingModuleKey,
|
ModuleKey enclosingModuleKey,
|
||||||
@@ -473,7 +475,7 @@ public final class GlobResolver {
|
|||||||
String globPattern)
|
String globPattern)
|
||||||
throws IOException, SecurityManagerException, InvalidGlobPatternException {
|
throws IOException, SecurityManagerException, InvalidGlobPatternException {
|
||||||
|
|
||||||
var result = new ArrayList<ResolvedGlobElement>();
|
var result = new LinkedHashMap<String, ResolvedGlobElement>();
|
||||||
var hasAbsoluteGlob = globPattern.matches("\\w+:.*");
|
var hasAbsoluteGlob = globPattern.matches("\\w+:.*");
|
||||||
|
|
||||||
if (reader.hasHierarchicalUris()) {
|
if (reader.hasHierarchicalUris()) {
|
||||||
@@ -484,7 +486,7 @@ public final class GlobResolver {
|
|||||||
if (globParts.length == 0) {
|
if (globParts.length == 0) {
|
||||||
var resolvedUri = IoUtils.resolve(reader, enclosingUri, globPattern);
|
var resolvedUri = IoUtils.resolve(reader, enclosingUri, globPattern);
|
||||||
if (reader.hasElement(securityManager, resolvedUri)) {
|
if (reader.hasElement(securityManager, resolvedUri)) {
|
||||||
result.add(new ResolvedGlobElement(globPattern, resolvedUri, true));
|
result.put(globPattern, new ResolvedGlobElement(globPattern, resolvedUri, true));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
2
pkl-core/src/test/files/LanguageSnippetTests/input-helper/basic/read/child/module2.pkl
vendored
Normal file
2
pkl-core/src/test/files/LanguageSnippetTests/input-helper/basic/read/child/module2.pkl
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
normalRead = read("resource.txt")
|
||||||
|
globbedRead = read*("*.txt")
|
||||||
1
pkl-core/src/test/files/LanguageSnippetTests/input-helper/basic/read/child/resource.txt
vendored
Normal file
1
pkl-core/src/test/files/LanguageSnippetTests/input-helper/basic/read/child/resource.txt
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
child resource
|
||||||
2
pkl-core/src/test/files/LanguageSnippetTests/input-helper/basic/read/module1.pkl
vendored
Normal file
2
pkl-core/src/test/files/LanguageSnippetTests/input-helper/basic/read/module1.pkl
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
normalRead = read("resource.txt")
|
||||||
|
globbedRead = read*("*.txt")
|
||||||
1
pkl-core/src/test/files/LanguageSnippetTests/input-helper/basic/read/resource.txt
vendored
Normal file
1
pkl-core/src/test/files/LanguageSnippetTests/input-helper/basic/read/resource.txt
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
resource
|
||||||
@@ -22,14 +22,32 @@ examples {
|
|||||||
}
|
}
|
||||||
|
|
||||||
["read file"] {
|
["read file"] {
|
||||||
read("read.pkl")
|
read("globtest/file1.txt")
|
||||||
module.catch(() -> read("other.txt"))
|
module.catch(() -> read("other.txt"))
|
||||||
read?("read.pkl")
|
read?("globtest/file1.txt")
|
||||||
read?("other.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"] {
|
["read non-allowed resource"] {
|
||||||
module.catch(() -> read("forbidden:resource"))
|
module.catch(() -> read("forbidden: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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,4 +30,18 @@ examples {
|
|||||||
["package:"] {
|
["package:"] {
|
||||||
read*("package://localhost:0/birds@0.5.0#/**.pkl")
|
read*("package://localhost:0/birds@0.5.0#/**.pkl")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
["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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,94 +17,80 @@ examples {
|
|||||||
}
|
}
|
||||||
["read file"] {
|
["read file"] {
|
||||||
new {
|
new {
|
||||||
uri = "file:///$snippetsDir/input/basic/read.pkl"
|
uri = "file:///$snippetsDir/input/basic/globtest/file1.txt"
|
||||||
text = """
|
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")
|
|
||||||
}
|
|
||||||
|
|
||||||
["read non-allowed resource"] {
|
|
||||||
module.catch(() -> read("forbidden:resource"))
|
|
||||||
module.catch(() -> read?("forbidden:resource"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
base64 = "YW1lbmRzICIuLi9zbmlwcGV0VGVzdC5wa2wiCgpleGFtcGxlcyB7CiAgWyJyZWFkIGVudiB2YXJpYWJsZSJdIHsKICAgIHJlYWQoImVudjpOQU1FMSIpCiAgICByZWFkKCJlbnY6TkFNRTIiKQogICAgbW9kdWxlLmNhdGNoKCgpIC0+IHJlYWQoImVudjpPVEhFUiIpKQoKICAgIHJlYWQ/KCJlbnY6TkFNRTEiKQogICAgcmVhZD8oImVudjpOQU1FMiIpCiAgICByZWFkPygiZW52Ok9USEVSIikKICB9CgogIFsicmVhZCBleHRlcm5hbCBwcm9wZXJ0eSJdIHsKICAgIHJlYWQoInByb3A6bmFtZTEiKQogICAgcmVhZCgicHJvcDpuYW1lMiIpCiAgICBtb2R1bGUuY2F0Y2goKCkgLT4gcmVhZCgicHJvcDpvdGhlciIpKQoKICAgIHJlYWQ/KCJwcm9wOm5hbWUxIikKICAgIHJlYWQ/KCJwcm9wOm5hbWUyIikKICAgIHJlYWQ/KCJwcm9wOm90aGVyIikKICB9CgogIFsicmVhZCBmaWxlIl0gewogICAgcmVhZCgicmVhZC5wa2wiKQogICAgbW9kdWxlLmNhdGNoKCgpIC0+IHJlYWQoIm90aGVyLnR4dCIpKQogICAgcmVhZD8oInJlYWQucGtsIikKICAgIHJlYWQ/KCJvdGhlci50eHQiKQogIH0KCiAgWyJyZWFkIG5vbi1hbGxvd2VkIHJlc291cmNlIl0gewogICAgbW9kdWxlLmNhdGNoKCgpIC0+IHJlYWQoImZvcmJpZGRlbjpyZXNvdXJjZSIpKQogICAgbW9kdWxlLmNhdGNoKCgpIC0+IHJlYWQ/KCJmb3JiaWRkZW46cmVzb3VyY2UiKSkKICB9Cn0K"
|
base64 = "ZmlsZTEK"
|
||||||
}
|
}
|
||||||
"Cannot find resource `other.txt`."
|
"Cannot find resource `other.txt`."
|
||||||
new {
|
new {
|
||||||
uri = "file:///$snippetsDir/input/basic/read.pkl"
|
uri = "file:///$snippetsDir/input/basic/globtest/file1.txt"
|
||||||
text = """
|
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")
|
|
||||||
}
|
|
||||||
|
|
||||||
["read non-allowed resource"] {
|
|
||||||
module.catch(() -> read("forbidden:resource"))
|
|
||||||
module.catch(() -> read?("forbidden:resource"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
base64 = "YW1lbmRzICIuLi9zbmlwcGV0VGVzdC5wa2wiCgpleGFtcGxlcyB7CiAgWyJyZWFkIGVudiB2YXJpYWJsZSJdIHsKICAgIHJlYWQoImVudjpOQU1FMSIpCiAgICByZWFkKCJlbnY6TkFNRTIiKQogICAgbW9kdWxlLmNhdGNoKCgpIC0+IHJlYWQoImVudjpPVEhFUiIpKQoKICAgIHJlYWQ/KCJlbnY6TkFNRTEiKQogICAgcmVhZD8oImVudjpOQU1FMiIpCiAgICByZWFkPygiZW52Ok9USEVSIikKICB9CgogIFsicmVhZCBleHRlcm5hbCBwcm9wZXJ0eSJdIHsKICAgIHJlYWQoInByb3A6bmFtZTEiKQogICAgcmVhZCgicHJvcDpuYW1lMiIpCiAgICBtb2R1bGUuY2F0Y2goKCkgLT4gcmVhZCgicHJvcDpvdGhlciIpKQoKICAgIHJlYWQ/KCJwcm9wOm5hbWUxIikKICAgIHJlYWQ/KCJwcm9wOm5hbWUyIikKICAgIHJlYWQ/KCJwcm9wOm90aGVyIikKICB9CgogIFsicmVhZCBmaWxlIl0gewogICAgcmVhZCgicmVhZC5wa2wiKQogICAgbW9kdWxlLmNhdGNoKCgpIC0+IHJlYWQoIm90aGVyLnR4dCIpKQogICAgcmVhZD8oInJlYWQucGtsIikKICAgIHJlYWQ/KCJvdGhlci50eHQiKQogIH0KCiAgWyJyZWFkIG5vbi1hbGxvd2VkIHJlc291cmNlIl0gewogICAgbW9kdWxlLmNhdGNoKCgpIC0+IHJlYWQoImZvcmJpZGRlbjpyZXNvdXJjZSIpKQogICAgbW9kdWxlLmNhdGNoKCgpIC0+IHJlYWQ/KCJmb3JiaWRkZW46cmVzb3VyY2UiKSkKICB9Cn0K"
|
base64 = "ZmlsZTEK"
|
||||||
}
|
}
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
["read triple-dot file"] {
|
||||||
|
new {
|
||||||
|
uri = "file:///$snippetsDir/input-helper/basic/read/resource.txt"
|
||||||
|
text = """
|
||||||
|
resource
|
||||||
|
|
||||||
|
"""
|
||||||
|
base64 = "cmVzb3VyY2UK"
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
uri = "file:///$snippetsDir/input-helper/basic/read/resource.txt"
|
||||||
|
text = """
|
||||||
|
resource
|
||||||
|
|
||||||
|
"""
|
||||||
|
base64 = "cmVzb3VyY2UK"
|
||||||
|
}
|
||||||
|
}
|
||||||
["read non-allowed resource"] {
|
["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`)."
|
||||||
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -176,4 +176,66 @@ examples {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
["use read expression with non-constant glob pattern"] {
|
||||||
|
new {
|
||||||
|
["globtest/file1.txt"] {
|
||||||
|
uri = "file:///$snippetsDir/input/basic/globtest/file1.txt"
|
||||||
|
text = """
|
||||||
|
file1
|
||||||
|
|
||||||
|
"""
|
||||||
|
base64 = "ZmlsZTEK"
|
||||||
|
}
|
||||||
|
["globtest/file2.txt"] {
|
||||||
|
uri = "file:///$snippetsDir/input/basic/globtest/file2.txt"
|
||||||
|
text = """
|
||||||
|
file2
|
||||||
|
|
||||||
|
"""
|
||||||
|
base64 = "ZmlsZTIK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
["globtest/file1.txt"] {
|
||||||
|
uri = "file:///$snippetsDir/input/basic/globtest/file1.txt"
|
||||||
|
text = """
|
||||||
|
file1
|
||||||
|
|
||||||
|
"""
|
||||||
|
base64 = "ZmlsZTEK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
["globtest/file2.txt"] {
|
||||||
|
uri = "file:///$snippetsDir/input/basic/globtest/file2.txt"
|
||||||
|
text = """
|
||||||
|
file2
|
||||||
|
|
||||||
|
"""
|
||||||
|
base64 = "ZmlsZTIK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
["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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user