Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't get symbol for local class #6622

Closed
scabug opened this issue Nov 6, 2012 · 10 comments
Closed

Can't get symbol for local class #6622

scabug opened this issue Nov 6, 2012 · 10 comments
Assignees
Labels
Milestone

Comments

@scabug
Copy link

scabug commented Nov 6, 2012

I saw #5431 and #6323; not sure if it'd be better to reopen one of those or file this as new.

scala> reflect.runtime.currentMirror.classSymbol((new {}).getClass)
res0: reflect.runtime.universe.ClassSymbol = anonymous class $anon$1

scala> reflect.runtime.currentMirror.classSymbol({ class A; (new A).getClass })
java.lang.AssertionError: assertion failed: not a type: symbol
 loaded from class A$1 in object $iw with name A$1 and classloader scala.tools.nsc.interpreter.IMain$TranslatingClassLoader@612dcb8c
        at scala.Predef$.assert(Predef.scala:179)
        at scala.reflect.runtime.JavaMirrors$JavaMirror.scala$reflect$runtime$JavaMirrors$JavaMirror$$classToScala1(JavaMirrors.scala:957)
        at scala.reflect.runtime.JavaMirrors$JavaMirror$$anonfun$classToScala$1.apply(JavaMirrors.scala:916)
        at scala.reflect.runtime.JavaMirrors$JavaMirror$$anonfun$classToScala$1.apply(JavaMirrors.scala:916)
        at scala.reflect.runtime.JavaMirrors$JavaMirror$$anonfun$toScala$1.apply(JavaMirrors.scala:104)
        at scala.reflect.runtime.TwoWayCache.toScala(TwoWayCache.scala:38)
        at scala.reflect.runtime.JavaMirrors$JavaMirror.toScala(JavaMirrors.scala:102)
        at scala.reflect.runtime.JavaMirrors$JavaMirror.classToScala(JavaMirrors.scala:916)
        at scala.reflect.runtime.JavaMirrors$JavaMirror.classSymbol(JavaMirrors.scala:206)
        at scala.reflect.runtime.JavaMirrors$JavaMirror.classSymbol(JavaMirrors.scala:65)
        at .<init>(<console>:8)
        at .<clinit>(<console>)
        at .<init>(<console>:7)
        at .<clinit>(<console>)
        at $print(<console>)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.call(IMain.scala:731)
        at scala.tools.nsc.interpreter.IMain$Request.loadAndRun(IMain.scala:980)
        at scala.tools.nsc.interpreter.IMain.loadAndRunReq$1(IMain.scala:570)
        at scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:601)
        at scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:565)
        at scala.tools.nsc.interpreter.ILoop.reallyInterpret$1(ILoop.scala:745)
        at scala.tools.nsc.interpreter.ILoop.interpretStartingWith(ILoop.scala:790)
        at scala.tools.nsc.interpreter.ILoop.command(ILoop.scala:702)
        at scala.tools.nsc.interpreter.ILoop.processLine$1(ILoop.scala:566)
        at scala.tools.nsc.interpreter.ILoop.innerLoop$1(ILoop.scala:573)
        at scala.tools.nsc.interpreter.ILoop.loop(ILoop.scala:576)
        at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply$mcZ$sp(ILoop.scala:867)
        at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply(ILoop.scala:822)
        at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply(ILoop.scala:822)
        at scala.tools.nsc.util.ScalaClassLoader$.savingContextLoader(ScalaClassLoader.scala:135)
        at scala.tools.nsc.interpreter.ILoop.process(ILoop.scala:822)
        at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:83)
        at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:96)
        at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:105)
        at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)
@scabug
Copy link
Author

scabug commented Nov 6, 2012

Imported From: https://issues.scala-lang.org/browse/SI-6622?orig=1
Reporter: Ryan Hendrickson (ryan.hendrickson_bwater)
Affected Versions: 2.10.0-RC1
See #6759

@scabug
Copy link
Author

scabug commented Nov 6, 2012

@paulp said:
It only happens in the repl. The repl/reflection integration is super-sketchy since nobody asked me what would be likely to work. I hope to straighten it out somewhere down the line.

