Skip to content

Commit

Permalink
[GR-23746] AssertionError: Unexpected IOException at Source$LiteralBu…
Browse files Browse the repository at this point in the history
…ilder.build.

PullRequest: graal/6319
  • Loading branch information
tzezula committed Jun 4, 2020
2 parents b55ca28 + dcd37b5 commit 524fc04
Show file tree
Hide file tree
Showing 12 changed files with 273 additions and 73 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ protected void onCreate(Env tmp) {
if (file == null || !file.exists()) {
throw InsightException.notFound(file);
}
String mimeType = file.getMimeType();
String mimeType = file.detectMimeType();
String lang = null;
for (Map.Entry<String, LanguageInfo> e : env.getLanguages().entrySet()) {
if (mimeType != null && e.getValue().getMimeTypes().contains(mimeType)) {
Expand Down
1 change: 1 addition & 0 deletions truffle/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ This changelog summarizes major changes between Truffle versions relevant to lan
* Improved `AssumedValue` utility class: Code that reads the value but can not constant fold it does not need to deopt when the value changes.
* A `TruffleFile` for an empty path is no more resolved to the current working directory.
* Added [`SourceBuilder.canonicalizePath(boolean)`](https://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/source/Source.SourceBuilder.html) to control whether the `Source#getPath()` should be canonicalized.
* Deprecated and renamed `TruffleFile.getMimeType` to [TruffleFile.detectMimeType](https://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/TruffleFile.html#detectMimeType--). The new method no longer throws `IOException` but returns `null` instead.

## Version 20.1.0
* Added `@GenerateLibrary(dynamicDispatchEnabled = false)` that allows to disable dynamic dispatch semantics for a library. The default is `true`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.TruffleLanguage.Env;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.impl.Accessor;
import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.test.OSUtils;
import java.nio.file.FileSystemException;
Expand Down Expand Up @@ -2627,12 +2626,4 @@ public String toString() {
}
}
}

private static final TestAPIAccessor API = new TestAPIAccessor();

private static final class TestAPIAccessor extends Accessor {
static EngineSupport engineAccess() {
return API.engineSupport();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
*
* Subject to the condition set forth below, permission is hereby granted to any
* person obtaining a copy of this software, associated documentation and/or
* data (collectively the "Software"), free of charge and under any and all
* copyright rights in the Software, and any and all patent rights owned or
* freely licensable by each licensor hereunder covering either (i) the
* unmodified Software as contributed to or provided by such licensor, or (ii)
* the Larger Works (as defined below), to deal in both
*
* (a) the Software, and
*
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
* one is included with the Software each a "Larger Work" to which the Software
* is contributed by such licensors),
*
* without restriction, including without limitation the rights to copy, create
* derivative works of, display, perform, and distribute the Software and make,
* use, sell, offer for sale, import, export, have made, and have sold the
* Software and the Larger Work(s), and to sublicense the foregoing rights on
* either these or other terms.
*
* This license is subject to the following condition:
*
* The above copyright notice and either this complete permission notice or at a
* minimum a reference to the UPL must be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.oracle.truffle.api.test.polyglot;

import com.oracle.truffle.api.impl.Accessor;

final class TestAPIAccessor extends Accessor {

private static final TestAPIAccessor API = new TestAPIAccessor();

static Accessor.EngineSupport engineAccess() {
return API.engineSupport();
}

static Accessor.LanguageSupport languageAccess() {
return API.languageSupport();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,17 @@
*/
package com.oracle.truffle.api.test.polyglot;

import static java.nio.charset.StandardCharsets.UTF_16;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import com.oracle.truffle.api.TruffleFile;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.test.OSUtils;
import com.oracle.truffle.api.test.polyglot.TruffleFileTest.DuplicateMimeTypeLanguage1.Language1Detector;
import com.oracle.truffle.api.test.polyglot.TruffleFileTest.DuplicateMimeTypeLanguage2.Language2Detector;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Array;
Expand All @@ -60,7 +64,6 @@
import java.net.URI;
import java.nio.channels.SeekableByteChannel;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.AccessMode;
import java.nio.file.CopyOption;
import java.nio.file.DirectoryStream;
Expand All @@ -76,12 +79,14 @@
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.FileTime;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.io.FileSystem;
Expand All @@ -93,6 +98,12 @@

public class TruffleFileTest extends AbstractPolyglotTest {

private static final Predicate<TruffleFile> FAILING_RECOGNIZER = (tf) -> {
throw silenceException(RuntimeException.class, new IOException());
};

private static final Predicate<TruffleFile> ALL_FILES_RECOGNIZER = (tf) -> true;

@Before
public void setUp() throws Exception {
setupEnv();
Expand All @@ -102,6 +113,12 @@ public void setUp() throws Exception {
@After
public void tearDown() throws Exception {
resetLanguageHomes();
for (Class<? extends BaseDetector> clz : Arrays.asList(Language1Detector.class, Language2Detector.class)) {
BaseDetector instance = BaseDetector.getInstance(clz);
if (instance != null) {
instance.reset();
}
}
}

@Test
Expand Down Expand Up @@ -160,30 +177,93 @@ public void testGetName() {
}

@Test
public void testGetMimeType() throws IOException {
public void testDetectMimeType() {
TruffleFile file = languageEnv.getPublicTruffleFile("/folder/filename.duplicate");
String result = file.getMimeType();
String result = file.detectMimeType();
assertNull(result);
assertEquals(1, BaseDetector.getInstance(DuplicateMimeTypeLanguage1.Detector.class).resetFindMimeTypeCalled());
assertEquals(1, BaseDetector.getInstance(DuplicateMimeTypeLanguage2.Detector.class).resetFindMimeTypeCalled());
BaseDetector.getInstance(DuplicateMimeTypeLanguage1.Detector.class).setMimeType(null);
BaseDetector.getInstance(DuplicateMimeTypeLanguage2.Detector.class).setMimeType("text/x-duplicate-mime");
result = file.getMimeType();
Language1Detector detector1 = Language1Detector.getInstance();
Language2Detector detector2 = Language2Detector.getInstance();
assertEquals(1, detector1.resetFindMimeTypeCalled());
assertEquals(1, detector2.resetFindMimeTypeCalled());
detector1.mimeType(null);
detector2.mimeType("text/x-duplicate-mime");
result = file.detectMimeType();
assertEquals("text/x-duplicate-mime", result);
BaseDetector.getInstance(DuplicateMimeTypeLanguage1.Detector.class).setMimeType("text/x-duplicate-mime");
BaseDetector.getInstance(DuplicateMimeTypeLanguage2.Detector.class).setMimeType(null);
result = file.getMimeType();
detector1.mimeType("text/x-duplicate-mime");
detector2.mimeType(null);
result = file.detectMimeType();
assertEquals("text/x-duplicate-mime", result);
BaseDetector.getInstance(DuplicateMimeTypeLanguage1.Detector.class).setMimeType("text/x-duplicate-mime");
BaseDetector.getInstance(DuplicateMimeTypeLanguage2.Detector.class).setMimeType("text/x-duplicate-mime");
result = file.getMimeType();
detector1.mimeType("text/x-duplicate-mime");
detector2.mimeType("text/x-duplicate-mime");
result = file.detectMimeType();
assertEquals("text/x-duplicate-mime", result);
BaseDetector.getInstance(DuplicateMimeTypeLanguage1.Detector.class).setMimeType("text/x-duplicate-mime-1");
BaseDetector.getInstance(DuplicateMimeTypeLanguage2.Detector.class).setMimeType("text/x-duplicate-mime-2");
result = file.getMimeType();
detector1.mimeType("text/x-duplicate-mime-1");
detector2.mimeType("text/x-duplicate-mime-2");
result = file.detectMimeType();
// Order is not deterministic can be either 'text/x-duplicate-mime-1' or
// 'text/x-duplicate-mime-2'
assertTrue("text/x-duplicate-mime-1".equals(result) || "text/x-duplicate-mime-2".equals(result));
detector1.reset().mimeType("text/x-duplicate-mime-1").recognizer(FAILING_RECOGNIZER);
detector2.reset().mimeType("text/x-duplicate-mime-2");
result = file.detectMimeType();
assertEquals("text/x-duplicate-mime-2", result);
detector1.reset().mimeType("text/x-duplicate-mime-1");
detector2.reset().mimeType("text/x-duplicate-mime-2").recognizer(FAILING_RECOGNIZER);
result = file.detectMimeType();
assertEquals("text/x-duplicate-mime-1", result);
detector1.reset().mimeType("text/x-duplicate-mime-1").recognizer(FAILING_RECOGNIZER);
detector2.reset().mimeType("text/x-duplicate-mime-2").recognizer(FAILING_RECOGNIZER);
result = file.detectMimeType();
assertNull(result);
detector1.reset().mimeType("text/x-duplicate-mime-1").recognizer(ALL_FILES_RECOGNIZER);
detector2.reset().mimeType("text/x-duplicate-mime-2").recognizer(ALL_FILES_RECOGNIZER);
result = languageEnv.getInternalTruffleFile("").detectMimeType();
assertNull(result);
assertEquals(0, detector1.resetFindMimeTypeCalled());
assertEquals(0, detector2.resetFindMimeTypeCalled());
}

@Test
public void testDetectEncoding() {
TruffleFile file = languageEnv.getPublicTruffleFile("/folder/filename.duplicate");
Charset encoding = TestAPIAccessor.languageAccess().detectEncoding(file, null);
assertNull(encoding);
Language1Detector detector1 = Language1Detector.getInstance();
Language2Detector detector2 = Language2Detector.getInstance();
String mimeType = "text/x-duplicate-mime";
detector1.reset();
detector2.reset().mimeType(mimeType);
encoding = TestAPIAccessor.languageAccess().detectEncoding(file, null);
assertNull(encoding);
detector1.reset().mimeType(mimeType);
detector2.reset().mimeType(mimeType).encoding(UTF_16);
encoding = TestAPIAccessor.languageAccess().detectEncoding(file, mimeType);
assertEquals(UTF_16, encoding);
detector1.reset().mimeType(mimeType).encoding(UTF_8);
detector2.reset().mimeType(mimeType);
encoding = TestAPIAccessor.languageAccess().detectEncoding(file, mimeType);
assertEquals(UTF_8, encoding);
detector1.reset().mimeType(mimeType).encoding(UTF_8);
detector2.reset().mimeType(mimeType).encoding(UTF_16);
encoding = TestAPIAccessor.languageAccess().detectEncoding(file, mimeType);
// Order is not deterministic can be either 'UTF-8' or 'UTF-16'
assertTrue(UTF_8.equals(encoding) || UTF_16.equals(encoding));
detector1.reset().mimeType(mimeType).encoding(UTF_8).recognizer(FAILING_RECOGNIZER);
detector2.reset().mimeType(mimeType).encoding(UTF_16);
encoding = TestAPIAccessor.languageAccess().detectEncoding(file, mimeType);
assertEquals(UTF_16, encoding);
detector1.reset().mimeType(mimeType).encoding(UTF_8);
detector2.reset().mimeType(mimeType).encoding(UTF_16).recognizer(FAILING_RECOGNIZER);
encoding = TestAPIAccessor.languageAccess().detectEncoding(file, mimeType);
assertEquals(UTF_8, encoding);
detector1.reset().mimeType(mimeType).encoding(UTF_8).recognizer(FAILING_RECOGNIZER);
detector2.reset().mimeType(mimeType).encoding(UTF_16).recognizer(FAILING_RECOGNIZER);
encoding = TestAPIAccessor.languageAccess().detectEncoding(file, mimeType);
assertNull(encoding);
detector1.reset().mimeType(mimeType).encoding(UTF_8).recognizer(ALL_FILES_RECOGNIZER);
detector2.reset().mimeType(mimeType).encoding(UTF_8).recognizer(ALL_FILES_RECOGNIZER);
encoding = TestAPIAccessor.languageAccess().detectEncoding(languageEnv.getInternalTruffleFile(""), mimeType);
assertNull(encoding);
}

@Test
Expand Down Expand Up @@ -294,7 +374,7 @@ public void testEmptyPath() throws Exception {
defaultParameterValues.put(TruffleFile.class, otherFile);
defaultParameterValues.put(Object.class, otherFile);
defaultParameterValues.put(Set.class, Collections.emptySet());
defaultParameterValues.put(Charset.class, StandardCharsets.UTF_8);
defaultParameterValues.put(Charset.class, UTF_8);
defaultParameterValues.put(OpenOption[].class, new OpenOption[0]);
defaultParameterValues.put(LinkOption[].class, new LinkOption[0]);
defaultParameterValues.put(CopyOption[].class, new CopyOption[0]);
Expand Down Expand Up @@ -382,12 +462,19 @@ private static void resetLanguageHomes() throws ReflectiveOperationException {
reset.invoke(null);
}

@SuppressWarnings({"unchecked", "unused"})
private static <T extends Throwable> T silenceException(Class<T> type, Throwable t) throws T {
throw (T) t;
}

public static class BaseDetector implements TruffleFile.FileTypeDetector {

private static Map<Class<? extends BaseDetector>, BaseDetector> INSTANCES = new HashMap<>();

private int findMimeTypeCalled;
private String mimeType;
private Charset encoding;
private Predicate<? super TruffleFile> recognizer;

protected BaseDetector() {
INSTANCES.put(getClass(), this);
Expand All @@ -399,14 +486,32 @@ int resetFindMimeTypeCalled() {
return res;
}

void setMimeType(String value) {
BaseDetector mimeType(String value) {
mimeType = value;
return this;
}

BaseDetector encoding(Charset value) {
encoding = value;
return this;
}

BaseDetector recognizer(Predicate<? super TruffleFile> predicate) {
this.recognizer = predicate;
return this;
}

BaseDetector reset() {
findMimeTypeCalled = 0;
mimeType = null;
encoding = null;
recognizer = null;
return this;
}

@Override
public String findMimeType(TruffleFile file) throws IOException {
String name = file.getName();
if (name != null && name.endsWith(".duplicate")) {
if (getRecognizer().test(file)) {
findMimeTypeCalled++;
return mimeType;
} else {
Expand All @@ -416,26 +521,44 @@ public String findMimeType(TruffleFile file) throws IOException {

@Override
public Charset findEncoding(TruffleFile file) throws IOException {
return null;
if (getRecognizer().test(file)) {
return encoding;
} else {
return null;
}
}

@SuppressWarnings("unchecked")
static <T extends BaseDetector> T getInstance(Class<T> clazz) {
return (T) INSTANCES.get(clazz);
}

private Predicate<? super TruffleFile> getRecognizer() {
if (recognizer == null) {
recognizer = (file) -> file.getName() != null && file.getName().endsWith(".duplicate");
}
return recognizer;
}
}

@TruffleLanguage.Registration(id = "DuplicateMimeTypeLanguage1", name = "DuplicateMimeTypeLanguage1", characterMimeTypes = "text/x-duplicate-mime", fileTypeDetectors = DuplicateMimeTypeLanguage1.Detector.class)
@TruffleLanguage.Registration(id = "DuplicateMimeTypeLanguage1", name = "DuplicateMimeTypeLanguage1", characterMimeTypes = "text/x-duplicate-mime", fileTypeDetectors = DuplicateMimeTypeLanguage1.Language1Detector.class)
public static final class DuplicateMimeTypeLanguage1 extends ProxyLanguage {

public static final class Detector extends BaseDetector {
public static final class Language1Detector extends BaseDetector {

static Language1Detector getInstance() {
return BaseDetector.getInstance(Language1Detector.class);
}
}
}

@TruffleLanguage.Registration(id = "DuplicateMimeTypeLanguage2", name = "DuplicateMimeTypeLanguage2", characterMimeTypes = "text/x-duplicate-mime", fileTypeDetectors = DuplicateMimeTypeLanguage2.Detector.class)
@TruffleLanguage.Registration(id = "DuplicateMimeTypeLanguage2", name = "DuplicateMimeTypeLanguage2", characterMimeTypes = "text/x-duplicate-mime", fileTypeDetectors = DuplicateMimeTypeLanguage2.Language2Detector.class)
public static final class DuplicateMimeTypeLanguage2 extends ProxyLanguage {

public static final class Detector extends BaseDetector {
public static final class Language2Detector extends BaseDetector {
static Language2Detector getInstance() {
return BaseDetector.getInstance(Language2Detector.class);
}
}

}
Expand Down
2 changes: 2 additions & 0 deletions truffle/src/com.oracle.truffle.api/snapshot.sigtest
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,9 @@ meth public com.oracle.truffle.api.TruffleFile resolveSibling(java.lang.String)
meth public int hashCode()
meth public java.io.BufferedReader newBufferedReader() throws java.io.IOException
meth public java.io.BufferedReader newBufferedReader(java.nio.charset.Charset) throws java.io.IOException
meth public java.lang.String detectMimeType()
meth public java.lang.String getMimeType() throws java.io.IOException
anno 0 java.lang.Deprecated()
meth public java.lang.String getName()
meth public java.lang.String getPath()
meth public java.lang.String toString()
Expand Down
Loading

0 comments on commit 524fc04

Please sign in to comment.