Skip to content

Commit cedf483

Browse files
Merge branch 'master' into disallow-interpreation-of-arbitrary-calls-in-paramteres
2 parents 6e7599d + 4ad3ffb commit cedf483

File tree

74 files changed

+367
-206
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+367
-206
lines changed

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -376,8 +376,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
376376
else if (name.isTypeName) typeText(txt)
377377
else txt
378378
case tree @ Select(qual, name) =>
379-
if (!printDebug && tree.hasType && tree.symbol.isTypeSplice) typeText("${") ~ toTextLocal(qual) ~ typeText("}")
380-
else if (qual.isType) toTextLocal(qual) ~ "#" ~ typeText(toText(name))
379+
if (qual.isType) toTextLocal(qual) ~ "#" ~ typeText(toText(name))
381380
else toTextLocal(qual) ~ ("." ~ nameIdText(tree) provided (name != nme.CONSTRUCTOR || printDebug))
382381
case tree: This =>
383382
optDotPrefix(tree) ~ keywordStr("this") ~ idText(tree)

compiler/src/dotty/tools/io/ZipArchive.scala

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ abstract class ZipArchive(override val jpath: JPath) extends AbstractFile with E
108108
if (entry.isDirectory) ensureDir(dirs, entry.getName)
109109
else ensureDir(dirs, dirName(entry.getName))
110110
}
111+
112+
def close(): Unit
111113
}
112114
/** ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */
113115
final class FileZipArchive(jpath: JPath) extends ZipArchive(jpath) {
@@ -176,6 +178,7 @@ final class FileZipArchive(jpath: JPath) extends ZipArchive(jpath) {
176178
}
177179
} finally {
178180
if (ZipArchive.closeZipFile) zipFile.close()
181+
else closeables ::= zipFile
179182
}
180183
(root, dirs)
181184
}
@@ -194,15 +197,24 @@ final class FileZipArchive(jpath: JPath) extends ZipArchive(jpath) {
194197
case x: FileZipArchive => jpath.toAbsolutePath == x.jpath.toAbsolutePath
195198
case _ => false
196199
}
200+
201+
private[this] var closeables: List[java.io.Closeable] = Nil
202+
override def close(): Unit = {
203+
closeables.foreach(_.close)
204+
closeables = Nil
205+
}
197206
}
198207