@scabug
Copy link
Author

scabug commented Nov 7, 2012

Ryan Hendrickson (ryan.hendrickson_bwater) said:
I see it with scalac as well, unfortunately.

package si6622

// Pulled out to rule out any potential DelayedInit shenanigans
object NotTheRepl {
  val result = {
    class A
    reflect.runtime.currentMirror.classSymbol((new A).getClass)
  }
}

object RunMe extends App {
  println(NotTheRepl.result)
}

@scabug
Copy link
Author

scabug commented Nov 7, 2012

@paulp said:
That's interesting. Here's why I said otherwise. Seems to be limited to vals.

object NotTheRepl {
  def f = {
    class A
    scala.reflect.runtime.currentMirror.classSymbol((new A).getClass)
  }
}
object RunMe extends App {
  println(NotTheRepl.f)
}

// % rcscala RunMe
// class A$1
// %

@scabug
Copy link
Author

scabug commented Apr 30, 2013

Evgeny (akshaal) said (edited on Apr 30, 2013 5:33:41 PM UTC):
Is there a workaround or something? It seems like there is no reliable way to get an instance mirror unless this one is fixed.

	at scala.reflect.runtime.JavaMirrors$JavaMirror.scala$reflect$runtime$JavaMirrors$JavaMirror$$classToScala1(JavaMirrors.scala:966)
	at scala.reflect.runtime.JavaMirrors$JavaMirror$$anonfun$classToScala$1.apply(JavaMirrors.scala:925)
	at scala.reflect.runtime.JavaMirrors$JavaMirror$$anonfun$classToScala$1.apply(JavaMirrors.scala:925)
	at scala.reflect.runtime.JavaMirrors$JavaMirror$$anonfun$toScala$1.apply(JavaMirrors.scala:104)
	at scala.reflect.runtime.TwoWayCache.toScala(TwoWayCache.scala:38)
	at scala.reflect.runtime.JavaMirrors$JavaMirror.toScala(JavaMirrors.scala:102)
	at scala.reflect.runtime.JavaMirrors$JavaMirror.classToScala(JavaMirrors.scala:925)
	at scala.reflect.runtime.JavaMirrors$JavaMirror.classSymbol(JavaMirrors.scala:205)
	at scala.reflect.runtime.JavaMirrors$JavaMirror$JavaInstanceMirror.symbol(JavaMirrors.scala:244)
	at scala.reflect.runtime.JavaMirrors$JavaMirror$JavaInstanceMirror.symbol(JavaMirrors.scala:242)

@scabug
Copy link
Author

scabug commented Jun 6, 2013

@retronym said (edited on Jun 6, 2014 7:07:00 AM UTC):
The difference between vals and defs comes down to whether or not that local class is emitted as an inner class or not. Class#isMemberClass reveals this; and is used by reflection to decide whether to look for this class in the declarations of the enclosing class, or to look it up as a top level class on the classpath.

I haven't quite figured out why def-vs-val is material; it may be a bug in the bytecode emitter.

Test:

object NotTheRepl1 {
  lazy val resultVal = {
    class A
    println("NotTheRepl1.resultVal::A " + classOf[A].isMemberClass)
    reflect.runtime.currentMirror.classSymbol((new A).getClass)
  }

  def resultDef = {
    class A
    println("NotTheRepl1.resultDef::A " + classOf[A].isMemberClass)
    reflect.runtime.currentMirror.classSymbol((new A).getClass)
  }
}

object NotTheRepl2 {
  val resultVal = {
    class B
    println("NotTheRepl2.resultVal::B " + classOf[B].isMemberClass)
    reflect.runtime.currentMirror.classSymbol((new B).getClass)
  }
}

object NotTheRepl3 {
  def resultDef = {
    class C
    println("NotTheRepl3.resultDef::C " + classOf[C].isMemberClass)
    reflect.runtime.currentMirror.classSymbol((new C).getClass)
  }
}


object Test extends App {

