From 718b2d5ae76e4ee60bc9f14f9c86638b9a053dbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 13 Nov 2023 10:30:05 +0100 Subject: [PATCH] Merge the functionality of Descriptors into JavaSignatures. With the exception of fields of a base type, descriptors are a strict subset of member signatures. We generalize JavaSignatures to be able to read such field descriptors, so that we can remove Descriptors in favor of JavaSignatures. We cannot do the same for *class* signatures, unfortunately, because monomorphic extends clauses are store in a completely different way that polymorphic ones. --- .../reader/classfiles/ClassfileParser.scala | 12 +- .../reader/classfiles/ClassfileReader.scala | 14 +- .../reader/classfiles/Descriptors.scala | 125 ------------------ .../reader/classfiles/JavaSignatures.scala | 21 ++- 4 files changed, 28 insertions(+), 144 deletions(-) delete mode 100644 tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/Descriptors.scala diff --git a/tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/ClassfileParser.scala b/tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/ClassfileParser.scala index 3a132098..a463413d 100644 --- a/tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/ClassfileParser.scala +++ b/tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/ClassfileParser.scala @@ -215,8 +215,8 @@ private[reader] object ClassfileParser { allRegisteredSymbols += sym sym - def loadMembers(): IArray[(TermSymbol, AccessFlags, SigOrDesc)] = - val buf = IArray.newBuilder[(TermSymbol, AccessFlags, SigOrDesc)] + def loadMembers(): IArray[(TermSymbol, AccessFlags, MemberSig)] = + val buf = IArray.newBuilder[(TermSymbol, AccessFlags, MemberSig)] structure.fields.use { reader.readFields { (name, sigOrDesc, access) => buf += ((createMember(name, EmptyFlagSet, access), access, sigOrDesc)) @@ -242,7 +242,7 @@ private[reader] object ClassfileParser { structure.supers.use { val superClass = reader.readSuperClass().map(binaryName) val interfaces = reader.readInterfaces().map(binaryName) - Descriptors.parseSupers(cls, superClass, interfaces) + JavaSignatures.parseSupers(cls, superClass, interfaces) } end parents val parents1 = @@ -262,10 +262,8 @@ private[reader] object ClassfileParser { else if cls.isString then rctx.createStringMagicMethods(cls) else if cls.isJavaEnum then rctx.createEnumMagicMethods(cls) - for (sym, javaFlags, sigOrDesc) <- loadMembers() do - val parsedType = sigOrDesc match - case SigOrDesc.Desc(desc) => Descriptors.parseDescriptor(sym, desc) - case SigOrDesc.Sig(sig) => JavaSignatures.parseSignature(sym, sig, allRegisteredSymbols) + for (sym, javaFlags, memberSig) <- loadMembers() do + val parsedType = JavaSignatures.parseSignature(sym, memberSig, allRegisteredSymbols) val adaptedType = if sym.isMethod && sym.name == nme.Constructor then cls.makePolyConstructorType(parsedType) else if sym.isMethod && javaFlags.isVarargsIfMethod then patchForVarargs(sym, parsedType) diff --git a/tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/ClassfileReader.scala b/tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/ClassfileReader.scala index 73af55fb..03aae987 100644 --- a/tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/ClassfileReader.scala +++ b/tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/ClassfileReader.scala @@ -187,15 +187,15 @@ private[classfiles] final class ClassfileReader private () { reader } - def readFields(op: (SimpleName, SigOrDesc, AccessFlags) => Unit)(using DataStream, ConstantPool): Unit = + def readFields(op: (SimpleName, MemberSig, AccessFlags) => Unit)(using DataStream, ConstantPool): Unit = readMembers(isMethod = false, op) - def readMethods(op: (SimpleName, SigOrDesc, AccessFlags) => Unit)(using DataStream, ConstantPool): Unit = + def readMethods(op: (SimpleName, MemberSig, AccessFlags) => Unit)(using DataStream, ConstantPool): Unit = readMembers(isMethod = true, op) private def readMembers( isMethod: Boolean, - op: (SimpleName, SigOrDesc, AccessFlags) => Unit + op: (SimpleName, MemberSig, AccessFlags) => Unit )(using ds: DataStream, pool: ConstantPool): Unit = { val count = data.readU2() loop(count) { @@ -214,9 +214,7 @@ private[classfiles] final class ClassfileReader private () { case _ => false } val sig = sigOrNull - if !accessFlags.isSynthetic then - if sig == null then op(name, SigOrDesc.Desc(desc), accessFlags) - else op(name, SigOrDesc.Sig(sig), accessFlags) + if !accessFlags.isSynthetic then op(name, if sig == null then desc else sig, accessFlags) } } @@ -399,9 +397,7 @@ private[classfiles] object ClassfileReader { IArray.unsafeFromArray(arr) } - enum SigOrDesc: - case Sig(str: String) - case Desc(str: String) + type MemberSig = String enum SigOrSupers: case Sig(str: String) diff --git a/tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/Descriptors.scala b/tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/Descriptors.scala deleted file mode 100644 index 1fdcaa23..00000000 --- a/tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/Descriptors.scala +++ /dev/null @@ -1,125 +0,0 @@ -package tastyquery.reader.classfiles - -import scala.annotation.switch - -import tastyquery.Contexts.* -import tastyquery.Exceptions.* -import tastyquery.Names.* -import tastyquery.Types.* -import tastyquery.Symbols.* -import tastyquery.Flags - -import tastyquery.reader.ReaderContext -import tastyquery.reader.ReaderContext.rctx - -import ClassfileParser.* - -private[classfiles] object Descriptors: - - def parseSupers(cls: ClassSymbol, superClass: Option[SimpleName], interfaces: IArray[SimpleName])( - using ReaderContext, - InnerClasses, - Resolver - ): List[Type] = - cls.withTypeParams(Nil) - if cls.isObject then rctx.AnyType :: rctx.MatchableType :: Nil - else - val superRef = superClass.map(classRef).getOrElse(rctx.ObjectType) - superRef :: interfaces.map(classRef).toList - - def classRef(binaryName: SimpleName)(using ReaderContext, InnerClasses, Resolver): TypeRef = - resolver.resolve(binaryName) - - @throws[ClassfileFormatException] - def parseDescriptor(member: Symbol, desc: String)(using ReaderContext, InnerClasses, Resolver): TypeOrMethodic = - // TODO: once we support inner classes, decide if we merge with parseSignature - var offset = 0 - var end = desc.length - val isMethod = member.isTerm && member.asTerm.isMethod - - def available = end - offset - - def peek = desc.charAt(offset) - - def consume(char: Char): Boolean = - if available >= 1 && peek == char then - offset += 1 - true - else false - - def charsUntil(char: Char): String = - val old = offset - while available > 0 && peek != char do offset += 1 - if available == 0 then abort - else - val result = desc.slice(old, offset) - offset += 1 // skip char - result - - def commitSimple[T](len: Int, t: T): T = - offset += len - t - - def baseType: Option[Type] = - if available >= 1 then - (peek: @switch) match - case 'B' => commitSimple(1, Some(rctx.ByteType)) - case 'C' => commitSimple(1, Some(rctx.CharType)) - case 'D' => commitSimple(1, Some(rctx.DoubleType)) - case 'F' => commitSimple(1, Some(rctx.FloatType)) - case 'I' => commitSimple(1, Some(rctx.IntType)) - case 'J' => commitSimple(1, Some(rctx.LongType)) - case 'S' => commitSimple(1, Some(rctx.ShortType)) - case 'Z' => commitSimple(1, Some(rctx.BooleanType)) - case _ => None - else None - - def objectType: Option[Type] = - if consume('L') then // has 'L', ';', and class name - val binaryName = termName(charsUntil(';')) // consume until ';', skip ';' - Some(classRef(binaryName)) - else None - - def arrayType: Option[Type] = - if consume('[') then - val tpe = fieldDescriptor - Some(rctx.ArrayTypeOf(tpe)) - else None - - def fieldDescriptor: Type = - baseType.orElse(objectType).orElse(arrayType).getOrElse(abort) - - def returnDescriptor: Type = - if consume('V') then rctx.UnitType - else fieldDescriptor - - def methodDescriptor: MethodType = - if consume('(') then // must have '(', ')', and return type - def paramDescriptors(acc: List[Type]): List[Type] = - if consume(')') then acc.reverse - else paramDescriptors(fieldDescriptor :: acc) - val params = paramDescriptors(Nil) - val ret = returnDescriptor - MethodType((0 until params.size).map(i => termName(s"x$$$i")).toList, params, ret) - else abort - - def unconsumed: Nothing = - throw ClassfileFormatException( - s"Expected end of descriptor but found $"${desc.slice(offset, end)}$", [is method? $isMethod]" - ) - - def abort: Nothing = - val msg = - if available == 0 then "Unexpected end of descriptor" - else s"Unexpected characted '$peek' in descriptor" - throw ClassfileFormatException(s"$msg of $member, original: `$desc` [is method? $isMethod]") - - val parsedDescriptor = - if isMethod then methodDescriptor - else fieldDescriptor - - if available > 0 then unconsumed - - parsedDescriptor - end parseDescriptor -end Descriptors diff --git a/tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/JavaSignatures.scala b/tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/JavaSignatures.scala index c4f3b7f4..5a4d8f2f 100644 --- a/tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/JavaSignatures.scala +++ b/tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/JavaSignatures.scala @@ -15,7 +15,7 @@ import tastyquery.Symbols.* import tastyquery.reader.ReaderContext import tastyquery.reader.ReaderContext.rctx -import ClassfileParser.{InnerClasses, Resolver} +import ClassfileParser.{InnerClasses, Resolver, resolver} private[classfiles] object JavaSignatures: @@ -23,6 +23,21 @@ private[classfiles] object JavaSignatures: private type JavaSignature = Null | PolyType | Map[TypeName, ClassTypeParamSymbol] | IgnoreTParamRefs.type + private def classRef(binaryName: SimpleName)(using ReaderContext, InnerClasses, Resolver): TypeRef = + resolver.resolve(binaryName) + + def parseSupers(cls: ClassSymbol, superClass: Option[SimpleName], interfaces: IArray[SimpleName])( + using ReaderContext, + InnerClasses, + Resolver + ): List[Type] = + cls.withTypeParams(Nil) + if cls.isObject then rctx.AnyType :: rctx.MatchableType :: Nil + else + val superRef = superClass.map(classRef).getOrElse(rctx.ObjectType) + superRef :: interfaces.map(classRef).toList + end parseSupers + @throws[ClassfileFormatException] def parseSignature(member: Symbol { val owner: Symbol }, signature: String, allRegisteredSymbols: Growable[Symbol])( using ReaderContext, @@ -163,7 +178,7 @@ private[classfiles] object JavaSignatures: end classTypeSignatureRest if consume('L') then // must have 'L', identifier, and ';'. - val pre = simpleClassTypeSignature(Descriptors.classRef(binaryName())) + val pre = simpleClassTypeSignature(classRef(binaryName())) Some(classTypeSignatureRest(pre)) else None end classTypeSignature @@ -279,7 +294,7 @@ private[classfiles] object JavaSignatures: classRest(null) def fieldSignature: Type = - referenceType(null) + baseType.getOrElse(referenceType(null)) def cookFailure(tname: TypeName, reason: String): Nothing = val path = if !isClass then s"${member.owner.displayFullName}#${member.name}" else member.displayFullName