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 constructor of java defined class in reflect api #18694

Open
goshacodes opened this issue Oct 13, 2023 · 2 comments
Open

Can't get constructor of java defined class in reflect api #18694

goshacodes opened this issue Oct 13, 2023 · 2 comments
Labels
area:metaprogramming:reflection Issues related to the quotes reflection API itype:bug itype:question

Comments

@goshacodes
Copy link

goshacodes commented Oct 13, 2023

Compiler version

3.3.0

Description

I'm trying to extend a java class using reflect api.
To create a symbol for a class I need parent, either TypeTree or Term. TypeTree is not working here, so I'am trying to get a constructor and apply nulls as arguments

Minimized code

public class JavaClassWithBridgeMethod {
    public String overloadedMethod(String a) { return "bar1"; }
}
def macro[T: Type](using quotes: Quotes): Expr[Any] =
  import quotes.reflect.*
  val tpe = TypeRepr.of[T]
  ...

macro[JavaClassWithBridgeMethod]

When I'm trying to use tpe.typeSymbol.primaryConstructor compiler crushes with this:

[error] java.lang.AssertionError: assertion failed: private constructor JavaClassWithBridgeMethod in class JavaClassWithBridgeMethod in /Users/g.kovalev/IdeaProjects/ScalaMock/jvm/src/test/java/com/paulbutcher/test/JavaClassWithBridgeMethod.java accessed from constructor $anon in /Users/g.kovalev/IdeaProjects/ScalaMock/jvm/src/test/scala/com.paulbutcher.test/mock/JavaMocksTest.scala
[error] scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:8)
[error] dotty.tools.dotc.transform.ExpandPrivate.ensurePrivateAccessible(ExpandPrivate.scala:88)
[error] dotty.tools.dotc.transform.ExpandPrivate.transformSelect(ExpandPrivate.scala:98)
[error] dotty.tools.dotc.transform.ExpandPrivate.transformSelect(ExpandPrivate.scala:97)
[error] dotty.tools.dotc.transform.MegaPhase.goSelect(MegaPhase.scala:605)
[error] dotty.tools.dotc.transform.MegaPhase.transformNamed$1(MegaPhase.scala:228)
[error] dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:425)
[error] dotty.tools.dotc.transform.MegaPhase.transformUnnamed$1(MegaPhase.scala:278)
[error] dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:427)
[error] dotty.tools.dotc.transform.MegaPhase.loop$2(MegaPhase.scala:444)
[error] dotty.tools.dotc.transform.MegaPhase.transformBlock(MegaPhase.scala:449)
[error] dotty.tools.dotc.transform.MegaPhase.transformUnnamed$1(MegaPhase.scala:298)
[error] dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:427)
[error] dotty.tools.dotc.transform.MegaPhase.mapDefDef$1(MegaPhase.scala:248)
[error] dotty.tools.dotc.transform.MegaPhase.transformNamed$1(MegaPhase.scala:251)
[error] dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:425)
[error] dotty.tools.dotc.transform.MegaPhase.transformSpecificTree(MegaPhase.scala:434)
[error] dotty.tools.dotc.transform.MegaPhase.transformUnnamed$1(MegaPhase.scala:356)
[error] dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:427)
[error] dotty.tools.dotc.transform.MegaPhase.transformNamed$1(MegaPhase.scala:255)
[error] dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:425)
[error] dotty.tools.dotc.transform.MegaPhase.loop$1(MegaPhase.scala:438)
[error] dotty.tools.dotc.transform.MegaPhase.transformStats(MegaPhase.scala:438)
[error] dotty.tools.dotc.transform.MegaPhase.transformUnnamed$1(MegaPhase.scala:359)
[error] dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:427)
[error] dotty.tools.dotc.transform.MegaPhase.transformNamed$1(MegaPhase.scala:255)
[error] dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:425)
[error] dotty.tools.dotc.transform.MegaPhase.loop$1(MegaPhase.scala:438)
[error] dotty.tools.dotc.transform.MegaPhase.transformStats(MegaPhase.scala:438)
[error] dotty.tools.dotc.transform.MegaPhase.mapPackage$1(MegaPhase.scala:379)
[error] dotty.tools.dotc.transform.MegaPhase.transformUnnamed$1(MegaPhase.scala:382)
[error] dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:427)
[error] dotty.tools.dotc.transform.MegaPhase.transformUnit(MegaPhase.scala:454)
[error] dotty.tools.dotc.transform.MegaPhase.run(MegaPhase.scala:466)
[error] dotty.tools.dotc.core.Phases$Phase.runOn$$anonfun$1(Phases.scala:324)
[error] scala.collection.immutable.List.map(List.scala:250)
[error] dotty.tools.dotc.core.Phases$Phase.runOn(Phases.scala:328)
[error] dotty.tools.dotc.Run.runPhases$1$$anonfun$1(Run.scala:247)
[error] scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
[error] scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
[error] scala.collection.ArrayOps$.foreach$extension(ArrayOps.scala:1321)
[error] dotty.tools.dotc.Run.runPhases$1(Run.scala:263)
[error] dotty.tools.dotc.Run.compileUnits$$anonfun$1(Run.scala:271)
[error] dotty.tools.dotc.Run.compileUnits$$anonfun$adapted$1(Run.scala:280)
[error] dotty.tools.dotc.util.Stats$.maybeMonitored(Stats.scala:67)
[error] dotty.tools.dotc.Run.compileUnits(Run.scala:280)
[error] dotty.tools.dotc.Run.compileSources(Run.scala:195)
[error] dotty.tools.dotc.Run.compile(Run.scala:179)
[error] dotty.tools.dotc.Driver.doCompile(Driver.scala:35)
[error] dotty.tools.xsbt.CompilerBridgeDriver.run(CompilerBridgeDriver.java:88)
[error] dotty.tools.xsbt.CompilerBridge.run(CompilerBridge.java:22)
[error] sbt.internal.inc.AnalyzingCompiler.compile(AnalyzingCompiler.scala:91)
[error] sbt.internal.inc.MixedAnalyzingCompiler.$anonfun$compile$7(MixedAnalyzingCompiler.scala:193)
[error] scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
[error] sbt.internal.inc.MixedAnalyzingCompiler.timed(MixedAnalyzingCompiler.scala:248)
[error] sbt.internal.inc.MixedAnalyzingCompiler.$anonfun$compile$4(MixedAnalyzingCompiler.scala:183)
[error] sbt.internal.inc.MixedAnalyzingCompiler.$anonfun$compile$4$adapted(MixedAnalyzingCompiler.scala:163)
[error] sbt.internal.inc.JarUtils$.withPreviousJar(JarUtils.scala:239)
[error] sbt.internal.inc.MixedAnalyzingCompiler.compileScala$1(MixedAnalyzingCompiler.scala:163)
[error] sbt.internal.inc.MixedAnalyzingCompiler.compile(MixedAnalyzingCompiler.scala:211)
[error] sbt.internal.inc.IncrementalCompilerImpl.$anonfun$compileInternal$1(IncrementalCompilerImpl.scala:534)
[error] sbt.internal.inc.IncrementalCompilerImpl.$anonfun$compileInternal$1$adapted(IncrementalCompilerImpl.scala:534)
[error] sbt.internal.inc.Incremental$.$anonfun$apply$5(Incremental.scala:179)
[error] sbt.internal.inc.Incremental$.$anonfun$apply$5$adapted(Incremental.scala:177)
[error] sbt.internal.inc.Incremental$$anon$2.run(Incremental.scala:463)
[error] sbt.internal.inc.IncrementalCommon$CycleState.next(IncrementalCommon.scala:116)
[error] sbt.internal.inc.IncrementalCommon$$anon$1.next(IncrementalCommon.scala:56)
[error] sbt.internal.inc.IncrementalCommon$$anon$1.next(IncrementalCommon.scala:52)
[error] sbt.internal.inc.IncrementalCommon.cycle(IncrementalCommon.scala:263)
[error] sbt.internal.inc.Incremental$.$anonfun$incrementalCompile$8(Incremental.scala:418)
[error] sbt.internal.inc.Incremental$.withClassfileManager(Incremental.scala:505)
[error] sbt.internal.inc.Incremental$.incrementalCompile(Incremental.scala:405)
[error] sbt.internal.inc.Incremental$.apply(Incremental.scala:171)
[error] sbt.internal.inc.IncrementalCompilerImpl.compileInternal(IncrementalCompilerImpl.scala:534)
[error] sbt.internal.inc.IncrementalCompilerImpl.$anonfun$compileIncrementally$1(IncrementalCompilerImpl.scala:488)
[error] sbt.internal.inc.IncrementalCompilerImpl.handleCompilationError(IncrementalCompilerImpl.scala:332)
[error] sbt.internal.inc.IncrementalCompilerImpl.compileIncrementally(IncrementalCompilerImpl.scala:425)
[error] sbt.internal.inc.IncrementalCompilerImpl.compile(IncrementalCompilerImpl.scala:137)
[error] sbt.Defaults$.compileIncrementalTaskImpl(Defaults.scala:2363)
[error] sbt.Defaults$.$anonfun$compileIncrementalTask$2(Defaults.scala:2313)
[error] sbt.internal.server.BspCompileTask$.$anonfun$compute$1(BspCompileTask.scala:30)
[error] sbt.internal.io.Retry$.apply(Retry.scala:46)
[error] sbt.internal.io.Retry$.apply(Retry.scala:28)
[error] sbt.internal.io.Retry$.apply(Retry.scala:23)
[error] sbt.internal.server.BspCompileTask$.compute(BspCompileTask.scala:30)
[error] sbt.Defaults$.$anonfun$compileIncrementalTask$1(Defaults.scala:2311)
[error] scala.Function1.$anonfun$compose$1(Function1.scala:49)
[error] sbt.internal.util.$tilde$greater.$anonfun$$u2219$1(TypeFunctions.scala:62)
[error] sbt.std.Transform$$anon$4.work(Transform.scala:68)
[error] sbt.Execute.$anonfun$submit$2(Execute.scala:282)
[error] sbt.internal.util.ErrorHandling$.wideConvert(ErrorHandling.scala:23)
[error] sbt.Execute.work(Execute.scala:291)
[error] sbt.Execute.$anonfun$submit$1(Execute.scala:282)
[error] sbt.ConcurrentRestrictions$$anon$4.$anonfun$submitValid$1(ConcurrentRestrictions.scala:265)
[error] sbt.CompletionService$$anon$2.call(CompletionService.scala:64)
[error] java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
[error] java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:577)
[error] java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
[error] java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
[error] java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)