  println(NotTheRepl3.resultDef)
  println(NotTheRepl1.resultDef)
  println(NotTheRepl2.resultVal)
  println(NotTheRepl1.resultVal)
}

Breadcrumbs:

      // `jclazz.isLocalClass` doesn't work because of problems with `getSimpleName`
      // hence we have to approximate by removing the `isAnonymousClass` check
//      def isLocalClass0: Boolean = jclazz.isLocalClass
      def isLocalClass0: Boolean = jclazz.getEnclosingMethod != null || jclazz.getEnclosingConstructor != null
if (jclazz.isMemberClass) {
        val jEnclosingClass = jclazz.getEnclosingClass
        val sEnclosingClass = classToScala(jEnclosingClass)
        followStatic(sEnclosingClass, jclazz.javaFlags)
      } else if (jclazz.isLocalClass0) {
        val jEnclosingMethod = jclazz.getEnclosingMethod
        if (jEnclosingMethod != null) {
          methodToScala(jEnclosingMethod)
        } else {
          val jEnclosingConstructor = jclazz.getEnclosingConstructor
          constructorToScala(jEnclosingConstructor)
        }
....

val cls =
          if (jclazz.isMemberClass && !nme.isImplClassName(jname))
            lookupClass
          else if (jclazz.isLocalClass0 || isInvalidClassName(jname))
            // local classes and implementation classes not preserved by unpickling - treat as Java
            //
            // upd. but only if they cannot be loaded as top-level classes
            // otherwise we may mistake mangled symbolic names for mangled nested names
            //
            // in case when a Java binary name can be treated both as a top-level class and as a nested class
            // (as described in http://groups.google.com/group/scala-internals/browse_thread/thread/10855403bbf04298)
            // we check for a top-level class first
            // this is totally correct, because a top-level class and a nested class with the same name cannot coexist
            // so it's either one or another, but not both - therefore we always load $-bearing classes correctly
            lookupClass orElse jclassAsScala(jclazz)
    /** Return the name of this symbol that can be used on the Java platform.  It removes spaces from names.
     *
     *  Special handling:
     *    scala.Nothing erases to scala.runtime.Nothing$
     *       scala.Null erases to scala.runtime.Null$
     *
     *  This is needed because they are not real classes, and they mean
     *  'abrupt termination upon evaluation of that expression' or null respectively.
     *  This handling is done already in GenICode, but here we need to remove
     *  references from method signatures to these types, because such classes
     *  cannot exist in the classpath: the type checker will be very confused.
     */
    def javaName(sym: Symbol): String = {

        /*
         * Checks if given symbol corresponds to inner class/object and add it to innerClassBuffer
         *
         * Note: This method is called recursively thus making sure that we add complete chain
         * of inner class all until root class.
         */
        def collectInnerClass(s: Symbol): Unit = {
          // TODO: some enteringFlatten { ... } which accounts for
          // being nested in parameterized classes (if we're going to selectively flatten.)
          val x = innerClassSymbolFor(s)
          if(x ne NoSymbol) {
            assert(x.isClass, "not an inner-class symbol")
            val isInner = !x.rawowner.isPackageClass
            if (isInner) {
              innerClassBuffer += x
              collectInnerClass(x.rawowner)
            }
          }
        }
  protected def originalEnclosingMethod(sym: Symbol): Symbol = {
    if (sym.isMethod || sym == NoSymbol) sym
    else {
      val owner = originalOwner.getOrElse(sym, sym.rawowner)
      if (sym.isLocalDummy) owner.enclClass.primaryConstructor
      else originalEnclosingMethod(owner)
    }
  }

@scabug
Copy link
Author

scabug commented Jun 13, 2013

@retronym said:
scala/scala#2654

@scabug
Copy link
Author

scabug commented Jul 10, 2013

@adriaanm said:
Unassigning and rescheduling to M6 as previous deadline was missed.

@scabug
Copy link
Author

scabug commented Aug 5, 2014

@gkossakowski said:
The 2.11.2 is out so I'm rescheduling the issue for 2.11.3.

@scabug
Copy link
Author

scabug commented Sep 29, 2014

@lrytz said:
scala/scala#4011

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants