Strange behavior of toolbox compilation when referencing an inner static java class
Asked Answered
M

1

7

Supposed I have the following java class:

package com.test;
public class Outer {
    public static class Inner { public static final String VAL = "Inner"; }
}

I can reference the VAL constant from scala code just as you would expect but when I try compiling code that references this field using the scala toolbox compiler, it fails. (stacktrace follows the example below).

This code demonstrated what I mean:

import scala.reflect.runtime.universe
import scala.tools.reflect.ToolBox
object Issue extends App {
  val mirror = universe.runtimeMirror(getClass.getClassLoader)
  val toolbox = ToolBox(mirror).mkToolBox()
  println                  (com.test.Outer.Inner.VAL) // prints 'Inner'
  val tree = toolbox.parse("com.test.Outer.Inner.VAL")
  val compiled = toolbox.compile(tree)() // fails to compile
}

This is the the exception trace I get:

Exception in thread "main" scala.tools.reflect.ToolBoxError: reflective compilation has failed: 

value Inner is not a member of object com.test.Outer
    at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal.throwIfErrors(ToolBoxFactory.scala:319)
    at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal.compile(ToolBoxFactory.scala:252)
    at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl.compile(ToolBoxFactory.scala:416)
    at Issue$delayedInit$body.apply(Issue.scala:8)
    at scala.Function0$class.apply$mcV$sp(Function0.scala:40)
    at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)
    at scala.App$$anonfun$main$1.apply(App.scala:71)
    at scala.App$$anonfun$main$1.apply(App.scala:71)
    at scala.collection.immutable.List.foreach(List.scala:318)
    at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:32)
    at scala.App$class.main(App.scala:71)
    at Issue$.main(Issue.scala:3)
    at Issue.main(Issue.scala)

Has anyone encountered this before? Is there anything I can change (i.e compiler options) to make this work?

Thanks.

EDIT

This is what I get when I compile with "-verbose -Yissue-debug":

