diff options
37 files changed, 802 insertions, 160 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e12698c..54db52c 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: [ 19, 17, 11.0.16 ] 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: 19 experimental: false - os: windows-latest - java: 17 + java: 19 experimental: false - os: ubuntu-latest - java: 18-ea + java: 20-ea experimental: true runs-on: ${{ matrix.os }} continue-on-error: ${{ matrix.experimental }} @@ -55,4 +55,10 @@ java_library_host { proto: { type: "full", }, + + errorprone: { + javacflags: [ + "-Xep:NoCanIgnoreReturnValueOnClasses:WARN", + ], + }, } @@ -1,7 +1,5 @@ name: "turbine" -description: - "Turbine is a header compiler for Java." - +description: "Turbine is a header compiler for Java." third_party { url { type: HOMEPAGE @@ -11,7 +9,11 @@ third_party { type: GIT value: "https://github.com/google/turbine" } - version: "a963d859dc98108c37a701c1f76c4494fc480198" - last_upgrade_date { year: 2020 month: 3 day: 27 } + version: "f42d03f5b18a61a3cdaf2f903e54618771c8797a" license_type: NOTICE + last_upgrade_date { + year: 2022 + month: 6 + day: 1 + } } diff --git a/java/com/google/turbine/binder/FileManagerClassBinder.java b/java/com/google/turbine/binder/FileManagerClassBinder.java index d36d2d8..a807dd7 100644 --- a/java/com/google/turbine/binder/FileManagerClassBinder.java +++ b/java/com/google/turbine/binder/FileManagerClassBinder.java @@ -19,7 +19,6 @@ package com.google.turbine.binder; import com.google.common.base.Joiner; import com.google.common.base.Supplier; import com.google.common.collect.ImmutableMap; -import com.google.common.io.ByteStreams; import com.google.turbine.binder.bound.ModuleInfo; import com.google.turbine.binder.bytecode.BytecodeBoundClass; import com.google.turbine.binder.env.Env; @@ -113,7 +112,7 @@ public final class FileManagerClassBinder { @Override public byte[] get() { try { - return ByteStreams.toByteArray(jfo.openInputStream()); + return jfo.openInputStream().readAllBytes(); } catch (IOException e) { throw new UncheckedIOException(e); } @@ -162,7 +161,7 @@ public final class FileManagerClassBinder { @Override public byte[] get() { try { - return ByteStreams.toByteArray(fileObject.openInputStream()); + return fileObject.openInputStream().readAllBytes(); } catch (IOException e) { throw new UncheckedIOException(e); } diff --git a/java/com/google/turbine/bytecode/ByteReader.java b/java/com/google/turbine/bytecode/ByteReader.java index a9deff2..5458b49 100644 --- a/java/com/google/turbine/bytecode/ByteReader.java +++ b/java/com/google/turbine/bytecode/ByteReader.java @@ -20,11 +20,9 @@ import static com.google.common.base.Verify.verify; import com.google.common.io.ByteArrayDataInput; import com.google.common.io.ByteStreams; -import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.ByteArrayInputStream; /** A {@link ByteArrayDataInput} wrapper that tracks the current byte array index. */ -@CanIgnoreReturnValue public class ByteReader { private final byte[] bytes; diff --git a/java/com/google/turbine/bytecode/ClassReader.java b/java/com/google/turbine/bytecode/ClassReader.java index 740026a..da35196 100644 --- a/java/com/google/turbine/bytecode/ClassReader.java +++ b/java/com/google/turbine/bytecode/ClassReader.java @@ -159,7 +159,7 @@ public class ClassReader { /** Reads a JVMS 4.7.9 Signature attribute. */ private String readSignature(ConstantPoolReader constantPool) { String signature; - reader.u4(); // length + int unusedLength = reader.u4(); signature = constantPool.utf8(reader.u2()); return signature; } @@ -167,7 +167,7 @@ public class ClassReader { /** Reads JVMS 4.7.6 InnerClasses attributes. */ private List<ClassFile.InnerClass> readInnerClasses( ConstantPoolReader constantPool, String thisClass) { - reader.u4(); // length + int unusedLength = reader.u4(); int numberOfClasses = reader.u2(); List<ClassFile.InnerClass> innerclasses = new ArrayList<>(); for (int i = 0; i < numberOfClasses; i++) { @@ -197,7 +197,7 @@ public class ClassReader { private void readAnnotations( ImmutableList.Builder<ClassFile.AnnotationInfo> annotations, ConstantPoolReader constantPool) { - reader.u4(); // length + int unusedLength = reader.u4(); int numAnnotations = reader.u2(); for (int n = 0; n < numAnnotations; n++) { annotations.add(readAnnotation(constantPool)); @@ -207,7 +207,7 @@ public class ClassReader { /** Processes a JVMS 4.7.18 RuntimeVisibleParameterAnnotations attribute */ public void readParameterAnnotations( List<ImmutableList.Builder<AnnotationInfo>> annotations, ConstantPoolReader constantPool) { - reader.u4(); // length + int unusedLength = reader.u4(); int numParameters = reader.u1(); while (annotations.size() < numParameters) { annotations.add(ImmutableList.builder()); @@ -223,7 +223,7 @@ public class ClassReader { /** Processes a JVMS 4.7.24 MethodParameters attribute. */ private void readMethodParameters( ImmutableList.Builder<ParameterInfo> parameters, ConstantPoolReader constantPool) { - reader.u4(); // length + int unusedLength = reader.u4(); int numParameters = reader.u1(); for (int i = 0; i < numParameters; i++) { String name = constantPool.utf8(reader.u2()); @@ -239,7 +239,7 @@ public class ClassReader { /** Processes a JVMS 4.7.25 Module attribute. */ private ModuleInfo readModule(ConstantPoolReader constantPool) { - reader.u4(); // length + int unusedLength = reader.u4(); String name = constantPool.moduleInfo(reader.u2()); int flags = reader.u2(); int versionIndex = reader.u2(); @@ -423,7 +423,7 @@ public class ClassReader { signature = readSignature(constantPool); break; case "AnnotationDefault": - reader.u4(); // length + int unusedLength = reader.u4(); defaultValue = readElementValue(constantPool); break; case "RuntimeInvisibleAnnotations": @@ -470,7 +470,7 @@ public class ClassReader { /** Reads an Exceptions attribute. */ private ImmutableList<String> readExceptions(ConstantPoolReader constantPool) { ImmutableList.Builder<String> exceptions = ImmutableList.builder(); - reader.u4(); // length + int unusedLength = reader.u4(); int numberOfExceptions = reader.u2(); for (int exceptionIndex = 0; exceptionIndex < numberOfExceptions; exceptionIndex++) { exceptions.add(constantPool.classInfo(reader.u2())); @@ -496,7 +496,7 @@ public class ClassReader { String attributeName = constantPool.utf8(reader.u2()); switch (attributeName) { case "ConstantValue": - reader.u4(); // length + int unusedLength = reader.u4(); value = constantPool.constant(reader.u2()); break; case "RuntimeInvisibleAnnotations": @@ -525,7 +525,7 @@ public class ClassReader { } private String readTurbineTransitiveJar(ConstantPoolReader constantPool) { - reader.u4(); // length + int unusedLength = reader.u4(); return constantPool.utf8(reader.u2()); } } diff --git a/java/com/google/turbine/main/Main.java b/java/com/google/turbine/main/Main.java index da97bcd..34984a8 100644 --- a/java/com/google/turbine/main/Main.java +++ b/java/com/google/turbine/main/Main.java @@ -58,7 +58,6 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.time.LocalDateTime; -import java.time.ZoneId; import java.util.Arrays; import java.util.Collection; import java.util.Map; @@ -433,16 +432,11 @@ public final class Main { } /** Normalize timestamps. */ - static final long DEFAULT_TIMESTAMP = - LocalDateTime.of(2010, 1, 1, 0, 0, 0) - .atZone(ZoneId.systemDefault()) - .toInstant() - .toEpochMilli(); + static final LocalDateTime DEFAULT_TIMESTAMP = LocalDateTime.of(2010, 1, 1, 0, 0, 0); private static void addEntry(JarOutputStream jos, String name, byte[] bytes) throws IOException { JarEntry je = new JarEntry(name); - // TODO(cushon): switch to setLocalTime after we migrate to JDK 9 - je.setTime(DEFAULT_TIMESTAMP); + je.setTimeLocal(DEFAULT_TIMESTAMP); je.setMethod(ZipEntry.STORED); je.setSize(bytes.length); je.setCrc(Hashing.crc32().hashBytes(bytes).padToLong()); diff --git a/java/com/google/turbine/options/LanguageVersion.java b/java/com/google/turbine/options/LanguageVersion.java index e2b0ea7..d8641b4 100644 --- a/java/com/google/turbine/options/LanguageVersion.java +++ b/java/com/google/turbine/options/LanguageVersion.java @@ -53,7 +53,7 @@ public abstract class LanguageVersion { try { return SourceVersion.valueOf("RELEASE_" + source()); } catch (IllegalArgumentException unused) { - throw new IllegalArgumentException("invalid -source version: " + source()); + return SourceVersion.latestSupported(); } } diff --git a/java/com/google/turbine/parse/ConstExpressionParser.java b/java/com/google/turbine/parse/ConstExpressionParser.java index 8b7466f..e4aad6b 100644 --- a/java/com/google/turbine/parse/ConstExpressionParser.java +++ b/java/com/google/turbine/parse/ConstExpressionParser.java @@ -588,11 +588,11 @@ public class ConstExpressionParser { } eat(); int pos = position; - Tree.ConstVarName constVarName = (Tree.ConstVarName) qualIdent(); - if (constVarName == null) { + Expression constVarName = qualIdent(); + if (!(constVarName instanceof Tree.ConstVarName)) { return null; } - ImmutableList<Ident> name = constVarName.name(); + ImmutableList<Ident> name = ((Tree.ConstVarName) constVarName).name(); ImmutableList.Builder<Tree.Expression> args = ImmutableList.builder(); if (token == Token.LPAREN) { eat(); diff --git a/java/com/google/turbine/parse/Parser.java b/java/com/google/turbine/parse/Parser.java index c370ad8..acf84d7 100644 --- a/java/com/google/turbine/parse/Parser.java +++ b/java/com/google/turbine/parse/Parser.java @@ -547,6 +547,7 @@ public class Parser { switch (token) { case IDENT: { + String javadoc = lexer.javadoc(); Ident name = eatIdent(); if (token == Token.LPAREN) { dropParens(); @@ -569,7 +570,7 @@ public class Parser { ImmutableList.of()), name, Optional.<Expression>empty(), - null)); + javadoc)); annos = ImmutableList.builder(); break; } @@ -719,6 +720,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/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<String> 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/java/com/google/turbine/processing/TurbineElements.java b/java/com/google/turbine/processing/TurbineElements.java index b5fd7f4..9b3ea26 100644 --- a/java/com/google/turbine/processing/TurbineElements.java +++ b/java/com/google/turbine/processing/TurbineElements.java @@ -384,7 +384,7 @@ public class TurbineElements implements Elements { return false; } TypeMirror a = overrider.asType(); - TypeMirror b = types.asMemberOf((DeclaredType) type.asType(), overridden); + TypeMirror b = types.asMemberOfInternal((DeclaredType) type.asType(), overridden); if (b == null) { return false; } diff --git a/java/com/google/turbine/processing/TurbineFiler.java b/java/com/google/turbine/processing/TurbineFiler.java index 45cdc22..8c522ba 100644 --- a/java/com/google/turbine/processing/TurbineFiler.java +++ b/java/com/google/turbine/processing/TurbineFiler.java @@ -24,7 +24,6 @@ import com.google.common.base.Function; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableMap; -import com.google.common.io.ByteStreams; import com.google.turbine.diag.SourceFile; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -232,7 +231,7 @@ public class TurbineFiler implements Filer { @Override public URI toUri() { - return URI.create("file://" + path); + return URI.create("file:///" + path); } @Override @@ -309,7 +308,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 @@ -380,7 +379,7 @@ public class TurbineFiler implements Filer { @Override public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { - return new String(ByteStreams.toByteArray(openInputStream()), UTF_8); + return new String(openInputStream().readAllBytes(), UTF_8); } } diff --git a/java/com/google/turbine/processing/TurbineTypes.java b/java/com/google/turbine/processing/TurbineTypes.java index d2068dd..467059c 100644 --- a/java/com/google/turbine/processing/TurbineTypes.java +++ b/java/com/google/turbine/processing/TurbineTypes.java @@ -26,18 +26,11 @@ import com.google.common.collect.ImmutableMap; import com.google.turbine.binder.bound.TypeBoundClass; import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo; import com.google.turbine.binder.sym.ClassSymbol; -import com.google.turbine.binder.sym.FieldSymbol; -import com.google.turbine.binder.sym.MethodSymbol; -import com.google.turbine.binder.sym.ParamSymbol; -import com.google.turbine.binder.sym.RecordComponentSymbol; import com.google.turbine.binder.sym.Symbol; import com.google.turbine.binder.sym.TyVarSymbol; import com.google.turbine.model.TurbineConstantTypeKind; import com.google.turbine.model.TurbineTyKind; -import com.google.turbine.processing.TurbineElement.TurbineExecutableElement; -import com.google.turbine.processing.TurbineElement.TurbineFieldElement; import com.google.turbine.processing.TurbineElement.TurbineTypeElement; -import com.google.turbine.processing.TurbineElement.TurbineTypeParameterElement; import com.google.turbine.processing.TurbineTypeMirror.TurbineDeclaredType; import com.google.turbine.processing.TurbineTypeMirror.TurbineErrorType; import com.google.turbine.processing.TurbineTypeMirror.TurbineTypeVariable; @@ -1127,41 +1120,6 @@ public class TurbineTypes implements Types { .build())); } - private static ClassSymbol enclosingClass(Symbol symbol) { - switch (symbol.symKind()) { - case CLASS: - return (ClassSymbol) symbol; - case TY_PARAM: - return enclosingClass(((TyVarSymbol) symbol).owner()); - case METHOD: - return ((MethodSymbol) symbol).owner(); - case FIELD: - return ((FieldSymbol) symbol).owner(); - case PARAMETER: - return ((ParamSymbol) symbol).owner().owner(); - case RECORD_COMPONENT: - return ((RecordComponentSymbol) symbol).owner(); - case MODULE: - case PACKAGE: - throw new IllegalArgumentException(symbol.symKind().toString()); - } - throw new AssertionError(symbol.symKind()); - } - - private static Type type(Element element) { - switch (element.getKind()) { - case TYPE_PARAMETER: - return TyVar.create(((TurbineTypeParameterElement) element).sym(), ImmutableList.of()); - case FIELD: - return ((TurbineFieldElement) element).info().type(); - case METHOD: - case CONSTRUCTOR: - return ((TurbineExecutableElement) element).info().asType(); - default: - throw new UnsupportedOperationException(element.toString()); - } - } - /** * Returns the {@link TypeMirror} of the given {@code element} as a member of {@code containing}, * or else {@code null} if it is not a member. @@ -1171,13 +1129,24 @@ public class TurbineTypes implements Types { */ @Override public TypeMirror asMemberOf(DeclaredType containing, Element element) { + TypeMirror result = asMemberOfInternal(containing, element); + if (result == null) { + throw new IllegalArgumentException(String.format("asMemberOf(%s, %s)", containing, element)); + } + return result; + } + + public @Nullable TypeMirror asMemberOfInternal(DeclaredType containing, Element element) { ClassTy c = ((TurbineDeclaredType) containing).asTurbineType(); - ClassSymbol symbol = enclosingClass(((TurbineElement) element).sym()); - ImmutableList<ClassTy> path = factory.cha().search(c, enclosingClass(symbol)); + Symbol enclosing = ((TurbineElement) element.getEnclosingElement()).sym(); + if (!enclosing.symKind().equals(Symbol.Kind.CLASS)) { + return null; + } + ImmutableList<ClassTy> path = factory.cha().search(c, (ClassSymbol) enclosing); if (path.isEmpty()) { return null; } - Type type = type(element); + Type type = asTurbineType(element.asType()); for (ClassTy ty : path) { ImmutableMap<TyVarSymbol, Type> mapping = getMapping(ty); if (mapping == null) { diff --git a/java/com/google/turbine/type/Type.java b/java/com/google/turbine/type/Type.java index 085346a..5fbf1b1 100644 --- a/java/com/google/turbine/type/Type.java +++ b/java/com/google/turbine/type/Type.java @@ -17,6 +17,7 @@ package com.google.turbine.type; import static com.google.common.collect.Iterables.getLast; +import static java.lang.Math.max; import static java.util.Objects.requireNonNull; import com.google.auto.value.AutoValue; @@ -144,15 +145,23 @@ public interface Type { StringBuilder sb = new StringBuilder(); boolean first = true; for (SimpleClassTy c : classes()) { - for (AnnoInfo anno : c.annos()) { - sb.append(anno); - sb.append(' '); - } + String binaryName = c.sym().binaryName(); if (!first) { + for (AnnoInfo anno : c.annos()) { + sb.append(anno); + sb.append(' '); + } sb.append('.'); - sb.append(c.sym().binaryName().substring(c.sym().binaryName().lastIndexOf('$') + 1)); + sb.append(binaryName, binaryName.lastIndexOf('$') + 1, binaryName.length()); } else { - sb.append(c.sym().binaryName().replace('/', '.').replace('$', '.')); + int idx = max(binaryName.lastIndexOf('/'), binaryName.lastIndexOf('$')) + 1; + String name = binaryName.replace('/', '.').replace('$', '.'); + sb.append(name, 0, idx); + for (AnnoInfo anno : c.annos()) { + sb.append(anno); + sb.append(' '); + } + sb.append(name, idx, name.length()); } if (!c.targs().isEmpty()) { sb.append('<'); diff --git a/java/com/google/turbine/zip/Zip.java b/java/com/google/turbine/zip/Zip.java index fa0f0e0..c08999b 100644 --- a/java/com/google/turbine/zip/Zip.java +++ b/java/com/google/turbine/zip/Zip.java @@ -18,7 +18,6 @@ package com.google.turbine.zip; import static java.nio.charset.StandardCharsets.UTF_8; -import com.google.common.io.ByteStreams; import com.google.common.primitives.UnsignedInts; import java.io.ByteArrayInputStream; import java.io.Closeable; @@ -65,7 +64,6 @@ import java.util.zip.ZipException; * supported. * <li>UTF-8 is the only supported encoding. * <li>STORED and DEFLATE are the only supported compression methods. - * <li>zip64 extensible data sectors are not supported. * <li>Zip files larger than Integer.MAX_VALUE bytes are not supported. * <li>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. @@ -74,6 +72,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 +195,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<Entry> iterator() { return new ZipIterator(path, chan, cd); @@ -308,9 +331,9 @@ public final class Zip { fc.get(bytes); if (deflate) { bytes = - ByteStreams.toByteArray( - new InflaterInputStream( - new ByteArrayInputStream(bytes), new Inflater(/*nowrap=*/ true))); + new InflaterInputStream( + new ByteArrayInputStream(bytes), new Inflater(/*nowrap=*/ true)) + .readAllBytes(); } return bytes; } catch (IOException e) { diff --git a/javatests/com/google/turbine/binder/bytecode/BytecodeBoundClassTest.java b/javatests/com/google/turbine/binder/bytecode/BytecodeBoundClassTest.java index 65d973d..e2d54bd 100644 --- a/javatests/com/google/turbine/binder/bytecode/BytecodeBoundClassTest.java +++ b/javatests/com/google/turbine/binder/bytecode/BytecodeBoundClassTest.java @@ -24,7 +24,6 @@ import static com.google.turbine.testing.TestClassPaths.TURBINE_BOOTCLASSPATH; import static java.util.Objects.requireNonNull; import com.google.common.collect.Iterables; -import com.google.common.io.ByteStreams; import com.google.turbine.binder.bound.TurbineClassValue; import com.google.turbine.binder.bound.TypeBoundClass; import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo; @@ -195,7 +194,7 @@ public class BytecodeBoundClassTest { private static byte[] toByteArrayOrDie(InputStream is) { try { - return ByteStreams.toByteArray(is); + return is.readAllBytes(); } catch (IOException e) { throw new UncheckedIOException(e); } diff --git a/javatests/com/google/turbine/deps/TransitiveTest.java b/javatests/com/google/turbine/deps/TransitiveTest.java index f08e899..3829ddd 100644 --- a/javatests/com/google/turbine/deps/TransitiveTest.java +++ b/javatests/com/google/turbine/deps/TransitiveTest.java @@ -26,7 +26,6 @@ import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; -import com.google.common.io.ByteStreams; import com.google.protobuf.ExtensionRegistry; import com.google.turbine.bytecode.ClassFile; import com.google.turbine.bytecode.ClassFile.InnerClass; @@ -87,7 +86,7 @@ public class TransitiveTest { Enumeration<JarEntry> entries = jf.entries(); while (entries.hasMoreElements()) { JarEntry je = entries.nextElement(); - jarEntries.put(je.getName(), ByteStreams.toByteArray(jf.getInputStream(je))); + jarEntries.put(je.getName(), jf.getInputStream(je).readAllBytes()); } } return jarEntries; diff --git a/javatests/com/google/turbine/lower/LongStringIntegrationTest.java b/javatests/com/google/turbine/lower/LongStringIntegrationTest.java index a462b69..7bb61e5 100644 --- a/javatests/com/google/turbine/lower/LongStringIntegrationTest.java +++ b/javatests/com/google/turbine/lower/LongStringIntegrationTest.java @@ -44,7 +44,7 @@ public class LongStringIntegrationTest { public void test() throws Exception { Map<String, byte[]> output = runTurbineWithStack( - /* stackSize= */ 1, + /* stackSize= */ 100_000, /* input= */ ImmutableMap.of("Test.java", source()), /* classpath= */ ImmutableList.of()); @@ -70,7 +70,7 @@ public class LongStringIntegrationTest { }, /* name= */ "turbine", stackSize); - t.run(); + t.start(); t.join(); return output; } diff --git a/javatests/com/google/turbine/lower/LowerIntegrationTest.java b/javatests/com/google/turbine/lower/LowerIntegrationTest.java index 97170ca..94f1d07 100644 --- a/javatests/com/google/turbine/lower/LowerIntegrationTest.java +++ b/javatests/com/google/turbine/lower/LowerIntegrationTest.java @@ -44,7 +44,12 @@ import org.junit.runners.Parameterized.Parameters; public class LowerIntegrationTest { private static final ImmutableMap<String, Integer> SOURCE_VERSION = - ImmutableMap.of("record.test", 16, "record2.test", 16, "sealed.test", 17); + ImmutableMap.of( + "record.test", 16, // + "record2.test", 16, + "sealed.test", 17, + "sealed_nested.test", 17, + "textblock.test", 15); @Parameters(name = "{index}: {0}") public static Iterable<Object[]> parameters() { @@ -285,6 +290,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/LowerTest.java b/javatests/com/google/turbine/lower/LowerTest.java index 6d3a6df..a6410db 100644 --- a/javatests/com/google/turbine/lower/LowerTest.java +++ b/javatests/com/google/turbine/lower/LowerTest.java @@ -312,7 +312,7 @@ public class LowerTest { String attributeName = pool.utf8(reader.u2()); switch (attributeName) { case "Signature": - reader.u4(); // length + int unusedLength = reader.u4(); signature = pool.utf8(reader.u2()); break; default: 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 {} + } +} 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/main/MainTest.java b/javatests/com/google/turbine/main/MainTest.java index 3504891..c894d9d 100644 --- a/javatests/com/google/turbine/main/MainTest.java +++ b/javatests/com/google/turbine/main/MainTest.java @@ -28,7 +28,6 @@ import static org.junit.Assert.assertThrows; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.common.io.ByteStreams; import com.google.common.io.MoreFiles; import com.google.protobuf.ExtensionRegistry; import com.google.turbine.diag.TurbineError; @@ -148,7 +147,7 @@ public class MainTest { Enumeration<JarEntry> entries = jf.entries(); while (entries.hasMoreElements()) { JarEntry je = entries.nextElement(); - data.put(je.getName(), ByteStreams.toByteArray(jf.getInputStream(je))); + data.put(je.getName(), jf.getInputStream(je).readAllBytes()); } } return data; diff --git a/javatests/com/google/turbine/options/LanguageVersionTest.java b/javatests/com/google/turbine/options/LanguageVersionTest.java index 601652c..a5b303d 100644 --- a/javatests/com/google/turbine/options/LanguageVersionTest.java +++ b/javatests/com/google/turbine/options/LanguageVersionTest.java @@ -140,8 +140,6 @@ public class LanguageVersionTest { public void unsupportedSourceVersion() { LanguageVersion languageVersion = LanguageVersion.fromJavacopts(ImmutableList.of("-source", "9999")); - IllegalArgumentException expected = - assertThrows(IllegalArgumentException.class, languageVersion::sourceVersion); - assertThat(expected).hasMessageThat().contains("invalid -source version:"); + assertThat(languageVersion.sourceVersion()).isEqualTo(SourceVersion.latestSupported()); } } diff --git a/javatests/com/google/turbine/parse/CommentParserTest.java b/javatests/com/google/turbine/parse/CommentParserTest.java index a2f84d5..d10d34d 100644 --- a/javatests/com/google/turbine/parse/CommentParserTest.java +++ b/javatests/com/google/turbine/parse/CommentParserTest.java @@ -58,6 +58,13 @@ public class CommentParserTest { " * class C", " */", " class C {}", + " /** This is an enum. */", + " enum E {", + " /** This is H. */", + " H,", + " /** This is I. */", + " I", + " }", "}\n")); TyDecl decl = getOnlyElement(unit.decls()); assertThat(decl.javadoc()).isEqualTo(" hello world "); @@ -68,11 +75,17 @@ public class CommentParserTest { .collect(toImmutableMap(c -> c.name().value(), c -> c.javadoc()))) .containsExactly( "A", "\n * This is\n * class A\n ", - "C", "\n * This is\n * class C\n "); + "C", "\n * This is\n * class C\n ", + "E", " This is an enum. "); TyDecl a = (TyDecl) decl.members().get(0); MethDecl f = (MethDecl) a.members().get(0); assertThat(f.javadoc()).isEqualTo(" This is a method "); VarDecl g = (VarDecl) a.members().get(1); assertThat(g.javadoc()).isEqualTo(" This is a field "); + TyDecl e = (TyDecl) decl.members().get(3); + VarDecl h = (VarDecl) e.members().get(0); + assertThat(h.javadoc()).isEqualTo(" This is H. "); + VarDecl i = (VarDecl) e.members().get(1); + assertThat(i.javadoc()).isEqualTo(" This is I. "); } } 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..4a92648 100644 --- a/javatests/com/google/turbine/parse/ParseErrorTest.java +++ b/javatests/com/google/turbine/parse/ParseErrorTest.java @@ -307,6 +307,32 @@ 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 = \"\"\" \"\"\"; }", + " ^")); + } + + @Test + public void annotationClassLiteral() { + String input = "@interface A { A value() default @Integer.class; }"; + TurbineError e = assertThrows(TurbineError.class, () -> Parser.parse(input)); + assertThat(e) + .hasMessageThat() + .isEqualTo( + lines( + "<>:1: error: unexpected token: ;", + "@interface A { A value() default @Integer.class; }", + " ^")); + } + private static String lines(String... lines) { return Joiner.on(System.lineSeparator()).join(lines); } diff --git a/javatests/com/google/turbine/processing/AbstractTurbineTypesBiFunctionTest.java b/javatests/com/google/turbine/processing/AbstractTurbineTypesBiFunctionTest.java new file mode 100644 index 0000000..e00673d --- /dev/null +++ b/javatests/com/google/turbine/processing/AbstractTurbineTypesBiFunctionTest.java @@ -0,0 +1,47 @@ +/* + * Copyright 2022 Google Inc. 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 + * + * http://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 com.google.turbine.processing; + +import static com.google.common.truth.Truth.assertWithMessage; + +import javax.lang.model.element.Element; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.util.Types; + +/** + * A combo test for {@link TurbineTypes} that compares the behaviour of bifunctions like {@link + * Types#asMemberOf(DeclaredType, Element)} with javac's implementation. + */ +abstract class AbstractTurbineTypesBiFunctionTest<T> extends AbstractTurbineTypesTest { + + final String testDescription; + final TypesBiFunctionInput javacInput; + final TypesBiFunctionInput turbineInput; + + public AbstractTurbineTypesBiFunctionTest( + String testDescription, TypesBiFunctionInput javacInput, TypesBiFunctionInput turbineInput) { + this.testDescription = testDescription; + this.javacInput = javacInput; + this.turbineInput = turbineInput; + } + + protected void test(String symbol, TypeBiFunction<T> predicate) { + assertWithMessage("%s = %s", javacInput.format(symbol), turbineInput.format(symbol)) + .that(turbineInput.apply(predicate)) + .isEqualTo(javacInput.apply(predicate)); + } +} diff --git a/javatests/com/google/turbine/processing/AbstractTurbineTypesBiPredicateTest.java b/javatests/com/google/turbine/processing/AbstractTurbineTypesBiPredicateTest.java index 6ea6e72..08891eb 100644 --- a/javatests/com/google/turbine/processing/AbstractTurbineTypesBiPredicateTest.java +++ b/javatests/com/google/turbine/processing/AbstractTurbineTypesBiPredicateTest.java @@ -16,8 +16,6 @@ package com.google.turbine.processing; -import static com.google.common.truth.Truth.assertWithMessage; - import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Types; @@ -25,22 +23,10 @@ import javax.lang.model.util.Types; * A combo test for {@link TurbineTypes} that compares the behaviour of bipredicates like {@link * Types#isSubtype(TypeMirror, TypeMirror)} with javac's implementation. */ -abstract class AbstractTurbineTypesBiPredicateTest extends AbstractTurbineTypesTest { - - final String testDescription; - final TypesBiFunctionInput javacInput; - final TypesBiFunctionInput turbineInput; - +abstract class AbstractTurbineTypesBiPredicateTest + extends AbstractTurbineTypesBiFunctionTest<Boolean> { public AbstractTurbineTypesBiPredicateTest( String testDescription, TypesBiFunctionInput javacInput, TypesBiFunctionInput turbineInput) { - this.testDescription = testDescription; - this.javacInput = javacInput; - this.turbineInput = turbineInput; - } - - protected void test(String symbol, TypeBiPredicate predicate) { - assertWithMessage("%s = %s", javacInput.format(symbol), turbineInput.format(symbol)) - .that(turbineInput.apply(predicate)) - .isEqualTo(javacInput.apply(predicate)); + super(testDescription, javacInput, turbineInput); } } diff --git a/javatests/com/google/turbine/processing/AbstractTurbineTypesTest.java b/javatests/com/google/turbine/processing/AbstractTurbineTypesTest.java index 7d8d479..02df1ec 100644 --- a/javatests/com/google/turbine/processing/AbstractTurbineTypesTest.java +++ b/javatests/com/google/turbine/processing/AbstractTurbineTypesTest.java @@ -31,6 +31,7 @@ import com.google.common.collect.ListMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; import com.google.common.collect.MultimapBuilder; +import com.google.common.collect.ObjectArrays; import com.google.common.collect.Streams; import com.google.turbine.binder.Binder; import com.google.turbine.binder.Binder.BindingResult; @@ -93,8 +94,8 @@ class AbstractTurbineTypesTest { } } - protected interface TypeBiPredicate { - boolean apply(Types types, TypeMirror a, TypeMirror b); + protected interface TypeBiFunction<T> { + T apply(Types types, TypeMirror a, TypeMirror b); } static class TypesBiFunctionInput { @@ -108,8 +109,8 @@ class AbstractTurbineTypesTest { this.rhs = rhs; } - boolean apply(TypeBiPredicate predicate) { - return predicate.apply(types, lhs, rhs); + <T> T apply(TypeBiFunction<T> function) { + return function.apply(types, lhs, rhs); } String format(String symbol) { @@ -232,18 +233,23 @@ class AbstractTurbineTypesTest { "Float", "Double", }, - // type annotations - { - "@A List<@B Integer>", - "@A List", - "@A int @B []", - "@A List<@A int @B []>", - "Map.@A Entry<@B Integer, @C Number>", - }, }; + + // type annotations + List<String> annotatedTypes = new ArrayList<>(); + annotatedTypes.add("@A int @B []"); + // The string representation of these types changed in JDK 19, see JDK-8281238 + if (Runtime.version().feature() >= 19) { + annotatedTypes.add("@A List<@B Integer>"); + annotatedTypes.add("@A List"); + annotatedTypes.add("@A List<@A int @B []>"); + annotatedTypes.add("Map.@A Entry<@B Integer, @C Number>"); + } + List<String> files = new ArrayList<>(); AtomicInteger idx = new AtomicInteger(); - for (String[] group : types) { + for (String[] group : + ObjectArrays.<String[]>concat(annotatedTypes.toArray(new String[0]), types)) { StringBuilder sb = new StringBuilder(); Joiner.on('\n') .appendTo( 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<U>(java.util.List<U>)U <: A#f<U>(java.util.List<U>)U ? false", "C#f<U>(java.util.List<U>)U <: B#f<U>(java.util.List<U>)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<? extends TypeElement> 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<Tree.CompUnit> 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"))); + } } diff --git a/javatests/com/google/turbine/processing/TurbineFilerTest.java b/javatests/com/google/turbine/processing/TurbineFilerTest.java index 83dcc70..96c325b 100644 --- a/javatests/com/google/turbine/processing/TurbineFilerTest.java +++ b/javatests/com/google/turbine/processing/TurbineFilerTest.java @@ -23,7 +23,6 @@ import static org.junit.Assert.assertThrows; import com.google.common.base.Function; import com.google.common.base.Supplier; -import com.google.common.io.ByteStreams; import com.google.common.io.CharStreams; import com.google.turbine.diag.SourceFile; import java.io.FileNotFoundException; @@ -127,8 +126,7 @@ public class TurbineFilerTest { Collection<SourceFile> unused = filer.finishRound(); FileObject output = filer.getResource(StandardLocation.SOURCE_OUTPUT, "com.foo", "Bar.java"); - assertThat(new String(ByteStreams.toByteArray(output.openInputStream()), UTF_8)) - .isEqualTo("hello"); + assertThat(new String(output.openInputStream().readAllBytes(), UTF_8)).isEqualTo("hello"); assertThat(output.getCharContent(false).toString()).isEqualTo("hello"); assertThat(CharStreams.toString(output.openReader(true))).isEqualTo("hello"); } @@ -142,8 +140,7 @@ public class TurbineFilerTest { Collection<SourceFile> unused = filer.finishRound(); FileObject output = filer.getResource(StandardLocation.CLASS_OUTPUT, "com.foo", "Baz.class"); - assertThat(new String(ByteStreams.toByteArray(output.openInputStream()), UTF_8)) - .isEqualTo("goodbye"); + assertThat(new String(output.openInputStream().readAllBytes(), UTF_8)).isEqualTo("goodbye"); assertThat(output.getCharContent(false).toString()).isEqualTo("goodbye"); assertThat(CharStreams.toString(output.openReader(true))).isEqualTo("goodbye"); } @@ -153,7 +150,7 @@ public class TurbineFilerTest { FileObject resource = filer.getResource(StandardLocation.ANNOTATION_PROCESSOR_PATH, "META-INF", "MANIFEST.MF"); - assertThat(new String(ByteStreams.toByteArray(resource.openInputStream()), UTF_8)) + assertThat(new String(resource.openInputStream().readAllBytes(), UTF_8)) .contains("Manifest-Version:"); assertThat(CharStreams.toString(resource.openReader(true))).contains("Manifest-Version:"); assertThat(resource.getCharContent(false).toString()).contains("Manifest-Version:"); diff --git a/javatests/com/google/turbine/processing/TurbineTypesAsMemberOfTest.java b/javatests/com/google/turbine/processing/TurbineTypesAsMemberOfTest.java new file mode 100644 index 0000000..1a368c9 --- /dev/null +++ b/javatests/com/google/turbine/processing/TurbineTypesAsMemberOfTest.java @@ -0,0 +1,76 @@ +/* + * Copyright 2022 Google Inc. 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 + * + * http://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 com.google.turbine.processing; + +import static com.google.common.truth.TruthJUnit.assume; +import static org.junit.Assert.assertThrows; + +import javax.lang.model.element.Element; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.TypeVariable; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class TurbineTypesAsMemberOfTest extends AbstractTurbineTypesBiFunctionTest<String> { + + @Parameters(name = "{index}: {0}") + public static Iterable<Object[]> parameters() throws Exception { + return binaryParameters(); + } + + public TurbineTypesAsMemberOfTest( + String testDescription, TypesBiFunctionInput javacInput, TypesBiFunctionInput turbineInput) { + super(testDescription, javacInput, turbineInput); + } + + @Test + public void asMemberOf() { + assume().that(javacInput.lhs.getKind()).isEqualTo(TypeKind.DECLARED); + assume().that(javacInput.rhs.getKind()).isAnyOf(TypeKind.TYPEVAR, TypeKind.DECLARED); + + TypeBiFunction<String> predicate = + (types, lhs, rhs) -> types.asMemberOf((DeclaredType) lhs, element(rhs)).toString(); + + try { + String unused = javacInput.apply(predicate); + } catch (IllegalArgumentException e) { + assertThrows( + turbineInput.format("asMemberOf"), + IllegalArgumentException.class, + () -> turbineInput.apply(predicate)); + return; + } + + test("asMemberOf", predicate); + } + + private static Element element(TypeMirror rhs) { + switch (rhs.getKind()) { + case TYPEVAR: + return ((TypeVariable) rhs).asElement(); + case DECLARED: + return ((DeclaredType) rhs).asElement(); + default: + throw new AssertionError(rhs.getKind()); + } + } +} diff --git a/javatests/com/google/turbine/testing/TestResources.java b/javatests/com/google/turbine/testing/TestResources.java index 86c7632..6c456ab 100644 --- a/javatests/com/google/turbine/testing/TestResources.java +++ b/javatests/com/google/turbine/testing/TestResources.java @@ -19,7 +19,6 @@ package com.google.turbine.testing; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Objects.requireNonNull; -import com.google.common.io.ByteStreams; import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; @@ -32,7 +31,7 @@ public final class TestResources { public static byte[] getResourceBytes(Class<?> clazz, String resource) { try (InputStream is = requireNonNull(clazz.getResourceAsStream(resource), resource)) { - return ByteStreams.toByteArray(is); + return is.readAllBytes(); } catch (IOException e) { throw new UncheckedIOException(e); } diff --git a/javatests/com/google/turbine/zip/ZipTest.java b/javatests/com/google/turbine/zip/ZipTest.java index e9dfc44..b64531a 100644 --- a/javatests/com/google/turbine/zip/ZipTest.java +++ b/javatests/com/google/turbine/zip/ZipTest.java @@ -22,9 +22,10 @@ import static org.junit.Assert.assertThrows; import com.google.common.collect.ImmutableMap; 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 +39,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; @@ -113,9 +115,7 @@ public class ZipTest { JarEntry je = entries.nextElement(); result.put( je.getName(), - Hashing.goodFastHash(128) - .hashBytes(ByteStreams.toByteArray(jf.getInputStream(je))) - .padToLong()); + Hashing.goodFastHash(128).hashBytes(jf.getInputStream(je).readAllBytes()).padToLong()); } } return result; @@ -164,4 +164,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)); + } } @@ -30,14 +30,15 @@ <url>https://github.com/google/turbine</url> <properties> - <asm.version>9.2</asm.version> + <asm.version>9.4</asm.version> <guava.version>31.0.1-jre</guava.version> - <errorprone.version>2.11.0</errorprone.version> + <errorprone.version>2.16</errorprone.version> <maven-javadoc-plugin.version>3.3.1</maven-javadoc-plugin.version> <maven-source-plugin.version>3.2.1</maven-source-plugin.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <protobuf.version>3.19.2</protobuf.version> <grpc.version>1.43.2</grpc.version> + <native.maven.plugin.version>0.9.11</native.maven.plugin.version> </properties> <organization> @@ -333,5 +334,44 @@ </plugins> </build> </profile> + <profile> + <id>native</id> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>${native.maven.plugin.version}</version> + <extensions>true</extensions> + <executions> + <execution> + <id>build-native</id> + <goals> + <goal>build</goal> + </goals> + <phase>package</phase> + </execution> + <execution> + <id>test-native</id> + <goals> + <goal>test</goal> + </goals> + <phase>test</phase> + </execution> + </executions> + <configuration> + <imageName>turbine</imageName> + <mainClass>com.google.turbine.main.Main</mainClass> + <classpath> + <param>${project.build.directory}/${project.artifactId}-${project.version}-all-deps.jar</param> + </classpath> + <buildArgs> + <buildArg>--no-fallback</buildArg> + </buildArgs> + </configuration> + </plugin> + </plugins> + </build> + </profile> </profiles> </project> |