So instead of trying to use primary constructor, I am trying to use another public one and I got this one.

wrong number of arguments at dropBreaks for (x$1: Unit): com.paulbutcher.test.JavaClassWithBridgeMethod: (com.paulbutcher.test.JavaClassWithBridgeMethod#<init> :
[error]    |  (x$1: Unit): com.paulbutcher.test.JavaClassWithBridgeMethod), expected: 1, found: 0

After it I am trying to pass there a () as argument and it gives me another error:

wrong number of arguments at pickler for (): com.paulbutcher.test.JavaClassWithBridgeMethod: (com.paulbutcher.test.JavaClassWithBridgeMethod#<init> :
[error]    |  (): com.paulbutcher.test.JavaClassWithBridgeMethod), expected: 0, found: 1
scala.MatchError: val <none> (of class dotty.tools.dotc.core.Symbols$NoSymbol$)
@goshacodes goshacodes added itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label labels Oct 13, 2023
@nicolasstucki nicolasstucki added itype:question area:metaprogramming:reflection Issues related to the quotes reflection API itype:bug and removed stat:needs triage Every issue needs to have an "area" and "itype" label itype:bug labels Oct 16, 2023
@nicolasstucki
Copy link
Contributor

@goshacodes could you provide the implementation of the macro?

@goshacodes
Copy link
Author

