From c527031ef8d9112b12d768f308e768af18fa12f0 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Wed, 23 Mar 2022 14:08:47 -0700 Subject: Initial support for text blocks PiperOrigin-RevId: 436825379 --- java/com/google/turbine/parse/StreamLexer.java | 162 +++++++++++++++++++++ .../google/turbine/lower/LowerIntegrationTest.java | 7 +- .../google/turbine/lower/testdata/textblock.test | 30 ++++ javatests/com/google/turbine/parse/LexerTest.java | 27 ++++ .../com/google/turbine/parse/ParseErrorTest.java | 13 ++ 5 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 javatests/com/google/turbine/lower/testdata/textblock.test diff --git a/java/com/google/turbine/parse/StreamLexer.java b/java/com/google/turbine/parse/StreamLexer.java index 2348385..3d46b90 100644 --- a/java/com/google/turbine/parse/StreamLexer.java +++ b/java/com/google/turbine/parse/StreamLexer.java @@ -17,8 +17,11 @@ package com.google.turbine.parse; import static com.google.common.base.Verify.verify; +import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.turbine.parse.UnicodeEscapePreprocessor.ASCII_SUB; +import static java.lang.Math.min; +import com.google.common.collect.ImmutableList; import com.google.turbine.diag.SourceFile; import com.google.turbine.diag.TurbineError; import com.google.turbine.diag.TurbineError.ErrorKind; @@ -399,6 +402,15 @@ public class StreamLexer implements Lexer { case '"': { eat(); + if (ch == '"') { + eat(); + if (ch != '"') { + saveValue(""); + return Token.STRING_LITERAL; + } + eat(); + return textBlock(); + } readFrom(); StringBuilder sb = new StringBuilder(); STRING: @@ -436,6 +448,156 @@ public class StreamLexer implements Lexer { } } + private Token textBlock() { + OUTER: + while (true) { + switch (ch) { + case ' ': + case '\r': + case '\t': + eat(); + break; + default: + break OUTER; + } + } + switch (ch) { + case '\r': + eat(); + if (ch == '\n') { + eat(); + } + break; + case '\n': + eat(); + break; + default: + throw inputError(); + } + readFrom(); + StringBuilder sb = new StringBuilder(); + while (true) { + switch (ch) { + case '"': + eat(); + if (ch != '"') { + sb.append("\""); + continue; + } + eat(); + if (ch != '"') { + sb.append("\"\""); + continue; + } + eat(); + String value = sb.toString(); + value = stripIndent(value); + value = translateEscapes(value); + saveValue(value); + return Token.STRING_LITERAL; + case ASCII_SUB: + if (reader.done()) { + return Token.EOF; + } + // falls through + default: + sb.appendCodePoint(ch); + eat(); + continue; + } + } + } + + static String stripIndent(String value) { + if (value.isEmpty()) { + return value; + } + ImmutableList lines = value.lines().collect(toImmutableList()); + // the amount of whitespace to strip from the beginning of every line + int strip = Integer.MAX_VALUE; + char last = value.charAt(value.length() - 1); + boolean trailingNewline = last == '\n' || last == '\r'; + if (trailingNewline) { + // If the input contains a trailing newline, we have something like: + // + // |String s = """ + // | foo + // |"""; + // + // Because the final """ is unindented, nothing should be stripped. + strip = 0; + } else { + // find the longest common prefix of whitespace across all non-blank lines + for (int i = 0; i < lines.size(); i++) { + String line = lines.get(i); + int nonWhitespaceStart = nonWhitespaceStart(line); + if (nonWhitespaceStart == line.length()) { + continue; + } + strip = min(strip, nonWhitespaceStart); + } + } + StringBuilder result = new StringBuilder(); + boolean first = true; + for (String line : lines) { + if (!first) { + result.append('\n'); + } + int end = trailingWhitespaceStart(line); + if (strip <= end) { + result.append(line, strip, end); + } + first = false; + } + if (trailingNewline) { + result.append('\n'); + } + return result.toString(); + } + + private static int nonWhitespaceStart(String value) { + int i = 0; + while (i < value.length() && Character.isWhitespace(value.charAt(i))) { + i++; + } + return i; + } + + private static int trailingWhitespaceStart(String value) { + int i = value.length() - 1; + while (i >= 0 && Character.isWhitespace(value.charAt(i))) { + i--; + } + return i + 1; + } + + private static String translateEscapes(String value) { + StreamLexer lexer = + new StreamLexer(new UnicodeEscapePreprocessor(new SourceFile(null, value + ASCII_SUB))); + return lexer.translateEscapes(); + } + + private String translateEscapes() { + readFrom(); + StringBuilder sb = new StringBuilder(); + OUTER: + while (true) { + switch (ch) { + case '\\': + eat(); + sb.append(escape()); + continue; + case ASCII_SUB: + break OUTER; + default: + sb.appendCodePoint(ch); + eat(); + continue; + } + } + return sb.toString(); + } + private char escape() { boolean zeroToThree = false; switch (ch) { diff --git a/javatests/com/google/turbine/lower/LowerIntegrationTest.java b/javatests/com/google/turbine/lower/LowerIntegrationTest.java index 97170ca..7ae9b1b 100644 --- a/javatests/com/google/turbine/lower/LowerIntegrationTest.java +++ b/javatests/com/google/turbine/lower/LowerIntegrationTest.java @@ -44,7 +44,11 @@ import org.junit.runners.Parameterized.Parameters; public class LowerIntegrationTest { private static final ImmutableMap SOURCE_VERSION = - ImmutableMap.of("record.test", 16, "record2.test", 16, "sealed.test", 17); + ImmutableMap.of( + "record.test", 16, // + "record2.test", 16, + "sealed.test", 17, + "textblock.test", 15); @Parameters(name = "{index}: {0}") public static Iterable parameters() { @@ -285,6 +289,7 @@ public class LowerIntegrationTest { "superabstract.test", "supplierfunction.test", "tbound.test", + "textblock.test", "tyanno_inner.test", "tyanno_varargs.test", "typaram.test", diff --git a/javatests/com/google/turbine/lower/testdata/textblock.test b/javatests/com/google/turbine/lower/testdata/textblock.test new file mode 100644 index 0000000..9683296 --- /dev/null +++ b/javatests/com/google/turbine/lower/testdata/textblock.test @@ -0,0 +1,30 @@ +=== TextBlock.java === +class TextBlock { + public static final String hello = """ + hello + world + """; + public static final String escape = """ + hello\nworld\" + \r\t\b + \0123 + \' + \\ + \" + """; + public static final String quotes = """ + " "" ""\" """; + public static final String newline = """ + hello + world"""; + public static final String blank = """ + hello + + + world + """; + public static final String allBlank = """ + + + """; +} diff --git a/javatests/com/google/turbine/parse/LexerTest.java b/javatests/com/google/turbine/parse/LexerTest.java index c3d7804..bf0b374 100644 --- a/javatests/com/google/turbine/parse/LexerTest.java +++ b/javatests/com/google/turbine/parse/LexerTest.java @@ -17,11 +17,15 @@ package com.google.turbine.parse; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assume.assumeTrue; import com.google.common.escape.SourceCodeEscapers; +import com.google.common.truth.Expect; import com.google.turbine.diag.SourceFile; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -29,6 +33,8 @@ import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class LexerTest { + @Rule public final Expect expect = Expect.create(); + @Test public void testSimple() { assertThat(lex("\nasd dsa\n")).containsExactly("IDENT(asd)", "IDENT(dsa)", "EOF"); @@ -367,4 +373,25 @@ public class LexerTest { } while (token != Token.EOF); return tokens; } + + @Test + public void stripIndent() throws Exception { + assumeTrue(Runtime.version().feature() >= 13); + String[] inputs = { + "", + "hello", + "hello\n", + "\nhello", + "\n hello\n world", + "\n hello\n world\n ", + "\n hello\n world\n", + "\n hello\n world\n ", + "\n hello\nworld", + "\n hello\n \nworld\n ", + }; + Method stripIndent = String.class.getMethod("stripIndent"); + for (String input : inputs) { + expect.that(StreamLexer.stripIndent(input)).isEqualTo(stripIndent.invoke(input)); + } + } } diff --git a/javatests/com/google/turbine/parse/ParseErrorTest.java b/javatests/com/google/turbine/parse/ParseErrorTest.java index 2c48b81..0187ce0 100644 --- a/javatests/com/google/turbine/parse/ParseErrorTest.java +++ b/javatests/com/google/turbine/parse/ParseErrorTest.java @@ -307,6 +307,19 @@ public class ParseErrorTest { " ^")); } + @Test + public void singleLineTextBlockRejected() { + String input = "class T { String s = \"\"\" \"\"\"; }"; + TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input)); + assertThat(e) + .hasMessageThat() + .isEqualTo( + lines( + "<>:1: error: unexpected input: \"", + "class T { String s = \"\"\" \"\"\"; }", + " ^")); + } + private static String lines(String... lines) { return Joiner.on(System.lineSeparator()).join(lines); } -- cgit v1.2.3 From 9bf393cbcfc281bbac6e3d3f042b4444156567ac Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Thu, 7 Apr 2022 13:31:48 -0700 Subject: Support zip64 extensible data sectors PiperOrigin-RevId: 440187167 --- java/com/google/turbine/zip/Zip.java | 33 +++++- javatests/com/google/turbine/zip/ZipTest.java | 163 ++++++++++++++++++++++++++ 2 files changed, 192 insertions(+), 4 deletions(-) diff --git a/java/com/google/turbine/zip/Zip.java b/java/com/google/turbine/zip/Zip.java index fa0f0e0..366cd6b 100644 --- a/java/com/google/turbine/zip/Zip.java +++ b/java/com/google/turbine/zip/Zip.java @@ -74,6 +74,7 @@ import java.util.zip.ZipException; public final class Zip { static final int ZIP64_ENDSIG = 0x06064b50; + static final int ZIP64_LOCSIG = 0x07064b50; static final int LOCHDR = 30; // LOC header size static final int CENHDR = 46; // CEN header size @@ -196,20 +197,44 @@ public final class Zip { if (totalEntries == ZIP64_MAGICCOUNT) { // Assume the zip64 EOCD has the usual size; we don't support zip64 extensible data sectors. long zip64eocdOffset = size - ENDHDR - ZIP64_LOCHDR - ZIP64_ENDHDR; - MappedByteBuffer zip64eocd = chan.map(MapMode.READ_ONLY, zip64eocdOffset, ZIP64_ENDHDR); - zip64eocd.order(ByteOrder.LITTLE_ENDIAN); // Note that zip reading is necessarily best-effort, since an archive could contain 0xFFFF // entries and the last entry's data could contain a ZIP64_ENDSIG. Some implementations // read the full EOCD records and compare them. - if (zip64eocd.getInt(0) == ZIP64_ENDSIG) { - cdsize = zip64eocd.getLong(ZIP64_ENDSIZ); + long zip64cdsize = zip64cdsize(chan, zip64eocdOffset); + if (zip64cdsize != -1) { eocdOffset = zip64eocdOffset; + cdsize = zip64cdsize; + } else { + // If we couldn't find a zip64 EOCD at a fixed offset, either it doesn't exist + // or there was a zip64 extensible data sector, so try going through the + // locator. This approach doesn't work if data was prepended to the archive + // without updating the offset in the locator. + MappedByteBuffer zip64loc = + chan.map(MapMode.READ_ONLY, size - ENDHDR - ZIP64_LOCHDR, ZIP64_LOCHDR); + zip64loc.order(ByteOrder.LITTLE_ENDIAN); + if (zip64loc.getInt(0) == ZIP64_LOCSIG) { + zip64eocdOffset = zip64loc.getLong(8); + zip64cdsize = zip64cdsize(chan, zip64eocdOffset); + if (zip64cdsize != -1) { + eocdOffset = zip64eocdOffset; + cdsize = zip64cdsize; + } + } } } this.cd = chan.map(MapMode.READ_ONLY, eocdOffset - cdsize, cdsize); cd.order(ByteOrder.LITTLE_ENDIAN); } + static long zip64cdsize(FileChannel chan, long eocdOffset) throws IOException { + MappedByteBuffer zip64eocd = chan.map(MapMode.READ_ONLY, eocdOffset, ZIP64_ENDHDR); + zip64eocd.order(ByteOrder.LITTLE_ENDIAN); + if (zip64eocd.getInt(0) == ZIP64_ENDSIG) { + return zip64eocd.getLong(ZIP64_ENDSIZ); + } + return -1; + } + @Override public Iterator iterator() { return new ZipIterator(path, chan, cd); diff --git a/javatests/com/google/turbine/zip/ZipTest.java b/javatests/com/google/turbine/zip/ZipTest.java index e9dfc44..2b6636d 100644 --- a/javatests/com/google/turbine/zip/ZipTest.java +++ b/javatests/com/google/turbine/zip/ZipTest.java @@ -25,6 +25,8 @@ import com.google.common.hash.Hashing; import com.google.common.io.ByteStreams; import java.io.IOException; import java.net.URI; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; @@ -38,6 +40,7 @@ import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.JarOutputStream; import java.util.zip.ZipException; +import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; import org.junit.Rule; import org.junit.Test; @@ -164,4 +167,164 @@ public class ZipTest { ZipException e = assertThrows(ZipException.class, () -> actual(path)); assertThat(e).hasMessageThat().isEqualTo("zip file comment length was 33, expected 17"); } + + // Create a zip64 archive with an extensible data sector + @Test + public void zip64extension() throws IOException { + + ByteBuffer buf = ByteBuffer.allocate(1000); + buf.order(ByteOrder.LITTLE_ENDIAN); + + // The jar has a single entry named 'hello', with the value 'world' + byte[] name = "hello".getBytes(UTF_8); + byte[] value = "world".getBytes(UTF_8); + int crc = Hashing.crc32().hashBytes(value).asInt(); + + int localHeaderPosition = buf.position(); + + // local file header signature 4 bytes (0x04034b50) + buf.putInt((int) ZipFile.LOCSIG); + // version needed to extract 2 bytes + buf.putShort((short) 0); + // general purpose bit flag 2 bytes + buf.putShort((short) 0); + // compression method 2 bytes + buf.putShort((short) 0); + // last mod file time 2 bytes + buf.putShort((short) 0); + // last mod file date 2 bytes + buf.putShort((short) 0); + // crc-32 4 bytes + buf.putInt(crc); + // compressed size 4 bytes + buf.putInt(value.length); + // uncompressed size 4 bytes + buf.putInt(value.length); + // file name length 2 bytes + buf.putShort((short) name.length); + // extra field length 2 bytes + buf.putShort((short) 0); + // file name (variable size) + buf.put(name); + // extra field (variable size) + // file data + buf.put(value); + + int centralDirectoryPosition = buf.position(); + + // central file header signature 4 bytes (0x02014b50) + buf.putInt((int) ZipFile.CENSIG); + // version made by 2 bytes + buf.putShort((short) 0); + // version needed to extract 2 bytes + buf.putShort((short) 0); + // general purpose bit flag 2 bytes + buf.putShort((short) 0); + // compression method 2 bytes + buf.putShort((short) 0); + // last mod file time 2 bytes + buf.putShort((short) 0); + // last mod file date 2 bytes + buf.putShort((short) 0); + // crc-32 4 bytes + buf.putInt(crc); + // compressed size 4 bytes + buf.putInt(value.length); + // uncompressed size 4 bytes + buf.putInt(value.length); + // file name length 2 bytes + buf.putShort((short) name.length); + // extra field length 2 bytes + buf.putShort((short) 0); + // file comment length 2 bytes + buf.putShort((short) 0); + // disk number start 2 bytes + buf.putShort((short) 0); + // internal file attributes 2 bytes + buf.putShort((short) 0); + // external file attributes 4 bytes + buf.putInt(0); + // relative offset of local header 4 bytes + buf.putInt(localHeaderPosition); + // file name (variable size) + buf.put(name); + + int centralDirectorySize = buf.position() - centralDirectoryPosition; + int zip64eocdPosition = buf.position(); + + // zip64 end of central dir + // signature 4 bytes (0x06064b50) + buf.putInt(Zip.ZIP64_ENDSIG); + // size of zip64 end of central + // directory record 8 bytes + buf.putLong(Zip.ZIP64_ENDSIZ + 5); + // version made by 2 bytes + buf.putShort((short) 0); + // version needed to extract 2 bytes + buf.putShort((short) 0); + // number of this disk 4 bytes + buf.putInt(0); + // number of the disk with the + // start of the central directory 4 bytes + buf.putInt(0); + // total number of entries in the + // central directory on this disk 8 bytes + buf.putLong(1); + // total number of entries in the + // central directory 8 bytes + buf.putLong(1); + // size of the central directory 8 bytes + buf.putLong(centralDirectorySize); + // offset of start of central + // directory with respect to + // offset of start of central + // the starting disk number 8 bytes + buf.putLong(centralDirectoryPosition); + // zip64 extensible data sector (variable size) + buf.put((byte) 3); + buf.putInt(42); + + // zip64 end of central dir locator + // signature 4 bytes (0x07064b50) + buf.putInt(Zip.ZIP64_LOCSIG); + // number of the disk with the + // start of the zip64 end of + // central directory 4 bytes + buf.putInt(0); + // relative offset of the zip64 + // end of central directory record 8 bytes + buf.putLong(zip64eocdPosition); + // total number of disks 4 bytes + buf.putInt(0); + + // end of central dir signature 4 bytes (0x06054b50) + buf.putInt((int) ZipFile.ENDSIG); + // number of this disk 2 bytes + buf.putShort((short) 0); + // number of the disk with the + // start of the central directory 2 bytes + buf.putShort((short) 0); + // total number of entries in the + // central directory on this disk 2 bytes + buf.putShort((short) 1); + // total number of entries in + // the central directory 2 bytes + buf.putShort((short) Zip.ZIP64_MAGICCOUNT); + // size of the central directory 4 bytes + buf.putInt(centralDirectorySize); + // offset of start of central + // directory with respect to + // the starting disk number 4 bytes + buf.putInt(centralDirectoryPosition); + // .ZIP file comment length 2 bytes + buf.putShort((short) 0); + // .ZIP file comment (variable size) + + byte[] bytes = new byte[buf.position()]; + buf.rewind(); + buf.get(bytes); + Path path = temporaryFolder.newFile("test.jar").toPath(); + Files.write(path, bytes); + assertThat(actual(path)).isEqualTo(expected(path)); + } } -- cgit v1.2.3 From 68172d4df0c054ed3499288079f109db18f8842c Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Thu, 7 Apr 2022 14:05:12 -0700 Subject: Follow-up to https://github.com/google/turbine/commit/9bf393cbcfc281bbac6e3d3f042b4444156567ac PiperOrigin-RevId: 440195352 --- java/com/google/turbine/zip/Zip.java | 1 - 1 file changed, 1 deletion(-) diff --git a/java/com/google/turbine/zip/Zip.java b/java/com/google/turbine/zip/Zip.java index 366cd6b..d732b35 100644 --- a/java/com/google/turbine/zip/Zip.java +++ b/java/com/google/turbine/zip/Zip.java @@ -65,7 +65,6 @@ import java.util.zip.ZipException; * supported. *
  • UTF-8 is the only supported encoding. *
  • STORED and DEFLATE are the only supported compression methods. - *
  • zip64 extensible data sectors are not supported. *
  • Zip files larger than Integer.MAX_VALUE bytes are not supported. *
  • The only supported ZIP64 field is ENDTOT. This implementation assumes that the ZIP64 end * header is present only if ENDTOT in EOCD header is 0xFFFF. -- cgit v1.2.3 From dd578b81d048aff7bab4d4772eb37ef11d5cd3b2 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Sat, 30 Apr 2022 16:59:45 -0700 Subject: Use well-formed URIs for `FileObject#toURI` The implementation doesn't support converting `URI`s for `FileObject`s to `Path`s and doing IO on them, but this at least makes them well-formed, and avoids errors like: ``` jshell> Paths.get(URI.create("file://foo")) | Exception java.lang.IllegalArgumentException: URI has an authority component | at UnixUriUtils.fromUri (UnixUriUtils.java:54) | at UnixFileSystemProvider.getPath (UnixFileSystemProvider.java:102) | at Path.of (Path.java:203) | at Paths.get (Paths.java:98) | at (#8:1) ``` https://github.com/micronaut-projects/micronaut-core/issues/7257 PiperOrigin-RevId: 445683352 --- .../google/turbine/processing/TurbineFiler.java | 4 +- .../processing/ProcessingIntegrationTest.java | 60 ++++++++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/java/com/google/turbine/processing/TurbineFiler.java b/java/com/google/turbine/processing/TurbineFiler.java index 45cdc22..f440ada 100644 --- a/java/com/google/turbine/processing/TurbineFiler.java +++ b/java/com/google/turbine/processing/TurbineFiler.java @@ -232,7 +232,7 @@ public class TurbineFiler implements Filer { @Override public URI toUri() { - return URI.create("file://" + path); + return URI.create("file:///" + path); } @Override @@ -309,7 +309,7 @@ public class TurbineFiler implements Filer { @Override public URI toUri() { - return URI.create("file://" + name + kind.extension); + return URI.create("file:///" + name + kind.extension); } @Override diff --git a/javatests/com/google/turbine/processing/ProcessingIntegrationTest.java b/javatests/com/google/turbine/processing/ProcessingIntegrationTest.java index fee2c75..65c7ed5 100644 --- a/javatests/com/google/turbine/processing/ProcessingIntegrationTest.java +++ b/javatests/com/google/turbine/processing/ProcessingIntegrationTest.java @@ -48,6 +48,9 @@ import java.io.IOException; import java.io.PrintWriter; import java.io.UncheckedIOException; import java.io.Writer; +import java.net.URI; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Optional; import java.util.Set; import javax.annotation.processing.AbstractProcessor; @@ -61,6 +64,7 @@ import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.type.ExecutableType; import javax.tools.Diagnostic; +import javax.tools.FileObject; import javax.tools.JavaFileObject; import javax.tools.StandardLocation; import org.junit.Test; @@ -804,4 +808,60 @@ public class ProcessingIntegrationTest { "C#f(java.util.List)U <: A#f(java.util.List)U ? false", "C#f(java.util.List)U <: B#f(java.util.List)U ? false"); } + + @SupportedAnnotationTypes("*") + public static class URIProcessor extends AbstractProcessor { + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latestSupported(); + } + + private boolean first = true; + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (!first) { + return false; + } + first = false; + try { + FileObject output = + processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "foo", "Bar"); + Path path = Paths.get(output.toUri()); + processingEnv + .getMessager() + .printMessage(Diagnostic.Kind.ERROR, output.toUri() + " - " + path); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + return false; + } + } + + @Test + public void uriProcessing() throws IOException { + ImmutableList units = + parseUnit( + "=== T.java ===", // + "class T {}"); + TurbineError e = + assertThrows( + TurbineError.class, + () -> + Binder.bind( + units, + ClassPathBinder.bindClasspath(ImmutableList.of()), + ProcessorInfo.create( + ImmutableList.of(new URIProcessor()), + getClass().getClassLoader(), + ImmutableMap.of(), + SourceVersion.latestSupported()), + TestClassPaths.TURBINE_BOOTCLASSPATH, + Optional.empty())); + assertThat( + e.diagnostics().stream() + .filter(d -> d.severity().equals(Diagnostic.Kind.ERROR)) + .map(d -> d.message())) + .containsExactly("file:///foo/Bar - " + Paths.get(URI.create("file:///foo/Bar"))); + } } -- cgit v1.2.3 From d3c79b4ea0a08383c25cd8d6a1814dd48577a34d Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Sun, 8 May 2022 14:25:11 -0700 Subject: Update ci.yml Add 19-ea, use released 18 --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e12698c..c5cea7a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,19 +29,19 @@ jobs: fail-fast: false matrix: os: [ ubuntu-latest ] - java: [ 17, 11 ] + java: [ 18, 17, 11 ] experimental: [ false ] include: # Only test on macos and windows with a single recent JDK to avoid a # combinatorial explosion of test configurations. - os: macos-latest - java: 17 + java: 18 experimental: false - os: windows-latest - java: 17 + java: 18 experimental: false - os: ubuntu-latest - java: 18-ea + java: 19-ea experimental: true runs-on: ${{ matrix.os }} continue-on-error: ${{ matrix.experimental }} -- cgit v1.2.3 From e0f2870e53290cfca4a6c09266aab86ea6ee10fd Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Tue, 10 May 2022 20:05:55 -0700 Subject: Update ASM version PiperOrigin-RevId: 447888943 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b007e74..38e91d8 100644 --- a/pom.xml +++ b/pom.xml @@ -30,7 +30,7 @@ https://github.com/google/turbine - 9.2 + 9.3 31.0.1-jre 2.11.0 3.3.1 -- cgit v1.2.3 From f42d03f5b18a61a3cdaf2f903e54618771c8797a Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Tue, 31 May 2022 12:39:45 -0700 Subject: Handle `sealed` psuedo-modifier on nested classes PiperOrigin-RevId: 452115286 --- java/com/google/turbine/parse/Parser.java | 5 +++++ javatests/com/google/turbine/lower/LowerIntegrationTest.java | 1 + javatests/com/google/turbine/lower/testdata/sealed_nested.test | 7 +++++++ 3 files changed, 13 insertions(+) create mode 100644 javatests/com/google/turbine/lower/testdata/sealed_nested.test diff --git a/java/com/google/turbine/parse/Parser.java b/java/com/google/turbine/parse/Parser.java index c370ad8..9417801 100644 --- a/java/com/google/turbine/parse/Parser.java +++ b/java/com/google/turbine/parse/Parser.java @@ -719,6 +719,11 @@ public class Parser { case IDENT: Ident ident = ident(); + if (ident.value().equals("sealed")) { + next(); + access.add(TurbineModifier.SEALED); + break; + } if (ident.value().equals("non")) { int pos = position; next(); diff --git a/javatests/com/google/turbine/lower/LowerIntegrationTest.java b/javatests/com/google/turbine/lower/LowerIntegrationTest.java index 7ae9b1b..94f1d07 100644 --- a/javatests/com/google/turbine/lower/LowerIntegrationTest.java +++ b/javatests/com/google/turbine/lower/LowerIntegrationTest.java @@ -48,6 +48,7 @@ public class LowerIntegrationTest { "record.test", 16, // "record2.test", 16, "sealed.test", 17, + "sealed_nested.test", 17, "textblock.test", 15); @Parameters(name = "{index}: {0}") diff --git a/javatests/com/google/turbine/lower/testdata/sealed_nested.test b/javatests/com/google/turbine/lower/testdata/sealed_nested.test new file mode 100644 index 0000000..6c4304e --- /dev/null +++ b/javatests/com/google/turbine/lower/testdata/sealed_nested.test @@ -0,0 +1,7 @@ +=== T.java === + +class T { + static sealed class Sealed permits Sealed.Foo { + static final class Foo extends Sealed {} + } +} -- cgit v1.2.3