[reflect-compiler] made Scala package scala
[reflect-compiler] unpickling Scala package package and package scala, owner = package scala
[reflect-compiler] made Scala package java
[reflect-compiler] made Scala package lang
[reflect-compiler] made Scala package runtime
[reflect-compiler] made Scala package annotation
[reflect-compiler] unpickling Scala package package and package runtime, owner = package runtime
[reflect-compiler] made Scala package collection
[reflect-compiler] unpickling Scala package package and package collection, owner = package collection
[reflect-compiler] translating reflection info for Java class java.lang.Object
[reflect-compiler] unpickling Scala class Unit and object Unit, owner = package scala
[reflect-compiler] translating reflection info for Java class java.lang.Throwable
[reflect-compiler] unpickling Scala class Long and object Long, owner = package scala
[reflect-compiler] translating reflection info for Java class java.lang.InterruptedException
[reflect-compiler] unpickling Scala class Int and object Int, owner = package scala
[reflect-compiler] unpickling Scala class Boolean and object Boolean, owner = package scala
[reflect-compiler] translating reflection info for Java class java.lang.String
[reflect-compiler] translating reflection info for Java class java.lang.CloneNotSupportedException
[reflect-compiler] made Scala package io
[reflect-compiler] translating reflection info for Java interface java.io.Serializable
[reflect-compiler] translating reflection info for Java interface java.lang.CharSequence
[reflect-compiler] unpickling Scala class Char and object Char, owner = package scala
[reflect-compiler] translating reflection info for Java class java.io.ObjectStreamField
[reflect-compiler] made Scala package util
[reflect-compiler] unpickling Scala class Float and object Float, owner = package scala
[reflect-compiler] unpickling Scala class Double and object Double, owner = package scala
[reflect-compiler] unpickling Scala class Byte and object Byte, owner = package scala
[reflect-compiler] translating reflection info for Java class java.lang.StringBuffer
[reflect-compiler] translating reflection info for Java class java.util.Locale
[reflect-compiler] translating reflection info for Java class java.io.UnsupportedEncodingException
[reflect-compiler] made Scala package nio
[reflect-compiler] made Scala package charset
[reflect-compiler] translating reflection info for Java class java.nio.charset.Charset
[reflect-compiler] translating reflection info for Java class java.lang.StringBuilder
[reflect-compiler] translating reflection info for Java interface java.lang.Comparable
[reflect-compiler] made Scala package beans
parsing com.test.Outer.Inner.VAL
importing com.test.Outer.Inner.VAL
compiling com.test.Outer.Inner.VAL
[reflect-compiler] unpickling Scala class Predef and object Predef, owner = package scala
[reflect-compiler] unpickling Scala class ClassfileAnnotation and object ClassfileAnnotation, owner = package annotation
[reflect-compiler] unpickling Scala class Annotation and object Annotation, owner = package annotation
[reflect-compiler] unpickling Scala class StaticAnnotation and object StaticAnnotation, owner = package annotation
[reflect-compiler] made Scala package reflect
[reflect-compiler] unpickling Scala package package and package reflect, owner = package reflect
[reflect-compiler] made Scala package api
[reflect-compiler] unpickling Scala package package and package api, owner = package api
[reflect-compiler] unpickling Scala class Universe and object Universe, owner = package api
[reflect-compiler] unpickling Scala class Symbols and object Symbols, owner = package api
[reflect-compiler] unpickling Scala class Types and object Types, owner = package api
[reflect-compiler] unpickling Scala class FlagSets and object FlagSets, owner = package api
[reflect-compiler] unpickling Scala class Scopes and object Scopes, owner = package api
[reflect-compiler] unpickling Scala class Names and object Names, owner = package api
[reflect-compiler] unpickling Scala class Trees and object Trees, owner = package api
[reflect-compiler] unpickling Scala class Constants and object Constants, owner = package api
[reflect-compiler] unpickling Scala class Annotations and object Annotations, owner = package api
[reflect-compiler] unpickling Scala class Positions and object Positions, owner = package api
[reflect-compiler] unpickling Scala class Exprs and object Exprs, owner = package api
[reflect-compiler] unpickling Scala class TypeTags and object TypeTags, owner = package api
[reflect-compiler] unpickling Scala class TagInterop and object TagInterop, owner = package api
[reflect-compiler] unpickling Scala class StandardDefinitions and object StandardDefinitions, owner = package api
[reflect-compiler] unpickling Scala class StandardNames and object StandardNames, owner = package api
[reflect-compiler] unpickling Scala class BuildUtils and object BuildUtils, owner = package api
[reflect-compiler] unpickling Scala class Mirrors and object Mirrors, owner = package api
[reflect-compiler] unpickling Scala class Printers and object Printers, owner = package api
[reflect-compiler] unpickling Scala class Importers and object Importers, owner = package api
[reflect-compiler] made Scala package runtime
[reflect-compiler] unpickling Scala package package and package runtime, owner = package runtime
[reflect-compiler] unpickling Scala class StringContext and object StringContext, owner = package scala
[reflect-compiler] unpickling Scala class Product and object Product, owner = package scala
[reflect-compiler] unpickling Scala class Equals and object Equals, owner = package scala
[reflect-compiler] unpickling Scala class Serializable and object Serializable, owner = package scala
[reflect-compiler] made Scala package immutable
[reflect-compiler] unpickling Scala package package and package immutable, owner = package immutable
[reflect-compiler] unpickling Scala class Array and object Array, owner = package scala
[reflect-compiler] translating reflection info for Java interface java.lang.Cloneable
[reflect-compiler] unpickling Scala class AnyVal and object AnyVal, owner = package scala
[reflect-compiler] unpickling Scala class NotNull and object NotNull, owner = package scala
[reflect-compiler] unpickling Scala class LowPriorityImplicits and object LowPriorityImplicits, owner = package scala
[reflect-compiler] made Scala package com
[reflect-compiler] unpickling Scala class Dynamic and object Dynamic, owner = package scala
[reflect-compiler] made Scala package test
[reflect-compiler] translating reflection info for Java class com.test.Outer
[reflect-compiler] *** missing: Inner/true/object Outer/false/class scala.reflect.runtime.SynchronizedOps$$anon$1
java.lang.Exception
    at scala.tools.nsc.typechecker.Contexts$Context.issue(Contexts.scala:399)
    at scala.tools.nsc.typechecker.ContextErrors$ErrorUtils$.issueTypeError(ContextErrors.scala:84)
    at scala.tools.nsc.typechecker.ContextErrors$ErrorUtils$.issueNormalTypeError(ContextErrors.scala:69)
    at scala.tools.nsc.typechecker.ContextErrors$TyperContextErrors$TyperErrorGen$.NotAMemberError(ContextErrors.scala:331)
    at scala.tools.nsc.typechecker.Typers$Typer.handleMissing$1(Typers.scala:4846)
    at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$typedSelect$1(Typers.scala:4851)
    at scala.tools.nsc.typechecker.Typers$Typer.typedSelectOrSuperCall$1(Typers.scala:4938)
    at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5551)
    at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5630)
    at scala.tools.nsc.typechecker.Typers$Typer.typedQualifier(Typers.scala:5710)
    at scala.tools.nsc.typechecker.Typers$Typer.typedQualifier(Typers.scala:5716)
    at scala.tools.nsc.typechecker.Typers$Typer.typedSelectOrSuperCall$1(Typers.scala:4918)
    at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5551)
    at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5630)
    at scala.tools.nsc.typechecker.Typers$Typer.transformedOrTyped(Typers.scala:5825)
    at scala.tools.nsc.typechecker.Typers$Typer.typedDefDef(Typers.scala:2250)
    at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5557)
    at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5630)
    at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$typedStat$1(Typers.scala:2921)
    at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$62.apply(Typers.scala:3025)
    at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$62.apply(Typers.scala:3025)
    at scala.collection.immutable.List.loop$1(List.scala:170)
    at scala.collection.immutable.List.mapConserve(List.scala:186)
    at scala.tools.nsc.typechecker.Typers$Typer.typedStats(Typers.scala:3025)
    at scala.tools.nsc.typechecker.Typers$Typer.typedTemplate(Typers.scala:1914)
    at scala.tools.nsc.typechecker.Typers$Typer.typedModuleDef(Typers.scala:1794)
    at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5572)
    at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5630)
    at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$typedStat$1(Typers.scala:2921)
    at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$62.apply(Typers.scala:3025)
    at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$62.apply(Typers.scala:3025)
    at scala.collection.immutable.List.loop$1(List.scala:170)
    at scala.collection.immutable.List.mapConserve(List.scala:186)
    at scala.tools.nsc.typechecker.Typers$Typer.typedStats(Typers.scala:3025)
    at scala.tools.nsc.typechecker.Typers$Typer.typedPackageDef$1(Typers.scala:5288)
    at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5575)
    at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5630)
    at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5687)
    at scala.tools.nsc.typechecker.Analyzer$typerFactory$$anon$3.apply(Analyzer.scala:99)
    at scala.tools.nsc.Global$GlobalPhase.applyPhase(Global.scala:463)
    at scala.tools.nsc.typechecker.Analyzer$typerFactory$$anon$3$$anonfun$run$1.apply(Analyzer.scala:91)
    at scala.tools.nsc.typechecker.Analyzer$typerFactory$$anon$3$$anonfun$run$1.apply(Analyzer.scala:91)
    at scala.collection.Iterator$class.foreach(Iterator.scala:727)
    at scala.collection.AbstractIterator.foreach(Iterator.scala:1157)
    at scala.tools.nsc.typechecker.Analyzer$typerFactory$$anon$3.run(Analyzer.scala:91)
    at scala.tools.nsc.Global$Run.compileUnitsInternal(Global.scala:1582)
    at scala.tools.nsc.Global$Run.compileUnits(Global.scala:1556)
    at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal.compile(ToolBoxFactory.scala:251)
    at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl.compile(ToolBoxFactory.scala:416)
    at Issue$delayedInit$body.apply(Issue.scala:8)
    at scala.Function0$class.apply$mcV$sp(Function0.scala:40)
    at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)
    at scala.App$$anonfun$main$1.apply(App.scala:71)
    at scala.App$$anonfun$main$1.apply(App.scala:71)
    at scala.collection.immutable.List.foreach(List.scala:318)
    at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:32)
    at scala.App$class.main(App.scala:71)
    at Issue$.main(Issue.scala:3)
    at Issue.main(Issue.scala)
