From 3e756ba2a271c24560d375e6931cd754f94f5fd1 Mon Sep 17 00:00:00 2001 From: Davy Landman Date: Wed, 11 May 2022 11:02:47 +0200 Subject: [PATCH 1/5] Added registration functionality for the fallback resolver --- .../rascalmpl/uri/URIResolverRegistry.java | 73 ++++++++++++++++--- 1 file changed, 64 insertions(+), 9 deletions(-) diff --git a/src/org/rascalmpl/uri/URIResolverRegistry.java b/src/org/rascalmpl/uri/URIResolverRegistry.java index 3a8d461724e..71b69c9670b 100644 --- a/src/org/rascalmpl/uri/URIResolverRegistry.java +++ b/src/org/rascalmpl/uri/URIResolverRegistry.java @@ -33,10 +33,12 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.checkerframework.checker.nullness.qual.Nullable; import org.rascalmpl.unicode.UnicodeInputStreamReader; import org.rascalmpl.unicode.UnicodeOffsetLengthReader; import org.rascalmpl.uri.ISourceLocationWatcher.ISourceLocationChanged; @@ -57,6 +59,12 @@ public class URIResolverRegistry { private final Map watchers = new ConcurrentHashMap<>(); private final Map>> watching = new ConcurrentHashMap<>(); + private final AtomicReference<@Nullable ISourceLocationInput> fallbackInputResolver = new AtomicReference<>(); + private final AtomicReference<@Nullable ISourceLocationOutput> fallbackOutputResolver = new AtomicReference<>(); + private final AtomicReference<@Nullable ILogicalSourceLocationResolver> fallbackLogicalResolver = new AtomicReference<>(); + private final AtomicReference<@Nullable IClassloaderLocationResolver> fallbackClassloaderResolver = new AtomicReference<>(); + private final AtomicReference<@Nullable ISourceLocationWatcher> fallbackWatcher = new AtomicReference<>(); + private static class InstanceHolder { static URIResolverRegistry sInstance = new URIResolverRegistry(); } @@ -98,6 +106,10 @@ private void loadServices() { try { Enumeration resources = getClass().getClassLoader().getResources(RESOLVERS_CONFIG); Collections.list(resources).forEach(f -> loadServices(f)); + var fallbackResolverClassName = System.getProperty("rascal.fallbackResolver"); + if (fallbackResolverClassName != null) { + loadFallback(fallbackResolverClassName); + } } catch (IOException e) { throw new Error("WARNING: Could not load URIResolverRegistry extensions from " + RESOLVERS_CONFIG, e); @@ -120,6 +132,57 @@ public Set getRegisteredClassloaderSchemes() { return Collections.unmodifiableSet(classloaderResolvers.keySet()); } + private Object constructService(String name) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException { + Class clazz = Thread.currentThread().getContextClassLoader().loadClass(name); + + try { + return clazz.getDeclaredConstructor(URIResolverRegistry.class).newInstance(this); + } + catch (NoSuchMethodException e) { + return clazz.newInstance(); + } + } + + private void loadFallback(String fallbackClass) { + try { + Object instance = constructService(fallbackClass); + boolean ok = false; + if (instance instanceof ILogicalSourceLocationResolver) { + fallbackLogicalResolver.set((ILogicalSourceLocationResolver) instance); + ok = true; + } + + if (instance instanceof ISourceLocationInput) { + fallbackInputResolver.set((ISourceLocationInput) instance); + ok = true; + } + + if (instance instanceof ISourceLocationOutput) { + fallbackOutputResolver.set((ISourceLocationOutput) instance); + ok = true; + } + + if (instance instanceof IClassloaderLocationResolver) { + fallbackClassloaderResolver.set((IClassloaderLocationResolver) instance); + ok = true; + } + + if (instance instanceof ISourceLocationWatcher) { + fallbackWatcher.set((ISourceLocationWatcher) instance); + } + if (!ok) { + System.err.println("WARNING: could not load fallback resolver " + fallbackClass + + " because it does not implement ISourceLocationInput or ISourceLocationOutput or ILogicalSourceLocationResolver"); + } + } + catch (ClassNotFoundException | InstantiationException | IllegalAccessException | ClassCastException + | IllegalArgumentException | InvocationTargetException | SecurityException e) { + System.err.println("WARNING: could not load resolver due to " + e.getMessage()); + e.printStackTrace(); + } + + } + private void loadServices(URL nextElement) { try { for (String name : readConfigFile(nextElement)) { @@ -130,15 +193,7 @@ private void loadServices(URL nextElement) { continue; } - Class clazz = Thread.currentThread().getContextClassLoader().loadClass(name); - Object instance; - - try { - instance = clazz.getDeclaredConstructor(URIResolverRegistry.class).newInstance(this); - } - catch (NoSuchMethodException e) { - instance = clazz.newInstance(); - } + Object instance = constructService(name); boolean ok = false; From 7b14e9ad204cda29a4e50e28c0666acf3c99329e Mon Sep 17 00:00:00 2001 From: Davy Landman Date: Wed, 11 May 2022 11:46:46 +0200 Subject: [PATCH 2/5] Added fallback resolvers to the mix --- src/org/rascalmpl/library/Prelude.java | 7 +- .../rascalmpl/uri/URIResolverRegistry.java | 146 ++++++++++-------- 2 files changed, 87 insertions(+), 66 deletions(-) diff --git a/src/org/rascalmpl/library/Prelude.java b/src/org/rascalmpl/library/Prelude.java index e096c3b0e5c..60d5edb2e55 100644 --- a/src/org/rascalmpl/library/Prelude.java +++ b/src/org/rascalmpl/library/Prelude.java @@ -1333,10 +1333,13 @@ private void writeFileEnc(ISourceLocation sloc, IString charset, IList V, boolea try { sloc = reg.logicalToPhysical(sloc); - if (reg.supportsInputScheme(sloc.getScheme())) { - if (sloc.hasOffsetLength()) { + + if (sloc.hasOffsetLength()) { + try { prefix = new UnicodeOffsetLengthReader(reg.getCharacterReader(sloc.top(), charset.getValue()), 0, sloc.getOffset() + ( append ? sloc.getLength() : 0 )); postfix = new UnicodeOffsetLengthReader(reg.getCharacterReader(sloc.top(), charset.getValue()), sloc.getOffset() + sloc.getLength(), -1); + } catch (UnsupportedSchemeException e) { + // silently ignoring that we cannot do an append for this scheme as there is no input stream resolver defined for this scheme } } diff --git a/src/org/rascalmpl/uri/URIResolverRegistry.java b/src/org/rascalmpl/uri/URIResolverRegistry.java index 71b69c9670b..ab729ef47c1 100644 --- a/src/org/rascalmpl/uri/URIResolverRegistry.java +++ b/src/org/rascalmpl/uri/URIResolverRegistry.java @@ -59,6 +59,8 @@ public class URIResolverRegistry { private final Map watchers = new ConcurrentHashMap<>(); private final Map>> watching = new ConcurrentHashMap<>(); + // we allow the user to define (using -Drascal.fallbackResolver=fully.qualified.classname) a single class that will handle + // scheme's not statically registered. That class should implement at least one of these interfaces private final AtomicReference<@Nullable ISourceLocationInput> fallbackInputResolver = new AtomicReference<>(); private final AtomicReference<@Nullable ISourceLocationOutput> fallbackOutputResolver = new AtomicReference<>(); private final AtomicReference<@Nullable ILogicalSourceLocationResolver> fallbackLogicalResolver = new AtomicReference<>(); @@ -318,78 +320,99 @@ public ISourceLocation logicalToPhysical(ISourceLocation loc) throws IOException return result; } - private ISourceLocation physicalLocation(ISourceLocation loc) throws IOException { - while (logicalResolvers.containsKey(loc.getScheme())) { - Map map = logicalResolvers.get(loc.getScheme()); - String auth = loc.hasAuthority() ? loc.getAuthority() : ""; - ILogicalSourceLocationResolver resolver = map.get(auth); - ISourceLocation prev = loc; - boolean removedOffset = false; + @FunctionalInterface + private static interface ThrowingFunction { + R apply(A arg) throws E; + } - if (resolver != null) { - loc = resolver.resolve(loc); - } + private static T mapIfPresent(AtomicReference reference, ThrowingFunction mapper, T ifNotPresent) throws E { + var ref = reference.get(); + if (ref != null) { + return mapper.apply(ref); + } + return ifNotPresent; + } - if (loc == null && prev.hasOffsetLength()) { - loc = resolver.resolve(URIUtil.removeOffset(prev)); - removedOffset = true; - } + private static ISourceLocation resolveAndFixOffsets(ISourceLocation loc, ILogicalSourceLocationResolver resolver, Iterable backups) throws IOException { + ISourceLocation prev = loc; + boolean removedOffset = false; + + if (resolver != null) { + loc = resolver.resolve(loc); + } - if (loc == null || prev.equals(loc)) { - for (ILogicalSourceLocationResolver backup : map.values()) { - removedOffset = false; - loc = backup.resolve(prev); + if (loc == null && prev.hasOffsetLength()) { + loc = resolver.resolve(URIUtil.removeOffset(prev)); + removedOffset = true; + } - if (loc == null && prev.hasOffsetLength()) { - loc = backup.resolve(URIUtil.removeOffset(prev)); - removedOffset = true; - } + if (loc == null || prev.equals(loc)) { + for (ILogicalSourceLocationResolver backup : backups) { + removedOffset = false; + loc = backup.resolve(prev); - if (loc != null && !prev.equals(loc)) { - break; // continue to offset/length handling below with found location - } + if (loc == null && prev.hasOffsetLength()) { + loc = backup.resolve(URIUtil.removeOffset(prev)); + removedOffset = true; } - } - if (loc == null || prev.equals(loc)) { - return null; + if (loc != null && !prev.equals(loc)) { + break; // continue to offset/length handling below with found location + } } + } - if (removedOffset || !loc.hasOffsetLength()) { // then copy the offset from the logical one - if (prev.hasLineColumn()) { - loc = vf.sourceLocation(loc, prev.getOffset(), prev.getLength(), prev.getBeginLine(), - prev.getEndLine(), prev.getBeginColumn(), prev.getEndColumn()); - } - else if (prev.hasOffsetLength()) { - if (loc.hasOffsetLength()) { - loc = vf.sourceLocation(loc, prev.getOffset() + loc.getOffset(), prev.getLength()); - } - else { - loc = vf.sourceLocation(loc, prev.getOffset(), prev.getLength()); - } - } + if (loc == null || prev.equals(loc)) { + return null; + } + + if (removedOffset || !loc.hasOffsetLength()) { // then copy the offset from the logical one + if (prev.hasLineColumn()) { + return vf.sourceLocation(loc, prev.getOffset(), prev.getLength(), prev.getBeginLine(), + prev.getEndLine(), prev.getBeginColumn(), prev.getEndColumn()); } - else if (loc.hasLineColumn()) { // the logical location offsets relative to the physical offset, possibly - // including line numbers - if (prev.hasLineColumn()) { - loc = vf.sourceLocation(loc, loc.getOffset() + prev.getOffset(), loc.getLength(), - loc.getBeginLine() + prev.getBeginLine() - 1, loc.getEndLine() + prev.getEndLine() - 1, - loc.getBeginColumn(), loc.getEndColumn()); + else if (prev.hasOffsetLength()) { + if (loc.hasOffsetLength()) { + return vf.sourceLocation(loc, prev.getOffset() + loc.getOffset(), prev.getLength()); } - else if (prev.hasOffsetLength()) { - loc = vf.sourceLocation(loc, loc.getOffset() + prev.getOffset(), loc.getLength()); + else { + return vf.sourceLocation(loc, prev.getOffset(), prev.getLength()); } } - else if (loc.hasOffsetLength()) { // the logical location offsets relative to the physical one - if (prev.hasOffsetLength()) { - loc = vf.sourceLocation(loc, loc.getOffset() + prev.getOffset(), loc.getLength()); - } + } + else if (loc.hasLineColumn()) { // the logical location offsets relative to the physical offset, possibly + // including line numbers + if (prev.hasLineColumn()) { + return vf.sourceLocation(loc, loc.getOffset() + prev.getOffset(), loc.getLength(), + loc.getBeginLine() + prev.getBeginLine() - 1, loc.getEndLine() + prev.getEndLine() - 1, + loc.getBeginColumn(), loc.getEndColumn()); + } + else if (prev.hasOffsetLength()) { + return vf.sourceLocation(loc, loc.getOffset() + prev.getOffset(), loc.getLength()); } } - + else if (loc.hasOffsetLength()) { // the logical location offsets relative to the physical one + if (prev.hasOffsetLength()) { + return vf.sourceLocation(loc, loc.getOffset() + prev.getOffset(), loc.getLength()); + } + } + // otherwise we return the loc without any offsets return loc; } + private ISourceLocation physicalLocation(ISourceLocation loc) throws IOException { + while (logicalResolvers.containsKey(loc.getScheme())) { + Map map = logicalResolvers.get(loc.getScheme()); + String auth = loc.hasAuthority() ? loc.getAuthority() : ""; + ILogicalSourceLocationResolver resolver = map.get(auth); + loc = resolveAndFixOffsets(loc, resolver, map.values()); + } + ISourceLocation finalLoc = loc; + return mapIfPresent(fallbackLogicalResolver, + r -> resolveAndFixOffsets(finalLoc, r, Collections.emptyList()), + finalLoc); + } + private ISourceLocation safeResolve(ISourceLocation loc) { ISourceLocation resolved = null; @@ -442,6 +465,7 @@ private ISourceLocationInput getInputResolver(String scheme) { String subScheme = m.group(1); return inputResolvers.get(subScheme); } + return fallbackInputResolver.get(); } return result; } @@ -454,6 +478,7 @@ private IClassloaderLocationResolver getClassloaderResolver(String scheme) { String subScheme = m.group(1); return classloaderResolvers.get(subScheme); } + return fallbackClassloaderResolver.get(); } return result; } @@ -466,18 +491,11 @@ private ISourceLocationOutput getOutputResolver(String scheme) { String subScheme = m.group(1); return outputResolvers.get(subScheme); } + return fallbackOutputResolver.get(); } return result; } - public boolean supportsInputScheme(String scheme) { - return getInputResolver(scheme) != null; - } - - public boolean supportsOutputScheme(String scheme) { - return getOutputResolver(scheme) != null; - } - public boolean supportsHost(ISourceLocation uri) { uri = safeResolve(uri); ISourceLocationInput resolver = getInputResolver(uri.getScheme()); @@ -914,7 +932,7 @@ public void watch(ISourceLocation loc, boolean recursive, Consumer Date: Wed, 11 May 2022 11:57:45 +0200 Subject: [PATCH 3/5] Simplified logic of fallback resolver --- .../rascalmpl/uri/URIResolverRegistry.java | 22 +++++-------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/src/org/rascalmpl/uri/URIResolverRegistry.java b/src/org/rascalmpl/uri/URIResolverRegistry.java index ab729ef47c1..d58f34e9eff 100644 --- a/src/org/rascalmpl/uri/URIResolverRegistry.java +++ b/src/org/rascalmpl/uri/URIResolverRegistry.java @@ -320,19 +320,6 @@ public ISourceLocation logicalToPhysical(ISourceLocation loc) throws IOException return result; } - @FunctionalInterface - private static interface ThrowingFunction { - R apply(A arg) throws E; - } - - private static T mapIfPresent(AtomicReference reference, ThrowingFunction mapper, T ifNotPresent) throws E { - var ref = reference.get(); - if (ref != null) { - return mapper.apply(ref); - } - return ifNotPresent; - } - private static ISourceLocation resolveAndFixOffsets(ISourceLocation loc, ILogicalSourceLocationResolver resolver, Iterable backups) throws IOException { ISourceLocation prev = loc; boolean removedOffset = false; @@ -407,10 +394,11 @@ private ISourceLocation physicalLocation(ISourceLocation loc) throws IOException ILogicalSourceLocationResolver resolver = map.get(auth); loc = resolveAndFixOffsets(loc, resolver, map.values()); } - ISourceLocation finalLoc = loc; - return mapIfPresent(fallbackLogicalResolver, - r -> resolveAndFixOffsets(finalLoc, r, Collections.emptyList()), - finalLoc); + var fallBack = fallbackLogicalResolver.get(); + if (fallBack != null) { + return resolveAndFixOffsets(loc, fallBack, Collections.emptyList()); + } + return loc; } private ISourceLocation safeResolve(ISourceLocation loc) { From d863b36cbde7175a03391c64f426a92c8664e37f Mon Sep 17 00:00:00 2001 From: Davy Landman Date: Thu, 12 May 2022 17:22:08 +0200 Subject: [PATCH 4/5] Using simpler volatile fields --- .../rascalmpl/uri/URIResolverRegistry.java | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/org/rascalmpl/uri/URIResolverRegistry.java b/src/org/rascalmpl/uri/URIResolverRegistry.java index d58f34e9eff..32671e95ab2 100644 --- a/src/org/rascalmpl/uri/URIResolverRegistry.java +++ b/src/org/rascalmpl/uri/URIResolverRegistry.java @@ -61,11 +61,11 @@ public class URIResolverRegistry { // we allow the user to define (using -Drascal.fallbackResolver=fully.qualified.classname) a single class that will handle // scheme's not statically registered. That class should implement at least one of these interfaces - private final AtomicReference<@Nullable ISourceLocationInput> fallbackInputResolver = new AtomicReference<>(); - private final AtomicReference<@Nullable ISourceLocationOutput> fallbackOutputResolver = new AtomicReference<>(); - private final AtomicReference<@Nullable ILogicalSourceLocationResolver> fallbackLogicalResolver = new AtomicReference<>(); - private final AtomicReference<@Nullable IClassloaderLocationResolver> fallbackClassloaderResolver = new AtomicReference<>(); - private final AtomicReference<@Nullable ISourceLocationWatcher> fallbackWatcher = new AtomicReference<>(); + private volatile @Nullable ISourceLocationInput fallbackInputResolver; + private volatile @Nullable ISourceLocationOutput fallbackOutputResolver; + private volatile @Nullable ILogicalSourceLocationResolver fallbackLogicalResolver; + private volatile @Nullable IClassloaderLocationResolver fallbackClassloaderResolver; + private volatile @Nullable ISourceLocationWatcher fallbackWatcher; private static class InstanceHolder { static URIResolverRegistry sInstance = new URIResolverRegistry(); @@ -150,27 +150,27 @@ private void loadFallback(String fallbackClass) { Object instance = constructService(fallbackClass); boolean ok = false; if (instance instanceof ILogicalSourceLocationResolver) { - fallbackLogicalResolver.set((ILogicalSourceLocationResolver) instance); + fallbackLogicalResolver = (ILogicalSourceLocationResolver) instance; ok = true; } if (instance instanceof ISourceLocationInput) { - fallbackInputResolver.set((ISourceLocationInput) instance); + fallbackInputResolver = (ISourceLocationInput) instance; ok = true; } if (instance instanceof ISourceLocationOutput) { - fallbackOutputResolver.set((ISourceLocationOutput) instance); + fallbackOutputResolver = (ISourceLocationOutput) instance; ok = true; } if (instance instanceof IClassloaderLocationResolver) { - fallbackClassloaderResolver.set((IClassloaderLocationResolver) instance); + fallbackClassloaderResolver = (IClassloaderLocationResolver) instance; ok = true; } if (instance instanceof ISourceLocationWatcher) { - fallbackWatcher.set((ISourceLocationWatcher) instance); + fallbackWatcher = (ISourceLocationWatcher) instance; } if (!ok) { System.err.println("WARNING: could not load fallback resolver " + fallbackClass @@ -394,7 +394,7 @@ private ISourceLocation physicalLocation(ISourceLocation loc) throws IOException ILogicalSourceLocationResolver resolver = map.get(auth); loc = resolveAndFixOffsets(loc, resolver, map.values()); } - var fallBack = fallbackLogicalResolver.get(); + var fallBack = fallbackLogicalResolver; if (fallBack != null) { return resolveAndFixOffsets(loc, fallBack, Collections.emptyList()); } @@ -453,7 +453,7 @@ private ISourceLocationInput getInputResolver(String scheme) { String subScheme = m.group(1); return inputResolvers.get(subScheme); } - return fallbackInputResolver.get(); + return fallbackInputResolver; } return result; } @@ -466,7 +466,7 @@ private IClassloaderLocationResolver getClassloaderResolver(String scheme) { String subScheme = m.group(1); return classloaderResolvers.get(subScheme); } - return fallbackClassloaderResolver.get(); + return fallbackClassloaderResolver; } return result; } @@ -479,7 +479,7 @@ private ISourceLocationOutput getOutputResolver(String scheme) { String subScheme = m.group(1); return outputResolvers.get(subScheme); } - return fallbackOutputResolver.get(); + return fallbackOutputResolver; } return result; } @@ -920,7 +920,7 @@ public void watch(ISourceLocation loc, boolean recursive, Consumer Date: Fri, 13 May 2022 15:33:38 +0200 Subject: [PATCH 5/5] Fixed error caused by subtle change in control flow due to factoring out method --- src/org/rascalmpl/uri/URIResolverRegistry.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/org/rascalmpl/uri/URIResolverRegistry.java b/src/org/rascalmpl/uri/URIResolverRegistry.java index 32671e95ab2..abb386bf935 100644 --- a/src/org/rascalmpl/uri/URIResolverRegistry.java +++ b/src/org/rascalmpl/uri/URIResolverRegistry.java @@ -388,7 +388,8 @@ else if (loc.hasOffsetLength()) { // the logical location offsets relative to th } private ISourceLocation physicalLocation(ISourceLocation loc) throws IOException { - while (logicalResolvers.containsKey(loc.getScheme())) { + ISourceLocation original = loc; + while (loc != null && logicalResolvers.containsKey(loc.getScheme())) { Map map = logicalResolvers.get(loc.getScheme()); String auth = loc.hasAuthority() ? loc.getAuthority() : ""; ILogicalSourceLocationResolver resolver = map.get(auth); @@ -396,7 +397,7 @@ private ISourceLocation physicalLocation(ISourceLocation loc) throws IOException } var fallBack = fallbackLogicalResolver; if (fallBack != null) { - return resolveAndFixOffsets(loc, fallBack, Collections.emptyList()); + return resolveAndFixOffsets(loc == null ? original : loc, fallBack, Collections.emptyList()); } return loc; }