goshacodes commented Oct 16, 2023

Implementation is here:
https://github.com/goshacodes/ScalaMock/blob/scala_3/shared/src/main/scala-3/org/scalamock/clazz/MockMaker.scala

You can also reproduce it:

  1. pull and go to branch scala_3: https://github.com/goshacodes/ScalaMock/tree/scala_3
  2. find JavaMocksTest.scala
  3. change line 82 to
      val m = mock[JavaClassWithBridgeMethod]

compile and you will get an java.lang.AssertionError from description

  1. Now change lines 38-50 to
    def asParent(tree: TypeTree): TypeTree | Term =
      val typeSymbol = tpe.dealias.typeSymbol
      val constructor = typeSymbol.declarations.filter(sym => sym.isClassConstructor && !sym.flags.is(Flags.Private))
        .sortBy(_ != typeSymbol.primaryConstructor)
        .headOption
        .getOrElse(report.errorAndAbort("Constructor not found"))

      val constructorFieldsFilledWithNulls: List[List[Term]] =
        constructor.paramSymss
          .filter(_.exists(!_.isType))
          .map(_.map(_.typeRef.asType match { case '[t] => '{ null.asInstanceOf[t] }.asTerm }))

      if constructorFieldsFilledWithNulls.forall(_.isEmpty) then
        tree
      else
        Select(
          New(TypeIdent(tree.tpe.typeSymbol)),
          constructor
        ).appliedToArgss(constructorFieldsFilledWithNulls)

and you will get wrong number of arguments at dropBreaks

  1. change constructorFieldsFilledWithNulls definition to
      val constructorFieldsFilledWithNulls: List[List[Term]] =
        if (constructor.flags.is(Flags.JavaDefined) && constructor != typeSymbol.primaryConstructor)
          List(List('{ () }.asTerm))
        else
          constructor.paramSymss
            .filter(_.exists(!_.isType))
            .map(_.map(_.typeRef.asType match { case '[t] => '{ null.asInstanceOf[t] }.asTerm }))

and you will get wrong number of arguments at pickler

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:metaprogramming:reflection Issues related to the quotes reflection API itype:bug itype:question
Projects
None yet
Development

No branches or pull requests

2 participants