199208
final class ManifestResources(val url: URL) extends ZipArchive(null) {
200209
def iterator(): Iterator[AbstractFile] = {
201210
val root = new DirEntry("/", null)
202211
val dirs = mutable.HashMap[String, DirEntry]("/" -> root)
203-
val manifest = new Manifest(input)
212+
val stream = input
213+
val manifest = new Manifest(stream)
204214
val iter = manifest.getEntries().keySet().iterator().asScala.filter(_.endsWith(".class")).map(new ZipEntry(_))
205215

216+
closeables ::= stream
217+
206218
for (zipEntry <- iter) {
207219
val dir = getDir(dirs, zipEntry)
208220
if (!zipEntry.isDirectory) {
@@ -251,4 +263,10 @@ final class ManifestResources(val url: URL) extends ZipArchive(null) {
251263
}
252264
}
253265
}
266+
267+
private[this] var closeables: List[java.io.Closeable] = Nil
268+
override def close(): Unit = {
269+
closeables.foreach(_.close())
270+
closeables = Nil
271+
}
254272
}
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
package dotty.tools.io
2+
3+
import java.io.IOException
4+
import java.net.{URI, URL, URLClassLoader}
5+
import java.nio.file.{Files, Path, Paths}
6+
import java.util.jar.{Attributes, Manifest, JarEntry, JarOutputStream}
7+
import java.lang.invoke.{MethodHandles, MethodType}
8+
9+
import org.junit.Assert._
10+
import org.junit.Test
11+
12+
import scala.util.chaining._
13+
import scala.util.Using
14+
15+
class ZipArchiveTest {
16+
17+
@Test
18+
def corruptZip(): Unit = {
19+
val f = Files.createTempFile("test", ".jar")
20+
val fza = new FileZipArchive(f)
21+
try {
22+
fza.iterator
23+
assert(false)
24+
}
25+
catch {
26+
case ex: IOException =>
27+
}
28+
finally {
29+
Files.delete(f)
30+
}
31+
}
32+
33+
@Test
34+
def missingFile(): Unit = {
35+
val f = Paths.get("xxx.does.not.exist")
36+
val fza = new FileZipArchive(f)
37+
try {
38+
fza.iterator
39+
assert(false)
40+
}
41+
catch {
42+
case ex: IOException =>
43+
}
44+
}
45+
46+
private val bootClassLoader: ClassLoader = {
47+
if (!util.Properties.isJavaAtLeast("9")) null
48+
else {
49+
try {
50+
MethodHandles
51+
.lookup()
52+
.findStatic(
53+
classOf[ClassLoader],
54+
"getPlatformClassLoader",
55+
MethodType.methodType(classOf[ClassLoader])
56+
)
57+
.invoke()
58+
.asInstanceOf[ClassLoader]
59+
} catch {
60+
case _: Throwable =>
61+
null
62+
}
63+
}
64+
}
65+
66+
private def classLoader(location: URI): ClassLoader =
67+
new URLClassLoader(Array(location.toURL), bootClassLoader)
68+
69+
private def manifestAt(location: URI): URL = classLoader(location).getResource("META-INF/MANIFEST.MF")
70+
71+
72+
// ZipArchive.fromManifestURL(URL)
73+
@Test def `manifest resources just works`(): Unit = {
74+
val jar = createTestJar()
75+
val archive = new ManifestResources(manifestAt(jar.toUri))
76+
try {
77+
val it = archive.iterator
78+
assertTrue(it.hasNext)
79+
val f = it.next()
80+
assertFalse(it.hasNext)
81+
assertEquals("foo.class", f.name)
82+
}
83+
finally {
84+
archive.close()
85+
// The following results in IOException on Windows (file in use by another process).
86+
// As jar created with Files.createTempFile, it will be deleted automatically.
87+
try Files.delete(jar) catch case _: IOException => ()
88+
}
89+
}
90+
91+
private def createTestJar(): Path = Files.createTempFile("junit", ".jar").tap { f =>
92+
val man = new Manifest()
93+
man.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0")
94+
man.getEntries().put("foo.class", new Attributes(0))
95+
Using.resource(new JarOutputStream(Files.newOutputStream(f), man)) { jout =>
96+
jout.putNextEntry(new JarEntry("foo.class"))
97+
val bytes = "hello, world".getBytes
98+
jout.write(bytes, 0, bytes.length)
99+
()
100+
}
101+
}
102+
103+
private def createTestZip(): Path = Files.createTempFile("junit", ".zip").tap { f =>
104+
import java.util.zip._
105+
Using.resource(new ZipOutputStream(Files.newOutputStream(f))) { zout =>
106+
zout.setLevel(Deflater.NO_COMPRESSION)
107+
zout.setMethod(ZipOutputStream.STORED)
108+
val entry = new ZipEntry("foo.class")
109+
val bytes = "hello, world".getBytes
110+
entry.setSize(bytes.length)
111+
entry.setCompressedSize(bytes.length)
112+
entry.setCrc(new CRC32().tap(_.update(bytes, 0, bytes.length)).getValue)
113+
zout.putNextEntry(entry)
114+
zout.write(bytes, 0, bytes.length)
115+
zout.closeEntry()
116+
()
117+
}
118+
}
119+
/* zipfs doesn't write size field in file header as required by URLZipArchive
120+
private def createTestZip2(): Path = {
121+
import java.nio.file.FileSystems
122+
import java.net.URI
123+
import scala.util.chaining._
124+
import scala.jdk.CollectionConverters._
125+
val f = Files.createTempFile("junit", ".zip")
126+
Files.delete(f)
127+
val uri = URI.create(s"jar:${f.toUri}")
128+
val env = Map("create" -> "true").asJava
129+
Using.resource(FileSystems.newFileSystem(uri, env)) { fs =>
130+
val path = fs.getPath("foo.class")
131+
val bytes = "hello, world".getBytes
132+
Files.write(path, bytes)
133+
}
134+
f.tap(println(_))
135+
}
136+
*/
137+
}

stdlib-bootstrapped-tasty-tests/test/BootstrappedStdLibTASYyTest.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ object BootstrappedStdLibTASYyTest:
108108
()
109109
}
110110
val classNames = scalaLibJarTastyClassNames.filterNot(blacklisted)
111-
val hasErrors = inspector.inspect(scalaLibJarPath, classNames)
111+
val hasErrors = inspector.inspectTastyFilesInJar(scalaLibJarPath)
112112
assert(!hasErrors, "Errors reported while loading from TASTy")
113113

