<commit_before>/*
 * Scala (https://www.scala-lang.org)
 *
 * Copyright EPFL and Lightbend, Inc.
 *
 * Licensed under Apache License 2.0
 * (http://www.apache.org/licenses/LICENSE-2.0).
 *
 * See the NOTICE file distributed with this work for
 * additional information regarding copyright ownership.
 */

package scala
package tools.nsc
package symtab
package classfile

import java.io.{ByteArrayInputStream, DataInputStream, File, IOException}
import java.lang.Integer.toHexString
import java.nio.ByteBuffer

import scala.collection.{immutable, mutable}
import scala.collection.mutable.{ArrayBuffer, ListBuffer}
import scala.annotation.switch
import scala.reflect.internal.JavaAccFlags
import scala.reflect.internal.pickling.ByteCodecs
import scala.reflect.internal.util.ReusableInstance
import scala.reflect.io.{NoAbstractFile, VirtualFile}
import scala.tools.nsc.util.ClassPath
import scala.tools.nsc.io.AbstractFile
import scala.util.control.NonFatal

/** This abstract class implements a class file parser.
 *
 *  @author <NAME>
 *  @version 1.0
 */
abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) {
  val symbolTable: SymbolTable {
    def settings: Settings
  }
  val loaders: SymbolLoaders {
    val symbolTable: ClassfileParser.this.symbolTable.type
  }

  import symbolTable._
  /**
   * If typer phase is defined then perform member lookup of a symbol
   * `sym` at typer phase. This method results from refactoring. The
   * original author of the logic that uses typer phase didn't explain
   * why we need to force infos at that phase specifically. It only mentioned
   * that ClassfileParse can be called late (e.g. at flatten phase) and
   * we make to make sure we handle such situation properly.
   */
  protected def lookupMemberAtTyperPhaseIfPossible(sym: Symbol, name: Name): Symbol

  /** The compiler classpath. */
  def classPath: ClassPath

  import definitions._
  import scala.reflect.internal.ClassfileConstants._
  import Flags._

  protected var file: AbstractFile = _         // the class file
  protected var in: DataReader = _             // the class file reader
  protected var clazz: ClassSymbol = _         // the class symbol containing dynamic members
  protected var staticModule: ModuleSymbol = _ // the module symbol containing static members
  protected var instanceScope: Scope = _       // the scope of all instance definitions
  protected var staticScope: Scope = _         // the scope of all static definitions
  protected var pool: ConstantPool = _         // the classfile's constant pool
  protected var isScala: Boolean = _           // does class file describe a scala class?
  protected var isScalaRaw: Boolean = _        // this class file is a scala class with no pickled info
  protected var busy: Symbol = _               // lock to detect recursive reads
  protected var currentClass: String = _       // JVM name of the current class
  protected var classTParams = Map[Name,Symbol]()
  protected var srcfile0 : Option[AbstractFile] = None
  protected def moduleClass: Symbol = staticModule.moduleClass
  private var sawPrivateConstructor = false

  private def ownerForFlags(jflags: JavaAccFlags) = if (jflags.isStatic) moduleClass else clazz

  def srcfile = srcfile0

  // u1, u2, and u4 are what these data types are called in the JVM spec.
  // They are an unsigned byte, unsigned char, and unsigned int respectively.
  // We bitmask u1 into an Int to make sure it's 0-255 (and u1 isn't used
  // for much beyond tags) but leave u2 alone as it's already unsigned.
  protected final def u1(): Int = in.nextByte & 0xFF
  protected final def u2(): Int = in.nextChar.toInt
  protected final def u4(): Int = in.nextInt

  protected final def s1(): Int = in.nextByte.toInt // sign-extend the byte to int
  protected final def s2(): Int = (in.nextByte.toInt << 8) | u1 // sign-extend and shift the first byte, or with the unsigned second byte

  private def readInnerClassFlags() = readClassFlags()
  private def readClassFlags()      = JavaAccFlags classFlags u2
  private def readMethodFlags()     = JavaAccFlags methodFlags u2
  private def readFieldFlags()      = JavaAccFlags fieldFlags u2
  private def readTypeName()        = readName().toTypeName
  private def readName()            = pool.getName(u2).name
  private def readType()            = pool getType u2

  private object unpickler extends scala.reflect.internal.pickling.UnPickler {
    val symbolTable: ClassfileParser.this.symbolTable.type = ClassfileParser.this.symbolTable
  }

  private def handleMissing(e: MissingRequirementError) = {
    if (settings.debug) e.printStackTrace
    throw new IOException(s"Missing dependency '${e.req}', required by $file")
  }
  private def handleError(e: Exception) = {
    if (settings.debug) e.printStackTrace()
    throw new IOException(s"class file '$file' is broken\n(${e.getClass}/${e.getMessage})")
  }
  private def mismatchError(c: Symbol) = {
    throw new IOException(s"class file '$file' has location not matching its contents: contains $c")
  }

  private def parseErrorHandler[T]: PartialFunction[Throwable, T] = {
    case e: MissingRequirementError => handleMissing(e)
    case e: RuntimeException        => handleError(e)
  }
  @inline private def pushBusy[T](sym: Symbol)(body: => T): T = {
    if (busy eq sym)
      throw new IOException(s"unsatisfiable cyclic dependency in '$sym'")
    else if ((busy ne null) && (busy ne NoSymbol))
      throw new IOException(s"illegal class file dependency between '$sym' and '$busy'")

    busy = sym
    try body
    catch parseErrorHandler
    finally busy = NoSymbol
  }

  /**
   * `clazz` and `module` are the class and module symbols corresponding to the classfile being
   * parsed. Note that the ClassfileLoader unconditionally creates both of these symbols, they may
   * may get invalidated later on (.exists).
   *
   * Note that using `companionModule` / `companionClass` does not always work to navigate between
   * those two symbols, namely when they are shadowed by a type / value in the a package object
   * (scala-dev#248).
   */
  def parse(file: AbstractFile, clazz: ClassSymbol, module: ModuleSymbol): Unit = {
    this.file = file
    pushBusy(clazz) {
      reader.using { reader =>
        this.in           = reader.reset(file)
        this.clazz        = clazz
        this.staticModule = module
        this.isScala      = false

        val magic = in.getInt(in.bp)
        if (magic != JAVA_MAGIC && file.name.endsWith(".sig")) {
          currentClass = clazz.javaClassName
          isScala = true
          unpickler.unpickle(in.buf, 0, clazz, staticModule, file.name)
        } else {
          parseHeader()
          this.pool = new ConstantPool
          parseClass()
          pool = null
        }
      }
      in = null
    }
  }

  private def parseHeader(): Unit = {
    val magic = u4
    if (magic != JAVA_MAGIC)
      abort(s"class file ${file} has wrong magic number 0x${toHexString(magic)}")

    val minor, major = u2
    if (major < JAVA_MAJOR_VERSION || major == JAVA_MAJOR_VERSION && minor < JAVA_MINOR_VERSION)
      abort(s"class file ${file} has unknown version $major.$minor, should be at least $JAVA_MAJOR_VERSION.$JAVA_MINOR_VERSION")
  }

  protected class NameOrString(val value: String) {
    private var _name: Name = null
    def name: Name = {
      if (_name eq null) _name = TermName(value)
      _name
    }
  }

  def getClassSymbol(name: String): Symbol = {
    name match {
      case name if name.endsWith(nme.MODULE_SUFFIX_STRING) => rootMirror getModuleByName newTermName(name).dropModule
      case name                           => classNameToSymbol(name)
    }
  }

  /**
   * Constructor of this class should not be called directly, use `newConstantPool` instead.
   */
  protected class ConstantPool {
    protected val len          = u2
    protected val starts       = new Array[Int](len)
    protected val values       = new Array[AnyRef](len)
    protected val internalized = new Array[NameOrString](len)

    val initBp = in.bp

    { var i = 1
      while (i < starts.length) {
        starts(i) = in.bp
        i += 1
        (u1: @switch) match {
          case CONSTANT_UTF8 | CONSTANT_UNICODE                                => in skip u2
          case CONSTANT_CLASS | CONSTANT_STRING | CONSTANT_METHODTYPE          => in skip 2
          case CONSTANT_METHODHANDLE                                           => in skip 3
          case CONSTANT_FIELDREF | CONSTANT_METHODREF | CONSTANT_INTFMETHODREF => in skip 4
          case CONSTANT_NAMEANDTYPE | CONSTANT_INTEGER | CONSTANT_FLOAT        => in skip 4
          case CONSTANT_INVOKEDYNAMIC                                          => in skip 4
          case CONSTANT_LONG | CONSTANT_DOUBLE                                 => in skip 8 ; i += 1
          case _                                                               => errorBadTag(in.bp - 1)
        }
      }
    }
    val endBp = in.bp
    def recordAtIndex[T <: AnyRef](value: T, idx: Int): T = {
      values(idx) = value
      value
    }

    def firstExpecting(index: Int, expected: Int): Int = {
      val start = starts(index)
      val first = in.getByte(start).toInt
      if (first == expected) start + 1
      else this errorBadTag start
    }

    /** Return the name found at given index. */
    def getName(index: Int): NameOrString = (
      if (index <= 0 || len <= index) errorBadIndex(index)
      else values(index) match {
        case name: NameOrString => name
        case _          =>
          val start = firstExpecting(index, CONSTANT_UTF8)
          val len   = in.getChar(start).toInt
          recordAtIndex(new NameOrString(in.getUTF(start, len + 2)), index)
      }
    )

    /** Return the name found at given index in the constant pool, with '/' replaced by '.'. */
    def getExternalName(index: Int): NameOrString = {
      if (index <= 0 || len <= index)
        errorBadIndex(index)

      if (internalized(index) == null)
        internalized(index) = new NameOrString(getName(index).value.replace('/', '.'))

      internalized(index)
    }

    def getClassSymbol(index: Int): Symbol = {
      if (index <= 0 || len <= index) errorBadIndex(index)
      values(index) match {
        case sym: Symbol => sym
        case _           =>
          val result = ClassfileParser.this.getClassSymbol(getClassName(index).value)
          recordAtIndex(result, index)
      }
    }

    /** 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): NameOrString = {
      val start = firstExpecting(index, CONSTANT_CLASS)
      getExternalName((in.getChar(start)).toInt)
    }

    /** Return a name and a type at the given index. If the type is a method
     *  type, a dummy symbol is created in `ownerTpe`, which is used as the
     *  owner of its value parameters. This might lead to inconsistencies,
     *  if a symbol of the given name already exists, and has a different
     *  type.
     */
    protected def getNameAndType(index: Int, ownerTpe: Type): (Name, Type) = {
      if (index <= 0 || len <= index) errorBadIndex(index)
      values(index) match {
        case p: ((Name @unchecked, Type @unchecked)) => p
        case _                                       =>
          val start = firstExpecting(index, CONSTANT_NAMEANDTYPE)
          val name = getName(in.getChar(start).toInt)
          // create a dummy symbol for method types
          val dummy = ownerTpe.typeSymbol.newMethod(name.name.toTermName, ownerTpe.typeSymbol.pos)
          val tpe   = getType(dummy, in.getChar(start + 2).toInt)
          // fix the return type, which is blindly set to the class currently parsed
          val restpe = tpe match {
            case MethodType(formals, _) if name.name == nme.CONSTRUCTOR => MethodType(formals, ownerTpe)
            case _                                                      => tpe
          }
          ((name.name, restpe))
      }
    }

    /** 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): Type = {
      if (index <= 0 || len <= index) errorBadIndex(index)
      else values(index) match {
        case tp: Type    => tp
        case cls: Symbol => cls.tpe_*
        case _           =>
          val name = getClassName(index)
          name.value.charAt(0) match {
            case ARRAY_TAG => recordAtIndex(sigToType(null, name.value), index)
            case _         => recordAtIndex(classNameToSymbol(name.value), index).tpe_*
          }
      }
    }

    def getType(index: Int): Type              = getType(null, index)
    def getType(sym: Symbol, index: Int): Type = sigToType(sym, getExternalName(index).value)
    def getSuperClassName(index: Int): NameOrString = if (index == 0) null else getClassName(index) // the only classfile that is allowed to have `0` in the super_class is java/lang/Object (see jvm spec)

    private def createConstant(index: Int): Constant = {
      val start = starts(index)
      Constant((in.getByte(start).toInt: @switch) match {
        case CONSTANT_STRING  => getName(in.getChar(start + 1).toInt).value
        case CONSTANT_INTEGER => in.getInt(start + 1)
        case CONSTANT_FLOAT   => in.getFloat(start + 1)
        case CONSTANT_LONG    => in.getLong(start + 1)
        case CONSTANT_DOUBLE  => in.getDouble(start + 1)
        case CONSTANT_CLASS   => getClassOrArrayType(index).typeSymbol.tpe_* // !!! Is this necessary or desirable?
        case _                => errorBadTag(start)
      })
    }
    def getConstant(index: Char): Constant = getConstant(index.toInt)
    def getConstant(index: Int): Constant = (
      if (index <= 0 || len <= index) errorBadIndex(index)
      else values(index) match {
        case  const: Constant => const
        case sym: Symbol      => Constant(sym.tpe_*)
        case tpe: Type        => Constant(tpe)
        case _                => recordAtIndex(createConstant(index), index)
      }
    )

    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
    }

    /**
     * Get an array of bytes stored in the classfile as a string. The data is encoded in the format
     * described in object [[ByteCodecs]]. Used for the ScalaSignature annotation argument.
     */
    def getBytes(index: Int): Array[Byte] = {
      if (index <= 0 || len <= index) errorBadIndex(index)
      else values(index) match {
        case xs: Array[Byte] => xs
        case _ =>
          val start = firstExpecting(index, CONSTANT_UTF8)
          val len = (in getChar start).toInt
          val bytes = new Array[Byte](len)
          in.getBytes(start + 2, bytes)
          recordAtIndex(getSubArray(bytes), index)
      }
    }

    /**
     * Get an array of bytes stored in the classfile as an array of strings. The data is encoded in
     * the format described in object [[ByteCodecs]]. Used for the ScalaLongSignature annotation
     * argument.
     */
    def getBytes(indices: List[Int]): Array[Byte] = {
      val head = indices.head
      values(head) match {
        case xs: Array[Byte] => xs
        case _               =>
          val arr: Array[Byte] = indices.toArray flatMap { index =>
            if (index <= 0 || ConstantPool.this.len <= index) errorBadIndex(index)
            val start = firstExpecting(index, CONSTANT_UTF8)
            val len   = (in getChar start).toInt
            val s     = start + 2
            val result = new Array[Byte](len)
            in.getBytes(s, result)
            result
          }
          recordAtIndex(getSubArray(arr), head)
      }
    }

    /** Throws an exception signaling a bad constant index. */
    protected def errorBadIndex(index: Int) =
      abort(s"bad constant pool index: $index at pos: ${in.bp}")

    /** Throws an exception signaling a bad tag at given address. */
    protected def errorBadTag(start: Int) =
      abort(s"bad constant pool tag ${in.getByte(start)} at byte $start")
  }

  def stubClassSymbol(name: Name): Symbol = {
    // scala/bug#5593 Scaladoc's current strategy is to visit all packages in search of user code that can be documented
    // therefore, it will rummage through the classpath triggering errors whenever it encounters package objects
    // that are not in their correct place (see bug for details)

    // TODO More consistency with use of stub symbols in `Unpickler`
    //   - better owner than `NoSymbol`
    //   - remove eager warning
    val msg = s"Class $name not found - continuing with a stub."
    if ((!settings.isScaladoc) && (settings.verbose || settings.developer)) warning(msg)
    NoSymbol.newStubSymbol(name.toTypeName, msg)
  }

  private def lookupClass(name: String) = try {
    def lookupTopLevel = {
      if (name contains '.')
        rootMirror getClassByName name
      else
      // FIXME - we shouldn't be doing ad hoc lookups in the empty package, getClassByName should return the class
        definitions.getMember(rootMirror.EmptyPackageClass, newTypeName(name))
    }

    // For inner classes we usually don't get here: `classNameToSymbol` already returns the symbol
    // of the inner class based on the InnerClass table. However, if the classfile is missing the
    // InnerClass entry for `name`, it might still be that there exists an inner symbol (because
    // some other classfile _does_ have an InnerClass entry for `name`). In this case, we want to
    // return the actual inner symbol (C.D, with owner C), not the top-level symbol C$D. This is
    // what the logic below is for (see PR #5822 / scala/bug#9937).
    val split = if (isScalaRaw) -1 else name.lastIndexOf('$')
    if (split > 0 && split < name.length) {
      val outerName = name.substring(0, split)
      val innerName = name.substring(split + 1, name.length)
      val outerSym = classNameToSymbol(outerName)

      // If the outer class C cannot be found, look for a top-level class C$D
      if (outerSym.isInstanceOf[StubSymbol]) lookupTopLevel
      else {
        val innerNameAsName = newTypeName(innerName)

        // We have a java-defined class name C$D and look for a member D of C. But we don't know if
        // D is declared static or not, so we have to search both in class C and its companion.
        val r = if (outerSym == clazz)
          staticScope.lookup(innerNameAsName) orElse
            instanceScope.lookup(innerNameAsName)
        else
          lookupMemberAtTyperPhaseIfPossible(outerSym, innerNameAsName) orElse
            lookupMemberAtTyperPhaseIfPossible(outerSym.companionModule, innerNameAsName)
        r orElse lookupTopLevel
      }
    } else
      lookupTopLevel
  } catch {
    // The handler
    //   - prevents crashes with deficient InnerClassAttributes (scala/bug#2464, 0ce0ad5)
    //   - was referenced in the bugfix commit for scala/bug#3756 (4fb0d53), not sure why
    //   - covers the case when a type alias in a package object shadows a class symbol,
    //     getClassByName throws a MissingRequirementError (scala-dev#248)
    case ex: FatalError =>
      // getClassByName can throw a MissingRequirementError (which extends FatalError)
      // definitions.getMember can throw a FatalError, for example in pos/t5165b
      if (settings.debug)
        ex.printStackTrace()
      stubClassSymbol(newTypeName(name))
  }

  /** Return the class symbol of the given name. */
  def classNameToSymbol(name: String): Symbol = {
    if (innerClasses contains name)
      innerClasses innerSymbol name
    else
      lookupClass(name)
  }

  def parseClass(): Unit = {
    unpickleOrParseInnerClasses()

    val jflags = readClassFlags()
    val classNameIndex = u2
    currentClass = pool.getClassName(classNameIndex).value

    // Ensure that (top-level) classfiles are in the correct directory
    val isTopLevel = !(currentClass contains '$') // Java class name; *don't* try to to use Scala name decoding (scala/bug#7532)
    if (isTopLevel) {
      val c = pool.getClassSymbol(classNameIndex)
      // scala-dev#248: when a type alias (in a package object) shadows a class symbol, getClassSymbol returns a stub
      // TODO: this also prevents the error when it would be useful (`mv a/C.class .`)
      if (!c.isInstanceOf[StubSymbol] && c != clazz) mismatchError(c)
    }

    // TODO: remove after the next 2.13 milestone
    // A bug in the backend caused classes ending in `$` do get only a Scala marker attribute
    // instead of a ScalaSig and a Signature annotaiton. This went unnoticed because isScalaRaw
    // classes were parsed like Java classes. The below covers the cases in the std lib.
    def isNothingOrNull = {
      val n = clazz.fullName.toString
      n == "scala.runtime.Nothing$" || n == "scala.runtime.Null$"
    }

    if (isScala) {
      () // We're done
    } else if (isScalaRaw && !isNothingOrNull) {
      val decls = clazz.enclosingPackage.info.decls
      for (c <- List(clazz, staticModule, staticModule.moduleClass)) {
        c.setInfo(NoType)
        decls.unlink(c)<commit_msg>Remove code in compiler per TODO<commit_after>/*
 * Scala (https://www.scala-lang.org)
 *
 * Copyright EPFL and Lightbend, Inc.
 *
 * Licensed under Apache License 2.0
 * (http://www.apache.org/licenses/LICENSE-2.0).
 *
 * See the NOTICE file distributed with this work for
 * additional information regarding copyright ownership.
 */

package scala
package tools.nsc
package symtab
package classfile

import java.io.{ByteArrayInputStream, DataInputStream, File, IOException}
import java.lang.Integer.toHexString
import java.nio.ByteBuffer

import scala.collection.{immutable, mutable}
import scala.collection.mutable.{ArrayBuffer, ListBuffer}
import scala.annotation.switch
import scala.reflect.internal.JavaAccFlags
import scala.reflect.internal.pickling.ByteCodecs
import scala.reflect.internal.util.ReusableInstance
import scala.reflect.io.{NoAbstractFile, VirtualFile}
import scala.tools.nsc.util.ClassPath
import scala.tools.nsc.io.AbstractFile
import scala.util.control.NonFatal

/** This abstract class implements a class file parser.
 *
 *  @author <NAME>
 *  @version 1.0
 */
abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) {
  val symbolTable: SymbolTable {
    def settings: Settings
  }
  val loaders: SymbolLoaders {
    val symbolTable: ClassfileParser.this.symbolTable.type
  }

  import symbolTable._
  /**
   * If typer phase is defined then perform member lookup of a symbol
   * `sym` at typer phase. This method results from refactoring. The
   * original author of the logic that uses typer phase didn't explain
   * why we need to force infos at that phase specifically. It only mentioned
   * that ClassfileParse can be called late (e.g. at flatten phase) and
   * we make to make sure we handle such situation properly.
   */
  protected def lookupMemberAtTyperPhaseIfPossible(sym: Symbol, name: Name): Symbol

  /** The compiler classpath. */
  def classPath: ClassPath

  import definitions._
  import scala.reflect.internal.ClassfileConstants._
  import Flags._

  protected var file: AbstractFile = _         // the class file
  protected var in: DataReader = _             // the class file reader
  protected var clazz: ClassSymbol = _         // the class symbol containing dynamic members
  protected var staticModule: ModuleSymbol = _ // the module symbol containing static members
  protected var instanceScope: Scope = _       // the scope of all instance definitions
  protected var staticScope: Scope = _         // the scope of all static definitions
  protected var pool: ConstantPool = _         // the classfile's constant pool
  protected var isScala: Boolean = _           // does class file describe a scala class?
  protected var isScalaRaw: Boolean = _        // this class file is a scala class with no pickled info
  protected var busy: Symbol = _               // lock to detect recursive reads
  protected var currentClass: String = _       // JVM name of the current class
  protected var classTParams = Map[Name,Symbol]()
  protected var srcfile0 : Option[AbstractFile] = None
  protected def moduleClass: Symbol = staticModule.moduleClass
  private var sawPrivateConstructor = false

  private def ownerForFlags(jflags: JavaAccFlags) = if (jflags.isStatic) moduleClass else clazz

  def srcfile = srcfile0

  // u1, u2, and u4 are what these data types are called in the JVM spec.
  // They are an unsigned byte, unsigned char, and unsigned int respectively.
  // We bitmask u1 into an Int to make sure it's 0-255 (and u1 isn't used
  // for much beyond tags) but leave u2 alone as it's already unsigned.
  protected final def u1(): Int = in.nextByte & 0xFF
  protected final def u2(): Int = in.nextChar.toInt
  protected final def u4(): Int = in.nextInt

  protected final def s1(): Int = in.nextByte.toInt // sign-extend the byte to int
  protected final def s2(): Int = (in.nextByte.toInt << 8) | u1 // sign-extend and shift the first byte, or with the unsigned second byte

  private def readInnerClassFlags() = readClassFlags()
  private def readClassFlags()      = JavaAccFlags classFlags u2
  private def readMethodFlags()     = JavaAccFlags methodFlags u2
  private def readFieldFlags()      = JavaAccFlags fieldFlags u2
  private def readTypeName()        = readName().toTypeName
  private def readName()            = pool.getName(u2).name
  private def readType()            = pool getType u2

  private object unpickler extends scala.reflect.internal.pickling.UnPickler {
    val symbolTable: ClassfileParser.this.symbolTable.type = ClassfileParser.this.symbolTable
  }

  private def handleMissing(e: MissingRequirementError) = {
    if (settings.debug) e.printStackTrace
    throw new IOException(s"Missing dependency '${e.req}', required by $file")
  }
  private def handleError(e: Exception) = {
    if (settings.debug) e.printStackTrace()
    throw new IOException(s"class file '$file' is broken\n(${e.getClass}/${e.getMessage})")
  }
  private def mismatchError(c: Symbol) = {
    throw new IOException(s"class file '$file' has location not matching its contents: contains $c")
  }

  private def parseErrorHandler[T]: PartialFunction[Throwable, T] = {
    case e: MissingRequirementError => handleMissing(e)
    case e: RuntimeException        => handleError(e)
  }
  @inline private def pushBusy[T](sym: Symbol)(body: => T): T = {
    if (busy eq sym)
      throw new IOException(s"unsatisfiable cyclic dependency in '$sym'")
    else if ((busy ne null) && (busy ne NoSymbol))
      throw new IOException(s"illegal class file dependency between '$sym' and '$busy'")

    busy = sym
    try body
    catch parseErrorHandler
    finally busy = NoSymbol
  }

  /**
   * `clazz` and `module` are the class and module symbols corresponding to the classfile being
   * parsed. Note that the ClassfileLoader unconditionally creates both of these symbols, they may
   * may get invalidated later on (.exists).
   *
   * Note that using `companionModule` / `companionClass` does not always work to navigate between
   * those two symbols, namely when they are shadowed by a type / value in the a package object
   * (scala-dev#248).
   */
  def parse(file: AbstractFile, clazz: ClassSymbol, module: ModuleSymbol): Unit = {
    this.file = file
    pushBusy(clazz) {
      reader.using { reader =>
        this.in           = reader.reset(file)
        this.clazz        = clazz
        this.staticModule = module
        this.isScala      = false

        val magic = in.getInt(in.bp)
        if (magic != JAVA_MAGIC && file.name.endsWith(".sig")) {
          currentClass = clazz.javaClassName
          isScala = true
          unpickler.unpickle(in.buf, 0, clazz, staticModule, file.name)
        } else {
          parseHeader()
          this.pool = new ConstantPool
          parseClass()
          pool = null
        }
      }
      in = null
    }
  }

  private def parseHeader(): Unit = {
    val magic = u4
    if (magic != JAVA_MAGIC)
      abort(s"class file ${file} has wrong magic number 0x${toHexString(magic)}")

    val minor, major = u2
    if (major < JAVA_MAJOR_VERSION || major == JAVA_MAJOR_VERSION && minor < JAVA_MINOR_VERSION)
      abort(s"class file ${file} has unknown version $major.$minor, should be at least $JAVA_MAJOR_VERSION.$JAVA_MINOR_VERSION")
  }

  protected class NameOrString(val value: String) {
    private var _name: Name = null
    def name: Name = {
      if (_name eq null) _name = TermName(value)
      _name
    }
  }

  def getClassSymbol(name: String): Symbol = {
    name match {
      case name if name.endsWith(nme.MODULE_SUFFIX_STRING) => rootMirror getModuleByName newTermName(name).dropModule
      case name                           => classNameToSymbol(name)
    }
  }

  /**
   * Constructor of this class should not be called directly, use `newConstantPool` instead.
   */
  protected class ConstantPool {
    protected val len          = u2
    protected val starts       = new Array[Int](len)
    protected val values       = new Array[AnyRef](len)
    protected val internalized = new Array[NameOrString](len)

    val initBp = in.bp

    { var i = 1
      while (i < starts.length) {
        starts(i) = in.bp
        i += 1
        (u1: @switch) match {
          case CONSTANT_UTF8 | CONSTANT_UNICODE                                => in skip u2
          case CONSTANT_CLASS | CONSTANT_STRING | CONSTANT_METHODTYPE          => in skip 2
          case CONSTANT_METHODHANDLE                                           => in skip 3
          case CONSTANT_FIELDREF | CONSTANT_METHODREF | CONSTANT_INTFMETHODREF => in skip 4
          case CONSTANT_NAMEANDTYPE | CONSTANT_INTEGER | CONSTANT_FLOAT        => in skip 4
          case CONSTANT_INVOKEDYNAMIC                                          => in skip 4
          case CONSTANT_LONG | CONSTANT_DOUBLE                                 => in skip 8 ; i += 1
          case _                                                               => errorBadTag(in.bp - 1)
        }
      }
    }
    val endBp = in.bp
    def recordAtIndex[T <: AnyRef](value: T, idx: Int): T = {
      values(idx) = value
      value
    }

    def firstExpecting(index: Int, expected: Int): Int = {
      val start = starts(index)
      val first = in.getByte(start).toInt
      if (first == expected) start + 1
      else this errorBadTag start
    }

    /** Return the name found at given index. */
    def getName(index: Int): NameOrString = (
      if (index <= 0 || len <= index) errorBadIndex(index)
      else values(index) match {
        case name: NameOrString => name
        case _          =>
          val start = firstExpecting(index, CONSTANT_UTF8)
          val len   = in.getChar(start).toInt
          recordAtIndex(new NameOrString(in.getUTF(start, len + 2)), index)
      }
    )

    /** Return the name found at given index in the constant pool, with '/' replaced by '.'. */
    def getExternalName(index: Int): NameOrString = {
      if (index <= 0 || len <= index)
        errorBadIndex(index)

      if (internalized(index) == null)
        internalized(index) = new NameOrString(getName(index).value.replace('/', '.'))

      internalized(index)
    }

    def getClassSymbol(index: Int): Symbol = {
      if (index <= 0 || len <= index) errorBadIndex(index)
      values(index) match {
        case sym: Symbol => sym
        case _           =>
          val result = ClassfileParser.this.getClassSymbol(getClassName(index).value)
          recordAtIndex(result, index)
      }
    }

    /** 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): NameOrString = {
      val start = firstExpecting(index, CONSTANT_CLASS)
      getExternalName((in.getChar(start)).toInt)
    }

    /** Return a name and a type at the given index. If the type is a method
     *  type, a dummy symbol is created in `ownerTpe`, which is used as the
     *  owner of its value parameters. This might lead to inconsistencies,
     *  if a symbol of the given name already exists, and has a different
     *  type.
     */
    protected def getNameAndType(index: Int, ownerTpe: Type): (Name, Type) = {
      if (index <= 0 || len <= index) errorBadIndex(index)
      values(index) match {
        case p: ((Name @unchecked, Type @unchecked)) => p
        case _                                       =>
          val start = firstExpecting(index, CONSTANT_NAMEANDTYPE)
          val name = getName(in.getChar(start).toInt)
          // create a dummy symbol for method types
          val dummy = ownerTpe.typeSymbol.newMethod(name.name.toTermName, ownerTpe.typeSymbol.pos)
          val tpe   = getType(dummy, in.getChar(start + 2).toInt)
          // fix the return type, which is blindly set to the class currently parsed
          val restpe = tpe match {
            case MethodType(formals, _) if name.name == nme.CONSTRUCTOR => MethodType(formals, ownerTpe)
            case _                                                      => tpe
          }
          ((name.name, restpe))
      }
    }

    /** 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): Type = {
      if (index <= 0 || len <= index) errorBadIndex(index)
      else values(index) match {
        case tp: Type    => tp
        case cls: Symbol => cls.tpe_*
        case _           =>
          val name = getClassName(index)
          name.value.charAt(0) match {
            case ARRAY_TAG => recordAtIndex(sigToType(null, name.value), index)
            case _         => recordAtIndex(classNameToSymbol(name.value), index).tpe_*
          }
      }
    }

    def getType(index: Int): Type              = getType(null, index)
    def getType(sym: Symbol, index: Int): Type = sigToType(sym, getExternalName(index).value)
    def getSuperClassName(index: Int): NameOrString = if (index == 0) null else getClassName(index) // the only classfile that is allowed to have `0` in the super_class is java/lang/Object (see jvm spec)

    private def createConstant(index: Int): Constant = {
      val start = starts(index)
      Constant((in.getByte(start).toInt: @switch) match {
        case CONSTANT_STRING  => getName(in.getChar(start + 1).toInt).value
        case CONSTANT_INTEGER => in.getInt(start + 1)
        case CONSTANT_FLOAT   => in.getFloat(start + 1)
        case CONSTANT_LONG    => in.getLong(start + 1)
        case CONSTANT_DOUBLE  => in.getDouble(start + 1)
        case CONSTANT_CLASS   => getClassOrArrayType(index).typeSymbol.tpe_* // !!! Is this necessary or desirable?
        case _                => errorBadTag(start)
      })
    }
    def getConstant(index: Char): Constant = getConstant(index.toInt)
    def getConstant(index: Int): Constant = (
      if (index <= 0 || len <= index) errorBadIndex(index)
      else values(index) match {
        case  const: Constant => const
        case sym: Symbol      => Constant(sym.tpe_*)
        case tpe: Type        => Constant(tpe)
        case _                => recordAtIndex(createConstant(index), index)
      }
    )

    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
    }

    /**
     * Get an array of bytes stored in the classfile as a string. The data is encoded in the format
     * described in object [[ByteCodecs]]. Used for the ScalaSignature annotation argument.
     */
    def getBytes(index: Int): Array[Byte] = {
      if (index <= 0 || len <= index) errorBadIndex(index)
      else values(index) match {
        case xs: Array[Byte] => xs
        case _ =>
          val start = firstExpecting(index, CONSTANT_UTF8)
          val len = (in getChar start).toInt
          val bytes = new Array[Byte](len)
          in.getBytes(start + 2, bytes)
          recordAtIndex(getSubArray(bytes), index)
      }
    }

    /**
     * Get an array of bytes stored in the classfile as an array of strings. The data is encoded in
     * the format described in object [[ByteCodecs]]. Used for the ScalaLongSignature annotation
     * argument.
     */
    def getBytes(indices: List[Int]): Array[Byte] = {
      val head = indices.head
      values(head) match {
        case xs: Array[Byte] => xs
        case _               =>
          val arr: Array[Byte] = indices.toArray flatMap { index =>
            if (index <= 0 || ConstantPool.this.len <= index) errorBadIndex(index)
            val start = firstExpecting(index, CONSTANT_UTF8)
            val len   = (in getChar start).toInt
            val s     = start + 2
            val result = new Array[Byte](len)
            in.getBytes(s, result)
            result
          }
          recordAtIndex(getSubArray(arr), head)
      }
    }

    /** Throws an exception signaling a bad constant index. */
    protected def errorBadIndex(index: Int) =
      abort(s"bad constant pool index: $index at pos: ${in.bp}")

    /** Throws an exception signaling a bad tag at given address. */
    protected def errorBadTag(start: Int) =
      abort(s"bad constant pool tag ${in.getByte(start)} at byte $start")
  }

  def stubClassSymbol(name: Name): Symbol = {
    // scala/bug#5593 Scaladoc's current strategy is to visit all packages in search of user code that can be documented
    // therefore, it will rummage through the classpath triggering errors whenever it encounters package objects
    // that are not in their correct place (see bug for details)

    // TODO More consistency with use of stub symbols in `Unpickler`
    //   - better owner than `NoSymbol`
    //   - remove eager warning
    val msg = s"Class $name not found - continuing with a stub."
    if ((!settings.isScaladoc) && (settings.verbose || settings.developer)) warning(msg)
    NoSymbol.newStubSymbol(name.toTypeName, msg)
  }

  private def lookupClass(name: String) = try {
    def lookupTopLevel = {
      if (name contains '.')
        rootMirror getClassByName name
      else
      // FIXME - we shouldn't be doing ad hoc lookups in the empty package, getClassByName should return the class
        definitions.getMember(rootMirror.EmptyPackageClass, newTypeName(name))
    }

    // For inner classes we usually don't get here: `classNameToSymbol` already returns the symbol
    // of the inner class based on the InnerClass table. However, if the classfile is missing the
    // InnerClass entry for `name`, it might still be that there exists an inner symbol (because
    // some other classfile _does_ have an InnerClass entry for `name`). In this case, we want to
    // return the actual inner symbol (C.D, with owner C), not the top-level symbol C$D. This is
    // what the logic below is for (see PR #5822 / scala/bug#9937).
    val split = if (isScalaRaw) -1 else name.lastIndexOf('$')
    if (split > 0 && split < name.length) {
      val outerName = name.substring(0, split)
      val innerName = name.substring(split + 1, name.length)
      val outerSym = classNameToSymbol(outerName)

      // If the outer class C cannot be found, look for a top-level class C$D
      if (outerSym.isInstanceOf[StubSymbol]) lookupTopLevel
      else {
        val innerNameAsName = newTypeName(innerName)

        // We have a java-defined class name C$D and look for a member D of C. But we don't know if
        // D is declared static or not, so we have to search both in class C and its companion.
        val r = if (outerSym == clazz)
          staticScope.lookup(innerNameAsName) orElse
            instanceScope.lookup(innerNameAsName)
        else
          lookupMemberAtTyperPhaseIfPossible(outerSym, innerNameAsName) orElse
            lookupMemberAtTyperPhaseIfPossible(outerSym.companionModule, innerNameAsName)
        r orElse lookupTopLevel
      }
    } else
      lookupTopLevel
  } catch {
    // The handler
    //   - prevents crashes with deficient InnerClassAttributes (scala/bug#2464, 0ce0ad5)
    //   - was referenced in the bugfix commit for scala/bug#3756 (4fb0d53), not sure why
    //   - covers the case when a type alias in a package object shadows a class symbol,
    //     getClassByName throws a MissingRequirementError (scala-dev#248)
    case ex: FatalError =>
      // getClassByName can throw a MissingRequirementError (which extends FatalError)
      // definitions.getMember can throw a FatalError, for example in pos/t5165b
      if (settings.debug)
        ex.printStackTrace()
      stubClassSymbol(newTypeName(name))
  }

  /** Return the class symbol of the given name. */
  def classNameToSymbol(name: String): Symbol = {
    if (innerClasses contains name)
      innerClasses innerSymbol name
    else
      lookupClass(name)
  }

  def parseClass(): Unit = {
    unpickleOrParseInnerClasses()

    val jflags = readClassFlags()
    val classNameIndex = u2
    currentClass = pool.getClassName(classNameIndex).value

    // Ensure that (top-level) classfiles are in the correct directory
    val isTopLevel = !(currentClass contains '$') // Java class name; *don't* try to to use Scala name decoding (scala/bug#7532)
    if (isTopLevel) {
      val c = pool.getClassSymbol(classNameIndex)
      // scala-dev#248: when a type alias (in a package object) shadows a class symbol, getClassSymbol returns a stub
      // TODO: this also prevents the error when it would be useful (`mv a/C.class .`)
      if (!c.isInstanceOf[StubSymbol] && c != clazz) mismatchError(c)
    }

    if (isScala) {
      () // We're done
    } else if (isScalaRaw) {
      val decls = clazz.enclosingPackage.info.decls
      for (c <- List(clazz, staticModule, staticModule.moduleClass)) {
        c.setInfo(NoType)
        decls.unlink(c)
{'fraction_non_alphanumeric': 0.09143707061406615, 'fraction_numerical': 0.005925820997709942, 'mean_word_length': 2.556805777143337, 'pattern_counts': {'":': 0, '<': 53, '<?xml version=': 0, '>': 95, 'https://': 2, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 26, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4824251', 'n_tokens_mistral': 12259, 'n_tokens_neox': 11904, 'n_words': 4081}