Exception in thread "main" scala.tools.reflect.ToolBoxError: reflective compilation has failed: 

[namer in 31ms]
[packageobjects in 0ms]
value Inner is not a member of object com.test.Outer
[typer in 211ms]
[total in 247ms]
[reset] recursing in package <root>
    at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal.throwIfErrors(ToolBoxFactory.scala:319)
    at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal.compile(ToolBoxFactory.scala:252)
    at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl.compile(ToolBoxFactory.scala:416)
    at Issue$delayedInit$body.apply(Issue.scala:8)
    at scala.Function0$class.apply$mcV$sp(Function0.scala:40)
    at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)
    at scala.App$$anonfun$main$1.apply(App.scala:71)
    at scala.App$$anonfun$main$1.apply(App.scala:71)
    at scala.collection.immutable.List.foreach(List.scala:318)
    at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:32)
    at scala.App$class.main(App.scala:71)
    at Issue$.main(Issue.scala:3)
    at Issue.main(Issue.scala)

EDIT 2

I think this is a bug in the reflection API but i'm not sure. I've debugged the typing phase and have come up with the following:

After com.test.Outer has been assigned with an appropriate symbol, we reach the the typing phase of com.test.Outer.Inner.

There it looks up a symbol for Inner using scala.reflect.internal.Scopes.Scope#lookupEntry(Name).

Inspecting the looked upon scope reveals that it does contain a ScopeEntry for a ClassSymbol of Inner but the equality testing between the scope symbol name and methods name parameter is done using Object#equals (i.e. equals has not been overriden), and the two name instances are not the same.

One last note regarding the symbol's name and the passed name parameter is that I see they have the same index field. I'm not sure what this means, but I got the impression this means they refer to the same name in the name table, so they are equal (?) (There is one difference between the two though, one is TermName and the symbol's name is a TypeName).

Anyway, I'll probably open a bug Scala's bug tracker, just wanted to conclude things here.

Mistaken answered 15/4, 2013 at 11:54 Comment(4)
Try toolbox.parse("com.test.Outer#Inner.VAL")?Uplift
Yes, please, submit a bug, and I will take a look. Thanks for the detailed analysis!Chatav
@EugeneBurmako Thanks, just opened SI-7378Mistaken
Well the static Inner class is in fact a static member of the Outer class, not a member of the instance of Outer. Consult the documentation, perhaps there is another way to deal with static objects and fields?Crozier
W
-1

Correct - it looks like the Scala / JVM reflection API is not able to determine that VAR is a static member of the nested Inner class.

Wembley answered 12/9, 2013 at 18:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.