114114
def compileFromTasty(blacklisted: String => Boolean): Unit = {

tasty-inspector/src/scala/tasty/inspector/TastyInspector.scala

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,37 @@ trait TastyInspector:
2222

2323
/** Load and process TASTy files using TASTy reflect
2424
*
25-
* @param classpath Classpath where the classes are located
26-
* @param classes classes to be inspected
27-
* @return if an error was reported
25+
* @param tastyFiles List of paths of `.tasty` files
2826
*/
29-
def inspect(classpath: String, classes: List[String]): Boolean =
27+
def inspectTastyFiles(tastyFiles: List[String]): Boolean =
28+
inspectAllTastyFiles(tastyFiles, Nil, Nil)
29+
30+
/** Load and process TASTy files in a `jar` file using TASTy reflect
31+
*
32+
* @param jars Path of `.jar` file
33+
*/
34+
def inspectTastyFilesInJar(jar: String): Boolean =
35+
inspectAllTastyFiles(Nil, List(jar), Nil)
36+
37+
/** Load and process TASTy files using TASTy reflect
38+
*
39+
* @param tastyFiles List of paths of `.tasty` files
40+
* @param jars List of path of `.jar` files
41+
* @param dependenciesClasspath Classpath with extra dependencies needed to load class in the `.tasty` files
42+
*/
43+
def inspectAllTastyFiles(tastyFiles: List[String], jars: List[String], dependenciesClasspath: List[String]): Boolean =
44+
def checkFile(fileName: String, ext: String): Unit =
45+
val file = dotty.tools.io.Path(fileName)
46+
if file.extension != ext then
47+
throw new IllegalArgumentException(s"File extension is not `.$ext`: $file")
48+
else if !file.exists then
49+
throw new IllegalArgumentException(s"File not found: ${file.toAbsolute}")
50+
tastyFiles.foreach(checkFile(_, "tasty"))
51+
jars.foreach(checkFile(_, "jar"))
52+
val files = tastyFiles ::: jars
53+
files.nonEmpty && inspectFiles(dependenciesClasspath, files)
54+
55+
private def inspectFiles(classpath: List[String], classes: List[String]): Boolean =
3056
if (classes.isEmpty)
3157
throw new IllegalArgumentException("Parameter classes should no be empty")
3258

@@ -64,11 +90,12 @@ trait TastyInspector:
6490
end TastyInspectorPhase
6591

6692
val currentClasspath = ClasspathFromClassloader(getClass.getClassLoader)
67-
val args = "-from-tasty" :: "-Yretain-trees" :: "-classpath" :: s"$classpath$pathSeparator$currentClasspath" :: classes
93+
val fullClasspath = (classpath :+ currentClasspath).mkString(pathSeparator)
94+
val args = "-from-tasty" :: "-Yretain-trees" :: "-classpath" :: fullClasspath :: classes
6895
val reporter = (new InspectorDriver).process(args.toArray)
6996
reporter.hasErrors
7097

71-
end inspect
98+
end inspectFiles
7299

73100

74101
end TastyInspector

tests/neg-macros/delegate-match-1/Macro_1.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ inline def f: Any = ${ fImpl }
55

66
private def fImpl(using qctx: QuoteContext): Expr[Unit] = {
77
import qctx.reflect._
8-
Implicits.search(('[A]).unseal.tpe) match {
8+
Implicits.search((Type[A]).unseal.tpe) match {
99
case x: ImplicitSearchSuccess =>
1010
'{}
1111
case x: DivergingImplicit => '{}

tests/neg-macros/delegate-match-2/Macro_1.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ inline def f: Any = ${ fImpl }
55

66
private def fImpl (using qctx: QuoteContext) : Expr[Unit] = {
77
import qctx.reflect._
8-
Implicits.search(('[A]).unseal.tpe) match {
8+
Implicits.search((Type[A]).unseal.tpe) match {
99
case x: ImplicitSearchSuccess =>
1010
'{}
1111
case x: DivergingImplicit => '{}

tests/neg-macros/delegate-match-3/Macro_1.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ inline def f: Any = ${ fImpl }
55

66
private def fImpl(using qctx: QuoteContext) : Expr[Unit] = {
77
import qctx.reflect._
8-
Implicits.search(('[A]).unseal.tpe) match {
8+
Implicits.search((Type[A]).unseal.tpe) match {
99
case x: ImplicitSearchSuccess =>
1010
'{}
1111
case x: DivergingImplicit => '{}

tests/neg-macros/i4493-b.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
class Index[K]
22
object Index {
33
inline def succ[K](x: K): Unit = ${
4-
implicit val t: quoted.Type[K] = '[K] // error
4+
implicit val t: quoted.Type[K] = Type[K] // error
55
'{new Index[K]} // error
66
}
77
}

tests/neg-macros/i4493.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
class Index[K]
22
object Index {
33
inline def succ[K]: Unit = ${
4-
implicit val t: quoted.Type[K] = '[K] // error
4+
implicit val t: quoted.Type[K] = Type[K] // error
55
'{new Index[K]} // error
66
}
77
}

tests/neg-macros/i6530.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
object Macros {
2-
inline def q : Int = ${ '[ Int ] } // error
2+
inline def q : Int = ${ Type[ Int ] } // error
33
val x : Int = 1 + q
44
}

tests/neg-macros/i6530b.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ object Foo {
44
val tpe: quoted.Type[Int] = ???
55
val expr: quoted.Expr[Int] = ???
66

7-
val a: quoted.Expr[Int] = ${ '[Int] } // error
7+
val a: quoted.Expr[Int] = ${ Type[Int] } // error
88
val b: quoted.Expr[Int] = '{ $tpe } // error
99
val c: ${ '{ 43 } } = ??? // error
10-
val d: quoted.Type[Int] = '[ $expr ] // error
10+
val d: quoted.Type[Int] = Type[ $expr ] // error
1111
}
1212
}

tests/neg-macros/i7013.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@ import quoted._
22

33
def foo()(using QuoteContext) = {
44
class C
5-
'[C] // error
5+
Type[C] // error
66
}

tests/neg-macros/i7013b.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ import quoted._
33
class Foo {
44
class Bar
55
def foo()(using QuoteContext) = {
6-
'[Bar] // error
6+
Type[Bar] // error
77
}
88
}

tests/neg-macros/i7013c.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@ import quoted._
22

33
def foo()(using QuoteContext) = {
44
type C
5-
'[C] // error
5+
Type[C] // error
66
}

tests/neg-macros/i7121.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ class Test()(implicit qtx: QuoteContext) {
77
@annot1('{4}) // error
88
def foo(str: String) = ()
99

10-
@annot2(4)(using '[Int]) // error
10+
@annot2(4)(using Type[Int]) // error
1111
def foo2(str: String) = ()
1212

1313
}

tests/pos-macros/i4023c/Macro_1.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import scala.quoted._
22
object Macro {
33
inline def ff[T](x: T): T = ${ impl('x) }
4-
def impl[T](x: Expr[T])(implicit t: Type[T], qctx: QuoteContext): Expr[T] = '{ $x: $t }
4+
def impl[T](x: Expr[T])(implicit t: Type[T], qctx: QuoteContext): Expr[T] = '{ $x: T }
55
}

tests/pos-macros/i4539.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import scala.quoted._
22
def test(using QuoteContext) = {
3-
val q = '[String]
4-
'[String]
3+
val q = Type[String]
4+
Type[String]
55
}

0 commit comments

Comments
 (0)