diff options
author | Felix Mulder <felix.mulder@gmail.com> | 2016-11-02 11:08:28 +0100 |
---|---|---|
committer | Guillaume Martres <smarter@ubuntu.com> | 2016-11-22 01:35:07 +0100 |
commit | 8a61ff432543a29234193cd1f7c14abd3f3d31a0 (patch) | |
tree | a8147561d307af862c295cfc8100d271063bb0dd /src/dotty/tools/dotc/core/classfile/ClassfileParser.scala | |
parent | 6a455fe6da5ff9c741d91279a2dc6fe2fb1b472f (diff) | |
download | dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.gz dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.bz2 dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.zip |
Move compiler and compiler tests to compiler dir
Diffstat (limited to 'src/dotty/tools/dotc/core/classfile/ClassfileParser.scala')
-rw-r--r-- | src/dotty/tools/dotc/core/classfile/ClassfileParser.scala | 1100 |
1 files changed, 0 insertions, 1100 deletions
diff --git a/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala deleted file mode 100644 index 97a82e80d..000000000 --- a/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ /dev/null @@ -1,1100 +0,0 @@ -package dotty.tools -package dotc -package core -package classfile - -import Contexts._, Symbols._, Types._, Names._, StdNames._, NameOps._, Scopes._, Decorators._ -import SymDenotations._, unpickleScala2.Scala2Unpickler._, Constants._, Annotations._, util.Positions._ -import ast.tpd._ -import java.io.{ File, IOException } -import java.lang.Integer.toHexString -import scala.collection.{ mutable, immutable } -import scala.collection.mutable.{ ListBuffer, ArrayBuffer } -import scala.annotation.switch -import typer.Checking.checkNonCyclic -import io.AbstractFile -import scala.util.control.NonFatal - -object ClassfileParser { - /** Marker trait for unpicklers that can be embedded in classfiles. */ - trait Embedded -} - -class ClassfileParser( - classfile: AbstractFile, - classRoot: ClassDenotation, - moduleRoot: ClassDenotation)(ictx: Context) { - - import ClassfileConstants._ - import ClassfileParser._ - - protected val in = new AbstractFileReader(classfile) - - protected val staticModule: Symbol = moduleRoot.sourceModule(ictx) - - protected val instanceScope: MutableScope = newScope // the scope of all instance definitions - protected val staticScope: MutableScope = newScope // the scope of all static definitions - protected var pool: ConstantPool = _ // the classfile's constant pool - - protected var currentClassName: Name = _ // JVM name of the current class - protected var classTParams = Map[Name,Symbol]() - - classRoot.info = (new NoCompleter).withDecls(instanceScope) - moduleRoot.info = (new NoCompleter).withDecls(staticScope).withSourceModule(_ => staticModule) - - private def currentIsTopLevel(implicit ctx: Context) = classRoot.owner is Flags.PackageClass - - private def mismatchError(c: Symbol) = - throw new IOException(s"class file '${in.file}' has location not matching its contents: contains $c") - - def run()(implicit ctx: Context): Option[Embedded] = try { - ctx.debuglog("[class] >> " + classRoot.fullName) - parseHeader - this.pool = new ConstantPool - parseClass() - } catch { - case e: RuntimeException => - if (ctx.debug) e.printStackTrace() - throw new IOException( - i"""class file $classfile is broken, reading aborted with ${e.getClass} - |${Option(e.getMessage).getOrElse("")}""") - } - - private def parseHeader(): Unit = { - val magic = in.nextInt - if (magic != JAVA_MAGIC) - throw new IOException(s"class file '${in.file}' has wrong magic number 0x${toHexString(magic)}, should be 0x${toHexString(JAVA_MAGIC)}") - val minorVersion = in.nextChar.toInt - val majorVersion = in.nextChar.toInt - if ((majorVersion < JAVA_MAJOR_VERSION) || - ((majorVersion == JAVA_MAJOR_VERSION) && - (minorVersion < JAVA_MINOR_VERSION))) - throw new IOException( - s"class file '${in.file}' has unknown version $majorVersion.$minorVersion, should be at least $JAVA_MAJOR_VERSION.$JAVA_MINOR_VERSION") - } - - /** Return the class symbol of the given name. */ - def classNameToSymbol(name: Name)(implicit ctx: Context): Symbol = innerClasses.get(name) match { - case Some(entry) => innerClasses.classSymbol(entry.externalName) - case None => ctx.requiredClass(name) - } - - var sawPrivateConstructor = false - - def parseClass()(implicit ctx: Context): Option[Embedded] = { - val jflags = in.nextChar - val isAnnotation = hasAnnotation(jflags) - val sflags = classTranslation.flags(jflags) - val isEnum = (jflags & JAVA_ACC_ENUM) != 0 - val nameIdx = in.nextChar - currentClassName = pool.getClassName(nameIdx) - - if (currentIsTopLevel) { - val c = pool.getClassSymbol(nameIdx) - if (c != classRoot.symbol) mismatchError(c) - } - - addEnclosingTParams() - - /** Parse parents for Java classes. For Scala, return AnyRef, since the real type will be unpickled. - * Updates the read pointer of 'in'. */ - def parseParents: List[Type] = { - val superType = if (isAnnotation) { in.nextChar; defn.AnnotationType } - else pool.getSuperClass(in.nextChar).typeRef - val ifaceCount = in.nextChar - var ifaces = for (i <- (0 until ifaceCount).toList) yield pool.getSuperClass(in.nextChar).typeRef - // Dotty deviation: was - // var ifaces = for (i <- List.range(0 until ifaceCount)) ... - // This does not typecheck because the type parameter of List is now lower-bounded by Int | Char. - // Consequently, no best implicit for the "Integral" evidence parameter of "range" - // is found. If we treat constant subtyping specially, we might be able - // to do something there. But in any case, the until should be more efficient. - - if (isAnnotation) ifaces = defn.ClassfileAnnotationType :: ifaces - superType :: ifaces - } - - val result = unpickleOrParseInnerClasses() - if (!result.isDefined) { - var classInfo: Type = TempClassInfoType(parseParents, instanceScope, classRoot.symbol) - // might be reassigned by later parseAttributes - val staticInfo = TempClassInfoType(List(), staticScope, moduleRoot.symbol) - - enterOwnInnerClasses - - classRoot.setFlag(sflags) - moduleRoot.setFlag(Flags.JavaDefined | Flags.ModuleClassCreationFlags) - setPrivateWithin(classRoot, jflags) - setPrivateWithin(moduleRoot, jflags) - setPrivateWithin(moduleRoot.sourceModule, jflags) - - for (i <- 0 until in.nextChar) parseMember(method = false) - for (i <- 0 until in.nextChar) parseMember(method = true) - classInfo = parseAttributes(classRoot.symbol, classInfo) - if (isAnnotation) addAnnotationConstructor(classInfo) - - val companionClassMethod = ctx.synthesizeCompanionMethod(nme.COMPANION_CLASS_METHOD, classRoot, moduleRoot) - if (companionClassMethod.exists) companionClassMethod.entered - val companionModuleMethod = ctx.synthesizeCompanionMethod(nme.COMPANION_MODULE_METHOD, moduleRoot, classRoot) - if (companionModuleMethod.exists) companionModuleMethod.entered - - setClassInfo(classRoot, classInfo) - setClassInfo(moduleRoot, staticInfo) - } - - // eager load java enum definitions for exhaustivity check of pattern match - if (isEnum) { - instanceScope.toList.map(_.ensureCompleted()) - staticScope.toList.map(_.ensureCompleted()) - classRoot.setFlag(Flags.Enum) - moduleRoot.setFlag(Flags.Enum) - } - - result - } - - /** Add type parameters of enclosing classes */ - def addEnclosingTParams()(implicit ctx: Context): Unit = { - var sym = classRoot.owner - while (sym.isClass && !(sym is Flags.ModuleClass)) { - for (tparam <- sym.typeParams) { - classTParams = classTParams.updated(tparam.name.unexpandedName, tparam) - } - sym = sym.owner - } - } - - def parseMember(method: Boolean)(implicit ctx: Context): Unit = { - val start = indexCoord(in.bp) - val jflags = in.nextChar - val sflags = - if (method) Flags.Method | methodTranslation.flags(jflags) - else fieldTranslation.flags(jflags) - val name = pool.getName(in.nextChar) - if (!(sflags is Flags.Private) || name == nme.CONSTRUCTOR || ctx.settings.optimise.value) { - val member = ctx.newSymbol( - getOwner(jflags), name, sflags, memberCompleter, coord = start) - getScope(jflags).enter(member) - } - // skip rest of member for now - in.nextChar // info - skipAttributes - } - - val memberCompleter = new LazyType { - - def complete(denot: SymDenotation)(implicit ctx: Context): Unit = { - val oldbp = in.bp - try { - in.bp = denot.symbol.coord.toIndex - val sym = denot.symbol - val jflags = in.nextChar - val isEnum = (jflags & JAVA_ACC_ENUM) != 0 - val name = pool.getName(in.nextChar) - val isConstructor = name eq nme.CONSTRUCTOR - - /** Strip leading outer param from constructor. - * Todo: Also strip trailing access tag for private inner constructors? - */ - def stripOuterParamFromConstructor() = innerClasses.get(currentClassName) match { - case Some(entry) if !isStatic(entry.jflags) => - val mt @ MethodType(paramnames, paramtypes) = denot.info - denot.info = mt.derivedMethodType(paramnames.tail, paramtypes.tail, mt.resultType) - case _ => - } - - /** Make return type of constructor be the enclosing class type, - * and make constructor type polymorphic in the type parameters of the class - */ - def normalizeConstructorInfo() = { - val mt @ MethodType(paramnames, paramtypes) = denot.info - val rt = classRoot.typeRef appliedTo (classRoot.typeParams map (_.typeRef)) - denot.info = mt.derivedMethodType(paramnames, paramtypes, rt) - addConstructorTypeParams(denot) - } - - denot.info = pool.getType(in.nextChar) - if (isEnum) denot.info = ConstantType(Constant(sym)) - if (isConstructor) stripOuterParamFromConstructor() - setPrivateWithin(denot, jflags) - denot.info = translateTempPoly(parseAttributes(sym, denot.info)) - if (isConstructor) normalizeConstructorInfo() - - if ((denot is Flags.Method) && (jflags & JAVA_ACC_VARARGS) != 0) - denot.info = arrayToRepeated(denot.info) - - // seal java enums - if (isEnum) { - val enumClass = sym.owner.linkedClass - if (!(enumClass is Flags.Sealed)) enumClass.setFlag(Flags.AbstractSealed) - enumClass.addAnnotation(Annotation.makeChild(sym)) - } - } finally { - in.bp = oldbp - } - } - } - - /** Map direct references to Object to references to Any */ - final def objToAny(tp: Type)(implicit ctx: Context) = - if (tp.isDirectRef(defn.ObjectClass) && !ctx.phase.erasedTypes) defn.AnyType else tp - - private def sigToType(sig: TermName, owner: Symbol = null)(implicit ctx: Context): Type = { - var index = 0 - val end = sig.length - def accept(ch: Char): Unit = { - assert(sig(index) == ch, (sig(index), ch)) - index += 1 - } - def subName(isDelimiter: Char => Boolean): TermName = { - val start = index - while (!isDelimiter(sig(index))) { index += 1 } - sig.slice(start, index) - } - // Warning: sigToType contains nested completers which might be forced in a later run! - // So local methods need their own ctx parameters. - def sig2type(tparams: immutable.Map[Name,Symbol], skiptvs: Boolean)(implicit ctx: Context): Type = { - val tag = sig(index); index += 1 - (tag: @switch) match { - case BYTE_TAG => defn.ByteType - case CHAR_TAG => defn.CharType - case DOUBLE_TAG => defn.DoubleType - case FLOAT_TAG => defn.FloatType - case INT_TAG => defn.IntType - case LONG_TAG => defn.LongType - case SHORT_TAG => defn.ShortType - case VOID_TAG => defn.UnitType - case BOOL_TAG => defn.BooleanType - case 'L' => - def processInner(tp: Type): Type = tp match { - case tp: TypeRef if !(tp.symbol.owner is Flags.ModuleClass) => - TypeRef(processInner(tp.prefix.widen), tp.name) - case _ => - tp - } - def processClassType(tp: Type): Type = tp match { - case tp: TypeRef => - if (sig(index) == '<') { - accept('<') - var tp1: Type = tp - var formals = tp.typeParamSymbols - while (sig(index) != '>') { - sig(index) match { - case variance @ ('+' | '-' | '*') => - index += 1 - val bounds = variance match { - case '+' => objToAny(TypeBounds.upper(sig2type(tparams, skiptvs))) - case '-' => - val tp = sig2type(tparams, skiptvs) - // sig2type seems to return AnyClass regardless of the situation: - // we don't want Any as a LOWER bound. - if (tp.isDirectRef(defn.AnyClass)) TypeBounds.empty - else TypeBounds.lower(tp) - case '*' => TypeBounds.empty - } - tp1 = RefinedType(tp1, formals.head.name, bounds) - case _ => - tp1 = RefinedType(tp1, formals.head.name, TypeAlias(sig2type(tparams, skiptvs))) - } - formals = formals.tail - } - accept('>') - tp1 - } else tp - case tp => - assert(sig(index) != '<', tp) - tp - } - - val classSym = classNameToSymbol(subName(c => c == ';' || c == '<')) - var tpe = processClassType(processInner(classSym.typeRef)) - while (sig(index) == '.') { - accept('.') - val name = subName(c => c == ';' || c == '<' || c == '.').toTypeName - val clazz = tpe.member(name).symbol - tpe = processClassType(processInner(clazz.typeRef)) - } - accept(';') - tpe - case ARRAY_TAG => - while ('0' <= sig(index) && sig(index) <= '9') index += 1 - var elemtp = sig2type(tparams, skiptvs) - // make unbounded Array[T] where T is a type variable into Ar ray[T with Object] - // (this is necessary because such arrays have a representation which is incompatible - // with arrays of primitive types. - // NOTE that the comparison to Object only works for abstract types bounded by classes that are strict subclasses of Object - // if the bound is exactly Object, it will have been converted to Any, and the comparison will fail - // see also RestrictJavaArraysMap (when compiling java sources directly) - if (elemtp.typeSymbol.isAbstractType && !(elemtp.derivesFrom(defn.ObjectClass))) { - elemtp = AndType(elemtp, defn.ObjectType) - } - defn.ArrayOf(elemtp) - case '(' => - // we need a method symbol. given in line 486 by calling getType(methodSym, ..) - val paramtypes = new ListBuffer[Type]() - var paramnames = new ListBuffer[TermName]() - while (sig(index) != ')') { - paramnames += nme.syntheticParamName(paramtypes.length) - paramtypes += objToAny(sig2type(tparams, skiptvs)) - } - index += 1 - val restype = sig2type(tparams, skiptvs) - JavaMethodType(paramnames.toList, paramtypes.toList)(_ => restype) - case 'T' => - val n = subName(';'.==).toTypeName - index += 1 - //assert(tparams contains n, s"classTparams = $classTParams, tparams = $tparams, key = $n") - if (skiptvs) defn.AnyType else tparams(n).typeRef - } - } // sig2type(tparams, skiptvs) - - def sig2typeBounds(tparams: immutable.Map[Name, Symbol], skiptvs: Boolean)(implicit ctx: Context): Type = { - val ts = new ListBuffer[Type] - while (sig(index) == ':') { - index += 1 - if (sig(index) != ':') // guard against empty class bound - ts += objToAny(sig2type(tparams, skiptvs)) - } - TypeBounds.upper(((NoType: Type) /: ts)(_ & _) orElse defn.AnyType) - } - - var tparams = classTParams - - def typeParamCompleter(start: Int) = new LazyType { - def complete(denot: SymDenotation)(implicit ctx: Context): Unit = { - val savedIndex = index - try { - index = start - denot.info = - checkNonCyclic( // we need the checkNonCyclic call to insert LazyRefs for F-bounded cycles - denot.symbol, - sig2typeBounds(tparams, skiptvs = false), - reportErrors = false) - } finally { - index = savedIndex - } - } - } - - val newTParams = new ListBuffer[Symbol]() - if (sig(index) == '<') { - assert(owner != null) - index += 1 - val start = index - while (sig(index) != '>') { - val tpname = subName(':'.==).toTypeName - val expname = if (owner.isClass) tpname.expandedName(owner) else tpname - val s = ctx.newSymbol( - owner, expname, owner.typeParamCreationFlags, - typeParamCompleter(index), coord = indexCoord(index)) - if (owner.isClass) owner.asClass.enter(s) - tparams = tparams + (tpname -> s) - sig2typeBounds(tparams, skiptvs = true) - newTParams += s - } - index += 1 - } - val ownTypeParams = newTParams.toList.asInstanceOf[List[TypeSymbol]] - val tpe = - if ((owner == null) || !owner.isClass) - sig2type(tparams, skiptvs = false) - else { - classTParams = tparams - val parents = new ListBuffer[Type]() - while (index < end) { - parents += sig2type(tparams, skiptvs = false) // here the variance doesnt'matter - } - TempClassInfoType(parents.toList, instanceScope, owner) - } - if (ownTypeParams.isEmpty) tpe else TempPolyType(ownTypeParams, tpe) - } // sigToType - - def parseAnnotArg(skip: Boolean = false)(implicit ctx: Context): Option[Tree] = { - val tag = in.nextByte.toChar - val index = in.nextChar - tag match { - case STRING_TAG => - if (skip) None else Some(Literal(Constant(pool.getName(index).toString))) - case BOOL_TAG | BYTE_TAG | CHAR_TAG | SHORT_TAG | INT_TAG | - LONG_TAG | FLOAT_TAG | DOUBLE_TAG => - if (skip) None else Some(Literal(pool.getConstant(index))) - case CLASS_TAG => - if (skip) None else Some(Literal(Constant(pool.getType(index)))) - case ENUM_TAG => - val t = pool.getType(index) - val n = pool.getName(in.nextChar) - val module = t.typeSymbol.companionModule - val s = module.info.decls.lookup(n) - if (skip) { - None - } else if (s != NoSymbol) { - Some(Literal(Constant(s))) - } else { - ctx.warning(s"""While parsing annotations in ${in.file}, could not find $n in enum $module.\nThis is likely due to an implementation restriction: an annotation argument cannot refer to a member of the annotated class (SI-7014).""") - None - } - case ARRAY_TAG => - val arr = new ArrayBuffer[Tree]() - var hasError = false - for (i <- 0 until index) - parseAnnotArg(skip) match { - case Some(c) => arr += c - case None => hasError = true - } - if (hasError) None - else if (skip) None - else { - val elems = arr.toList - val elemType = - if (elems.isEmpty) defn.ObjectType - else ctx.typeComparer.lub(elems.tpes).widen - Some(JavaSeqLiteral(elems, TypeTree(elemType))) - } - case ANNOTATION_TAG => - parseAnnotation(index, skip) map (_.tree) - } - } - - /** Parse and return a single annotation. If it is malformed, - * return None. - */ - def parseAnnotation(attrNameIndex: Char, skip: Boolean = false)(implicit ctx: Context): Option[Annotation] = try { - val attrType = pool.getType(attrNameIndex) - val nargs = in.nextChar - val argbuf = new ListBuffer[Tree] - var hasError = false - for (i <- 0 until nargs) { - val name = pool.getName(in.nextChar) - parseAnnotArg(skip) match { - case Some(arg) => argbuf += NamedArg(name, arg) - case None => hasError = !skip - } - } - if (hasError || skip) None - else Some(Annotation.deferredResolve(attrType, argbuf.toList)) - } catch { - case f: FatalError => throw f // don't eat fatal errors, they mean a class was not found - case NonFatal(ex) => - // We want to be robust when annotations are unavailable, so the very least - // we can do is warn the user about the exception - // There was a reference to ticket 1135, but that is outdated: a reference to a class not on - // the classpath would *not* end up here. A class not found is signaled - // with a `FatalError` exception, handled above. Here you'd end up after a NPE (for example), - // and that should never be swallowed silently. - ctx.warning("Caught: " + ex + " while parsing annotations in " + in.file) - if (ctx.debug) ex.printStackTrace() - - None // ignore malformed annotations - } - - def parseAttributes(sym: Symbol, symtype: Type)(implicit ctx: Context): Type = { - def convertTo(c: Constant, pt: Type): Constant = { - if (pt == defn.BooleanType && c.tag == IntTag) - Constant(c.value != 0) - else - c convertTo pt - } - var newType = symtype - - def parseAttribute(): Unit = { - val attrName = pool.getName(in.nextChar).toTypeName - val attrLen = in.nextInt - val end = in.bp + attrLen - attrName match { - case tpnme.SignatureATTR => - val sig = pool.getExternalName(in.nextChar) - newType = sigToType(sig, sym) - if (ctx.debug && ctx.verbose) - println("" + sym + "; signature = " + sig + " type = " + newType) - case tpnme.SyntheticATTR => - sym.setFlag(Flags.SyntheticArtifact) - case tpnme.BridgeATTR => - sym.setFlag(Flags.Bridge) - case tpnme.DeprecatedATTR => - val msg = Literal(Constant("see corresponding Javadoc for more information.")) - val since = Literal(Constant("")) - sym.addAnnotation(Annotation(defn.DeprecatedAnnot, msg, since)) - case tpnme.ConstantValueATTR => - val c = pool.getConstant(in.nextChar) - val c1 = convertTo(c, symtype) - if (c1 ne null) newType = ConstantType(c1) - else println("failure to convert " + c + " to " + symtype); //debug - case tpnme.AnnotationDefaultATTR => - sym.addAnnotation(Annotation(defn.AnnotationDefaultAnnot, Nil)) - // Java annotations on classes / methods / fields with RetentionPolicy.RUNTIME - case tpnme.RuntimeAnnotationATTR => - parseAnnotations(attrLen) - - // TODO 1: parse runtime visible annotations on parameters - // case tpnme.RuntimeParamAnnotationATTR - - // TODO 2: also parse RuntimeInvisibleAnnotation / RuntimeInvisibleParamAnnotation, - // i.e. java annotations with RetentionPolicy.CLASS? - - case tpnme.ExceptionsATTR => - parseExceptions(attrLen) - - case tpnme.CodeATTR => - if (sym.owner is Flags.JavaTrait) { - sym.resetFlag(Flags.Deferred) - sym.owner.resetFlag(Flags.PureInterface) - ctx.log(s"$sym in ${sym.owner} is a java8+ default method.") - } - in.skip(attrLen) - - case _ => - } - in.bp = end - } - - /** - * Parse the "Exceptions" attribute which denotes the exceptions - * thrown by a method. - */ - def parseExceptions(len: Int): Unit = { - val nClasses = in.nextChar - for (n <- 0 until nClasses) { - // FIXME: this performs an equivalent of getExceptionTypes instead of getGenericExceptionTypes (SI-7065) - val cls = pool.getClassSymbol(in.nextChar.toInt) - sym.addAnnotation(ThrowsAnnotation(cls.asClass)) - } - } - - /** Parse a sequence of annotations and attaches them to the - * current symbol sym, except for the ScalaSignature annotation that it returns, if it is available. */ - def parseAnnotations(len: Int): Unit = { - val nAttr = in.nextChar - for (n <- 0 until nAttr) - parseAnnotation(in.nextChar) match { - case Some(annot) => - sym.addAnnotation(annot) - case None => - } - } - - // begin parseAttributes - for (i <- 0 until in.nextChar) { - parseAttribute() - } - newType - } - - /** Add synthetic constructor(s) and potentially also default getters which - * reflects the fields of the annotation with given `classInfo`. - * Annotations in Scala are assumed to get all their arguments as constructor - * parameters. For Java annotations we need to fake it by making up the constructor. - * Note that default getters have type Nothing. That's OK because we need - * them only to signal that the corresponding parameter is optional. - */ - def addAnnotationConstructor(classInfo: Type, tparams: List[TypeSymbol] = Nil)(implicit ctx: Context): Unit = { - def addDefaultGetter(attr: Symbol, n: Int) = - ctx.newSymbol( - owner = moduleRoot.symbol, - name = nme.CONSTRUCTOR.defaultGetterName(n), - flags = attr.flags & Flags.AccessFlags, - info = defn.NothingType).entered - - classInfo match { - case classInfo @ TempPolyType(tparams, restpe) if tparams.isEmpty => - addAnnotationConstructor(restpe, tparams) - case classInfo: TempClassInfoType => - val attrs = classInfo.decls.toList.filter(_.isTerm) - val targs = tparams.map(_.typeRef) - val paramNames = attrs.map(_.name.asTermName) - val paramTypes = attrs.map(_.info.resultType) - - def addConstr(ptypes: List[Type]) = { - val mtype = MethodType(paramNames, ptypes, classRoot.typeRef.appliedTo(targs)) - val constrType = if (tparams.isEmpty) mtype else TempPolyType(tparams, mtype) - val constr = ctx.newSymbol( - owner = classRoot.symbol, - name = nme.CONSTRUCTOR, - flags = Flags.Synthetic, - info = constrType - ).entered - for ((attr, i) <- attrs.zipWithIndex) - if (attr.hasAnnotation(defn.AnnotationDefaultAnnot)) { - constr.setFlag(Flags.HasDefaultParams) - addDefaultGetter(attr, i) - } - } - - addConstr(paramTypes) - - // The code below added an extra constructor to annotations where the - // last parameter of the constructor is an Array[X] for some X, the - // array was replaced by a vararg argument. Unfortunately this breaks - // inference when doing: - // @Annot(Array()) - // The constructor is overloaded so the expected type of `Array()` is - // WildcardType, and the type parameter of the Array apply method gets - // instantiated to `Nothing` instead of `X`. - // I'm leaving this commented out in case we improve inference to make this work. - // Note that if this is reenabled then JavaParser will also need to be modified - // to add the extra constructor (this was not implemented before). - /* - if (paramTypes.nonEmpty) - paramTypes.last match { - case defn.ArrayOf(elemtp) => - addConstr(paramTypes.init :+ defn.RepeatedParamType.appliedTo(elemtp)) - case _ => - } - */ - } - } - - /** Enter own inner classes in the right scope. It needs the scopes to be set up, - * and implicitly current class' superclasses. - */ - private def enterOwnInnerClasses()(implicit ctx: Context): Unit = { - def className(name: Name): Name = name.drop(name.lastIndexOf('.') + 1) - - def enterClassAndModule(entry: InnerClassEntry, file: AbstractFile, jflags: Int) = { - ctx.base.loaders.enterClassAndModule( - getOwner(jflags), - entry.originalName, - new ClassfileLoader(file), - classTranslation.flags(jflags), - getScope(jflags)) - } - - for (entry <- innerClasses.values) { - // create a new class member for immediate inner classes - if (entry.outerName == currentClassName) { - val file = ctx.platform.classPath.findSourceFile(entry.externalName.toString) getOrElse { - throw new AssertionError(entry.externalName) - } - enterClassAndModule(entry, file, entry.jflags) - } - } - } - - /** Parse inner classes. Expects `in.bp` to point to the superclass entry. - * Restores the old `bp`. - * @return true iff classfile is from Scala, so no Java info needs to be read. - */ - def unpickleOrParseInnerClasses()(implicit ctx: Context): Option[Embedded] = { - val oldbp = in.bp - try { - skipSuperclasses() - skipMembers() // fields - skipMembers() // methods - val attrs = in.nextChar - val attrbp = in.bp - - def scan(target: TypeName): Boolean = { - in.bp = attrbp - var i = 0 - while (i < attrs && pool.getName(in.nextChar).toTypeName != target) { - val attrLen = in.nextInt - in.skip(attrLen) - i += 1 - } - i < attrs - } - - def unpickleScala(bytes: Array[Byte]): Some[Embedded] = { - val unpickler = new unpickleScala2.Scala2Unpickler(bytes, classRoot, moduleRoot)(ctx) - unpickler.run()(ctx.addMode(Mode.Scala2Unpickling)) - Some(unpickler) - } - - def unpickleTASTY(bytes: Array[Byte]): Some[Embedded] = { - val unpickler = new tasty.DottyUnpickler(bytes) - unpickler.enter(roots = Set(classRoot, moduleRoot, moduleRoot.sourceModule)) - Some(unpickler) - } - - def parseScalaSigBytes: Array[Byte] = { - val tag = in.nextByte.toChar - assert(tag == STRING_TAG, tag) - pool getBytes in.nextChar - } - - def parseScalaLongSigBytes: Array[Byte] = { - val tag = in.nextByte.toChar - assert(tag == ARRAY_TAG, tag) - val stringCount = in.nextChar - val entries = - for (i <- 0 until stringCount) yield { - val stag = in.nextByte.toChar - assert(stag == STRING_TAG, stag) - in.nextChar.toInt - } - pool.getBytes(entries.toList) - } - - if (scan(tpnme.TASTYATTR)) { - val attrLen = in.nextInt - return unpickleTASTY(in.nextBytes(attrLen)) - } - - if (scan(tpnme.RuntimeAnnotationATTR)) { - val attrLen = in.nextInt - val nAnnots = in.nextChar - var i = 0 - while (i < nAnnots) { - val attrClass = pool.getType(in.nextChar).typeSymbol - val nArgs = in.nextChar - var j = 0 - while (j < nArgs) { - val argName = pool.getName(in.nextChar) - if (argName == nme.bytes) - if (attrClass == defn.ScalaSignatureAnnot) - return unpickleScala(parseScalaSigBytes) - else if (attrClass == defn.ScalaLongSignatureAnnot) - return unpickleScala(parseScalaLongSigBytes) - else if (attrClass == defn.TASTYSignatureAnnot) - return unpickleTASTY(parseScalaSigBytes) - else if (attrClass == defn.TASTYLongSignatureAnnot) - return unpickleTASTY(parseScalaLongSigBytes) - parseAnnotArg(skip = true) - j += 1 - } - i += 1 - } - } - - if (scan(tpnme.InnerClassesATTR)) { - val attrLen = in.nextInt - val entries = in.nextChar.toInt - for (i <- 0 until entries) { - val innerIndex = in.nextChar - val outerIndex = in.nextChar - val nameIndex = in.nextChar - val jflags = in.nextChar - if (innerIndex != 0 && outerIndex != 0 && nameIndex != 0) { - val entry = InnerClassEntry(innerIndex, outerIndex, nameIndex, jflags) - innerClasses(pool.getClassName(innerIndex)) = entry - } - } - } - None - } finally in.bp = oldbp - } - - /** An entry in the InnerClasses attribute of this class file. */ - case class InnerClassEntry(external: Int, outer: Int, name: Int, jflags: Int) { - def externalName = pool.getClassName(external) - def outerName = pool.getClassName(outer) - def originalName = pool.getName(name) - - override def toString = - originalName + " in " + outerName + "(" + externalName + ")" - } - - object innerClasses extends scala.collection.mutable.HashMap[Name, InnerClassEntry] { - /** Return the Symbol of the top level class enclosing `name`, - * or 'name's symbol if no entry found for `name`. - */ - def topLevelClass(name: Name)(implicit ctx: Context): Symbol = { - val tlName = if (isDefinedAt(name)) { - var entry = this(name) - while (isDefinedAt(entry.outerName)) - entry = this(entry.outerName) - entry.outerName - } else - name - classNameToSymbol(tlName) - } - - /** Return the class symbol for `externalName`. It looks it up in its outer class. - * Forces all outer class symbols to be completed. - * - * If the given name is not an inner class, it returns the symbol found in `defn`. - */ - def classSymbol(externalName: Name)(implicit ctx: Context): Symbol = { - /** Return the symbol of `innerName`, having the given `externalName`. */ - def innerSymbol(externalName: Name, innerName: Name, static: Boolean): Symbol = { - def getMember(sym: Symbol, name: Name): Symbol = - if (static) - if (sym == classRoot.symbol) staticScope.lookup(name) - else sym.companionModule.info.member(name).symbol - else - if (sym == classRoot.symbol) instanceScope.lookup(name) - else sym.info.member(name).symbol - - innerClasses.get(externalName) match { - case Some(entry) => - val outerName = entry.outerName.stripModuleClassSuffix - val owner = classSymbol(outerName) - val result = ctx.atPhaseNotLaterThanTyper { implicit ctx => - getMember(owner, innerName.toTypeName) - } - assert(result ne NoSymbol, - i"""failure to resolve inner class: - |externalName = $externalName, - |outerName = $outerName, - |innerName = $innerName - |owner.fullName = ${owner.showFullName} - |while parsing ${classfile}""") - result - - case None => - classNameToSymbol(externalName) - } - } - - get(externalName) match { - case Some(entry) => - innerSymbol(entry.externalName, entry.originalName, isStatic(entry.jflags)) - case None => - classNameToSymbol(externalName) - } - } - } - - def skipAttributes(): Unit = { - val attrCount = in.nextChar - for (i <- 0 until attrCount) { - in.skip(2); in.skip(in.nextInt) - } - } - - def skipMembers(): Unit = { - val memberCount = in.nextChar - for (i <- 0 until memberCount) { - in.skip(6); skipAttributes() - } - } - - def skipSuperclasses(): Unit = { - in.skip(2) // superclass - val ifaces = in.nextChar - in.skip(2 * ifaces) - } - - protected def getOwner(flags: Int): Symbol = - if (isStatic(flags)) moduleRoot.symbol else classRoot.symbol - - protected def getScope(flags: Int): MutableScope = - if (isStatic(flags)) staticScope else instanceScope - - private def setPrivateWithin(denot: SymDenotation, jflags: Int)(implicit ctx: Context): Unit = { - if ((jflags & (JAVA_ACC_PRIVATE | JAVA_ACC_PUBLIC)) == 0) - denot.privateWithin = denot.enclosingPackageClass - } - - private def isPrivate(flags: Int) = (flags & JAVA_ACC_PRIVATE) != 0 - private def isStatic(flags: Int) = (flags & JAVA_ACC_STATIC) != 0 - private def hasAnnotation(flags: Int) = (flags & JAVA_ACC_ANNOTATION) != 0 - - class ConstantPool { - private val len = in.nextChar - private val starts = new Array[Int](len) - private val values = new Array[AnyRef](len) - private val internalized = new Array[TermName](len) - - { var i = 1 - while (i < starts.length) { - starts(i) = in.bp - i += 1 - (in.nextByte.toInt: @switch) match { - case CONSTANT_UTF8 | CONSTANT_UNICODE => - in.skip(in.nextChar) - case CONSTANT_CLASS | CONSTANT_STRING | CONSTANT_METHODTYPE => - in.skip(2) - case CONSTANT_METHODHANDLE => - in.skip(3) - case CONSTANT_FIELDREF | CONSTANT_METHODREF | CONSTANT_INTFMETHODREF - | CONSTANT_NAMEANDTYPE | CONSTANT_INTEGER | CONSTANT_FLOAT - | CONSTANT_INVOKEDYNAMIC => - in.skip(4) - case CONSTANT_LONG | CONSTANT_DOUBLE => - in.skip(8) - i += 1 - case _ => - errorBadTag(in.bp - 1) - } - } - } - - /** Return the name found at given index. */ - def getName(index: Int): TermName = { - if (index <= 0 || len <= index) - errorBadIndex(index) - - values(index) match { - case name: TermName => name - case null => - val start = starts(index) - if (in.buf(start).toInt != CONSTANT_UTF8) errorBadTag(start) - val name = termName(in.buf, start + 3, in.getChar(start + 1)) - values(index) = name - name - } - } - - /** Return the name found at given index in the constant pool, with '/' replaced by '.'. */ - def getExternalName(index: Int): TermName = { - if (index <= 0 || len <= index) - errorBadIndex(index) - - if (internalized(index) == null) - internalized(index) = getName(index).replace('/', '.') - - internalized(index) - } - - def getClassSymbol(index: Int)(implicit ctx: Context): Symbol = { - if (index <= 0 || len <= index) errorBadIndex(index) - var c = values(index).asInstanceOf[Symbol] - if (c eq null) { - val start = starts(index) - if (in.buf(start).toInt != CONSTANT_CLASS) errorBadTag(start) - val name = getExternalName(in.getChar(start + 1)) - if (name.isModuleClassName && (name ne nme.nothingRuntimeClass) && (name ne nme.nullRuntimeClass)) - // Null$ and Nothing$ ARE classes - c = ctx.requiredModule(name.sourceModuleName) - else c = classNameToSymbol(name) - values(index) = c - } - c - } - - /** Return the external name of the class info structure found at 'index'. - * Use 'getClassSymbol' if the class is sure to be a top-level class. - */ - def getClassName(index: Int): TermName = { - val start = starts(index) - if (in.buf(start).toInt != CONSTANT_CLASS) errorBadTag(start) - getExternalName(in.getChar(start + 1)) - } - - /** Return a name and a type at the given index. - */ - private def getNameAndType(index: Int, ownerTpe: Type)(implicit ctx: Context): (Name, Type) = { - if (index <= 0 || len <= index) errorBadIndex(index) - var p = values(index).asInstanceOf[(Name, Type)] - if (p eq null) { - val start = starts(index) - if (in.buf(start).toInt != CONSTANT_NAMEANDTYPE) errorBadTag(start) - val name = getName(in.getChar(start + 1).toInt) - var tpe = getType(in.getChar(start + 3).toInt) - // fix the return type, which is blindly set to the class currently parsed - if (name == nme.CONSTRUCTOR) - tpe match { - case tp: MethodType => - tp.derivedMethodType(tp.paramNames, tp.paramTypes, ownerTpe) - } - p = (name, tpe) - values(index) = p - } - p - } - - /** Return the type of a class constant entry. Since - * arrays are considered to be class types, they might - * appear as entries in 'newarray' or 'cast' opcodes. - */ - def getClassOrArrayType(index: Int)(implicit ctx: Context): Type = { - if (index <= 0 || len <= index) errorBadIndex(index) - val value = values(index) - var c: Type = null - if (value eq null) { - val start = starts(index) - if (in.buf(start).toInt != CONSTANT_CLASS) errorBadTag(start) - val name = getExternalName(in.getChar(start + 1)) - if (name(0) == ARRAY_TAG) { - c = sigToType(name) - values(index) = c - } else { - val sym = classNameToSymbol(name) - values(index) = sym - c = sym.typeRef - } - } else c = value match { - case tp: Type => tp - case cls: Symbol => cls.typeRef - } - c - } - - def getType(index: Int)(implicit ctx: Context): Type = - sigToType(getExternalName(index)) - - def getSuperClass(index: Int)(implicit ctx: Context): Symbol = { - assert(index != 0, "attempt to parse java.lang.Object from classfile") - getClassSymbol(index) - } - - def getConstant(index: Int)(implicit ctx: Context): Constant = { - if (index <= 0 || len <= index) errorBadIndex(index) - var value = values(index) - if (value eq null) { - val start = starts(index) - value = (in.buf(start).toInt: @switch) match { - case CONSTANT_STRING => - Constant(getName(in.getChar(start + 1).toInt).toString) - case CONSTANT_INTEGER => - Constant(in.getInt(start + 1)) - case CONSTANT_FLOAT => - Constant(in.getFloat(start + 1)) - case CONSTANT_LONG => - Constant(in.getLong(start + 1)) - case CONSTANT_DOUBLE => - Constant(in.getDouble(start + 1)) - case CONSTANT_CLASS => - getClassOrArrayType(index).typeSymbol - case _ => - errorBadTag(start) - } - values(index) = value - } - value match { - case ct: Constant => ct - case cls: Symbol => Constant(cls.typeRef) - case arr: Type => Constant(arr) - } - } - - private def getSubArray(bytes: Array[Byte]): Array[Byte] = { - val decodedLength = ByteCodecs.decode(bytes) - val arr = new Array[Byte](decodedLength) - System.arraycopy(bytes, 0, arr, 0, decodedLength) - arr - } - - def getBytes(index: Int): Array[Byte] = { - if (index <= 0 || len <= index) errorBadIndex(index) - var value = values(index).asInstanceOf[Array[Byte]] - if (value eq null) { - val start = starts(index) - if (in.buf(start).toInt != CONSTANT_UTF8) errorBadTag(start) - val len = in.getChar(start + 1) - val bytes = new Array[Byte](len) - System.arraycopy(in.buf, start + 3, bytes, 0, len) - value = getSubArray(bytes) - values(index) = value - } - value - } - - def getBytes(indices: List[Int]): Array[Byte] = { - assert(!indices.isEmpty, indices) - var value = values(indices.head).asInstanceOf[Array[Byte]] - if (value eq null) { - val bytesBuffer = ArrayBuffer.empty[Byte] - for (index <- indices) { - if (index <= 0 || ConstantPool.this.len <= index) errorBadIndex(index) - val start = starts(index) - if (in.buf(start).toInt != CONSTANT_UTF8) errorBadTag(start) - val len = in.getChar(start + 1) - bytesBuffer ++= in.buf.view(start + 3, start + 3 + len) - } - value = getSubArray(bytesBuffer.toArray) - values(indices.head) = value - } - value - } - - /** Throws an exception signaling a bad constant index. */ - private def errorBadIndex(index: Int) = - throw new RuntimeException("bad constant pool index: " + index + " at pos: " + in.bp) - - /** Throws an exception signaling a bad tag at given address. */ - private def errorBadTag(start: Int) = - throw new RuntimeException("bad constant pool tag " + in.buf(start) + " at byte " + start) - } -} - |