Why is invokeDynamic in a class extending Dynamic not working? - scala

$ scala -Xexperimental
Welcome to Scala version 2.9.0.1 (OpenJDK Server VM, Java 1.6.0_22).
Type in expressions to have them evaluated.
Type :help for more information.
scala> class D extends Dynamic {
| def invokeDynamic(s:String)(args:Any*) = println(s)
| def doo() = { this hello }
| }
dynatype: this.applyDynamic("hello")()
dynatype: this.applyDynamic("applyDynamic")
...
This repeats a few dozen times....
...
dynatype: this.applyDynamic("applyDynamic")
java.lang.StackOverflowError
at scala.tools.nsc.symtab.Types$TypeMap$$anonfun$mapOverArgs$1.apply(Types.scala:3107)
at scala.tools.nsc.symtab.Types$TypeMap$$anonfun$mapOverArgs$1.apply(Types.scala:3103)
at scala.tools.nsc.symtab.Types$class.map2Conserve(Types.scala:4867)
at scala.tools.nsc.symtab.SymbolTable.map2Conserve(SymbolTable.scala:13)
at scala.tools.nsc.symtab.Types$TypeMap.mapOverArgs(Types.scala:3103)
at scala.tools.nsc.symtab.Types$TypeMap.mapOver(Types.scala:3010)
at scala.tools.nsc.symtab.Types$ApproximateDependentMap$.apply(Types.scala:3594)
at scala.tools.nsc.symtab.Types$ApproximateDependentMap$.apply(Types.scala:3591)
at scala.tools.nsc.symtab.Types$TypeMap$$anonfun$16.apply(Types.scala:3125)
at scala.tools.nsc.symtab.Types$TypeMap$$anonfun$16.apply(Types.scala:3122)
at scala.collection.LinearSeqOptimized$class.exists(LinearSeqOptimized.scala:79)
at scala.collection.immutable.List.exists(List.scala:45)
at scala.tools.nsc.symtab.Types$TypeMap.mapOver(Types.scala:3122)
at scala.tools.nsc.symtab.Types$TypeMap.mapOver(Types.scala:3024)
at scala.tools.nsc.symtab.Types$ApproximateDependentMap$.apply(Types.scala:3594)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.scala$tools$nsc$typechecker$Implicits$ImplicitSearch$$depoly(Implicits.scala:261)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch$ImplicitComputation.survives(Implicits.scala:619)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch$ImplicitComputation$$anonfun$6$$anonfun$7.apply(Implicits.scala:648)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch$ImplicitComputation$$anonfun$6$$anonfun$7.apply(Implicits.scala:648)
at scala.collection.TraversableLike$$anonfun$filter$1.apply(TraversableLike.scala:213)
at scala.collection.LinearSeqOptimized$class.foreach(LinearSeqOptimized.scala:59)
at scala.collection.immutable.List.foreach(List.scala:45)
at scala.collection.TraversableLike$class.filter(TraversableLike.scala:212)
at scala.collection.immutable.List.filter(List.scala:45)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch$ImplicitComputation$$anonfun$6.apply(Implicits.scala:648)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch$ImplicitComputation$$anonfun$6.apply(Implicits.scala:647)
at scala.collection.TraversableLike$$anonfun$flatMap$1.apply(TraversableLike.scala:200)
at scala.collection.TraversableLike$$anonfun$flatMap$1.apply(TraversableLike.scala:200)
at scala.collection.LinearSeqOptimized$class.foreach(LinearSeqOptimized.scala:59)
at scala.collection.immutable.List.foreach(List.scala:45)
at scala.collection.TraversableLike$class.flatMap(TraversableLike.scala:200)
at scala.collection.immutable.List.flatMap(List.scala:45)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch$ImplicitComputation.<init>(Implicits.scala:647)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.searchImplicit(Implicits.scala:753)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.bestImplicit(Implicits.scala:1084)
at scala.tools.nsc.typechecker.Implicits$class.inferImplicit(Implicits.scala:57)
at scala.tools.nsc.Global$analyzer$.inferImplicit(Global.scala:347)
at scala.tools.nsc.typechecker.Typers$Typer.wrapImplicit$1(Typers.scala:167)
at scala.tools.nsc.typechecker.Typers$Typer.inferView(Typers.scala:171)
at scala.tools.nsc.typechecker.Typers$Typer.adaptToMember(Typers.scala:985)
at scala.tools.nsc.typechecker.Typers$Typer.adaptToMemberWithArgs(Typers.scala:1024)
at scala.tools.nsc.typechecker.Typers$Typer.typedSelect$1(Typers.scala:3534)
at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:4123)
at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:4217)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$typedApply$1$1.apply(Typers.scala:3326)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$typedApply$1$1.apply(Typers.scala:3326)
at scala.tools.nsc.typechecker.Typers$Typer.silent(Typers.scala:623)
at scala.tools.nsc.typechecker.Typers$Typer.typedApply$1(Typers.scala:3326)
at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:4062)
at scala.tools.nsc.typechecker.Typers$Typer.typedSelect$1(Typers.scala:3554)
at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:4123)
at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:4217)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$typedApply$1$1.apply(Typers.scala:3326)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$typedApply$1$1.apply(Typers.scala:3326)
at scala.tools.nsc.typechecker.Typers$Typer.silent(Typers.scala:623)
at scala.tools.nsc.typechecker.Typers$Typer.typedApply$1(Typers.scala:3326)
at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:4062)
at scala.tools.nsc.typechecker.Typers$Typer.typedSelect$1(Typers.scala:3554)
at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:4123)
at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:4217)
at
And so on...until finally:
The repl compiler has crashed spectacularly. Shall I replay your
session? I can re-run all lines except the last one.
[y/n]

It crashes the compiler because it can't find the right method to handle your code.
I agree that it shouldn't crash the compiler, but should return an useful error message instead.
Note that it works correctly with the right method name applyDynamic:
scala> class D extends Dynamic {
def applyDynamic(s: String)(i: Int) {
println("Called "+ s +" with "+ i)
}
}
defined class D
scala> val d = new D
d: D = D#3b2601c
scala> d hello 42
dynatype: $line33.$read.$iw.$iw.d.applyDynamic("hello")
Called hello with 42

Very weird. I think it shouldn't compile, because you're supposed to implement applyDynamic and not invokeDynamic. This one works for me:
class D extends Dynamic {
def applyDynamic(s: String)(args: Any*) = println(s)
def doo() = { this.hello }
}
Probably invokeDynamic somehow messes with the synthetic stuff...?

Related

Scala 3 compiler plugin generating expected code but failing at runtime

I'm trying to get started with writing a compiler plugin for scala 3. At this stage, it's primarily based on https://github.com/liufengyun/scala3-plugin-example/blob/master/plugin/src/main/scala/Phases.scala (and the accompanying youtube video explaining how it works).
It's been an interesting process so far, and I'm getting a bit of a feel for some aspects of the compiler.
As a first step, I'm simply trying to wrap a method body into a block, print whatever the returned object was going to be, and then return the object.
This differs from the original plugin mainly in that there was a single side-effecting method call added to each method - this is also assigning a local variable, (which I think is probably the cause of the problem), and moving the method body into a block.
I've produced as minimal of a working example as I could in a fork here: https://github.com/robmwalsh/scala3-plugin-example
The plugin compiles fine, seems to run as part of compilation as expected, and then blows up at runtime. I'm not entirely sure if this is me doing something wrong (not unlikely) or a bug in the compiler (less likely, but a distinct possibility!).
Can anybody please shed some light on why this isn't working? I don't know what flags should be set when creating a new Symbol, so that's one possibility, but there's heaps of stuff that sorta seemed to work so I rolled with it.
Here's where I'm at (the interesting bits):
...
override def prepareForUnit(tree: Tree)(using ctx: Context): Context =
//find the printLn method
val predef = requiredModule("scala.Predef")
printlnSym = predef.requiredMethod("println", List(defn.AnyType))
ctx
override def transformDefDef(tree: DefDef)(using ctx: Context): Tree =
val sym = tree.symbol
// ignore abstract and synthetic methods
if tree.rhs.isEmpty|| sym.isOneOf(Synthetic | Deferred | Private | Accessor)
then return tree
try {
println("\n\n\n\n")
println("========================== tree ==========================")
println(tree.show)
// val body = {tree.rhs}
val body = ValDef(
newSymbol(
tree.symbol, termName("body"), tree.symbol.flags, tree.rhs.tpe),
Block(Nil, tree.rhs)
)
// println(body)
val bodyRef = ref(body.symbol)
val printRes = ref(printlnSym).appliedTo(bodyRef)
// shove it all together in a block
val rhs1 = tpd.Block(body :: printRes :: Nil, bodyRef)
//replace RHS with new
val newDefDef = cpy.DefDef(tree)(rhs = rhs1)
println("====================== transformed ======================")
println(newDefDef.show)
newDefDef
} catch {
case e =>
println("====================== error ===========================")
println(e)
println(e.printStackTrace)
tree
}
...
test program for compiler plugin
object Test extends App:
def foo: String = "forty two"
def bar(x: String): Int = x.length
def baz(x: String, y: Int): String = x + y
baz(foo, bar(foo))
output during compile using plugin (exactly what I wanted! I got very excited at this point)
========================== tree ==========================
def foo: String = "forty two"
====================== transformed ======================
def foo: String =
{
val body: ("forty two" : String) =
{
"forty two"
}
println(body)
body
}
========================== tree ==========================
def bar(x: String): Int = x.length()
====================== transformed ======================
def bar(x: String): Int =
{
val body: Int =
{
x.length()
}
println(body)
body
}
========================== tree ==========================
def baz(x: String, y: Int): String = x.+(y)
====================== transformed ======================
def baz(x: String, y: Int): String =
{
val body: String =
{
x.+(y)
}
println(body)
body
}
output during runtime :'( (this changes depending on the code it's running on, but always the same theme)
Exception in thread "main" java.lang.VerifyError: Bad local variable type
Exception Details:
Location:
testing/Test$.body$2()I #0: aload_1
Reason:
Type top (current frame, locals[1]) is not assignable to reference type
Current Frame:
bci: #0
flags: { }
locals: { 'testing/Test$' }
stack: { }
Bytecode:
0000000: 2bb6 007d ac
at testing.Test.main(Example.scala)
Edit: I'm using scala 3.1.2
I was using the existing flags when creating my new symbol. Instead, I needed to use the Local flag, which I suppose makes sense.

Test a nested method call on a mocked class using ScalaMock

I am new to both ScalaMock and mocking in general. I am trying to test a method which calls a method in another (mocked) class and then calls a method on the returned object.
Detailed information:
So I am using ScalaTest and there are five classes involved in this test...
SubInstruction which I am testing
class SubInstruction(label: String, val result: Int, val op1: Int, val op2: Int) extends Instruction(label, "sub") {
override def execute(m: Machine) {
val value1 = m.regs(op1)
val value2 = m.regs(op2)
m.regs(result) = value1 - value2
}
}
object SubInstruction {
def apply(label: String, result: Int, op1: Int, op2: Int) =
new SubInstruction(label, result, op1, op2)
}
Machine which must be mocked for the test
case class Machine(labels: Labels, prog: Vector[Instruction]) {
private final val NUMBEROFREGISTERS = 32
val regs: Registers = new Registers(NUMBEROFREGISTERS)
override def toString(): String = {
prog.foldLeft("")(_ + _)
}
def execute(start: Int) =
start.until(prog.length).foreach(x => prog(x) execute this)
}
object Machine extends App {
if (args.length == 0) {
println("Machine: args should be sml code file to execute")
} else {
println("SML interpreter - Scala version")
val m = Translator(args(0)).readAndTranslate(new Machine(Labels(), Vector()))
println("Here is the program; it has " + m.prog.size + " instructions.")
println(m)
println("Beginning program execution.")
m.execute(0)
println("Ending program execution.")
println("Values of registers at program termination:")
println(m.regs + ".")
}
}
Registers which is required to construct a Machine object
case class Registers(size: Int) {
val registers: Array[Int] = new Array(size)
override def toString(): String =
registers.mkString(" ")
def update(k: Int, v: Int) = registers(k) = v
def apply(k: Int) = registers(k)
}
MockableMachine which I have created as the original Machine class does not have an empty constructor and therefore (as I understand) can not be mocked
class MockableMachine extends Machine(Labels(), Vector()){
}
and finally my test class SubInstructionTest which compiles but throws the exception below.
class SubInstructionTest extends FlatSpec with MockFactory with Matchers {
val label1 = "f0"
val result1 = 25
val op1_1 = 24
val op2_1 = 20
val sub1 = SubInstruction(label1, result1, op1_1, op2_1)
"A SubInstruction" should "retrieve the operands from the correct registers in the given machine " +
"when execute(m: Machine) is called, and perform the operation saving the " +
"result in the correct register." in {
val mockMachine = mock[MockableMachine]
inSequence {
(mockMachine.regs.apply _).expects(op1_1).returning(50)
(mockMachine.regs.apply _).expects(op2_1).returning(16)
(mockMachine.regs.update _).expects(result1, 34)
}
sub1.execute(mockMachine)
}
}
Throws:
java.lang.NoSuchMethodException: Registers.mock$apply$0()
-
I have been searching for a straightforward way to mock this class for hours, but have found nothing. For the time being I have settled on the workaround detailed below, but I was under the impression that mocking would offer a less convoluted solution to the problem of testing my SubInstruction class.
The workaround:
Delete the MockableMachine class and create a CustomMachine class which extends Machine and replaces the registers value with mockedRegisters provided at construction time.
class CustomMachine (mockedRegister: Registers) extends Machine(Labels(), Vector()) {
override
val regs: Registers = mockedRegister
}
a MockableRegisters class which I have created as the original does not have an empty constructor and therefore (as I understand) can not be mocked
class MockableRegisters extends Registers(32) {
}
and the SubInstructionTest class written in a slightly different way
class SubInstructionTest extends FlatSpec with MockFactory with Matchers {
val label1 = "f0"
val result1 = 25
val op1_1 = 24
val op2_1 = 20
val sub1 = SubInstruction(label1, result1, op1_1, op2_1)
"A SubInstruction" should "retrieve the operands from the correct registers in the given machine " +
"when execute(m: Machine) is called, and perform the operation saving the " +
"result in the correct register." in {
val mockRegisters = mock[MockableRegisters]
val machine = new CustomMachine(mockRegisters)
inSequence {
(mockRegisters.apply _).expects(op1_1).returning(50)
(mockRegisters.apply _).expects(op2_1).returning(16)
(mockRegisters.update _).expects(result1, 34)
}
sub1.execute(machine)
}
}
As indicated, this feels like a workaround to me, is there not a simpler way to do this (perhaps similar to my original attempt)?
I have just included the essential code to ask the question, but you can find the full code on my GitHub account.
I don't think mocking nested objects is supported by Scalamock implicitly. You'll have to mock the object returned by the first call which is what your working example does.
FWIW, Mockito supports this. Search for RETURNS_DEEP_STUBS.

How to compile this Scala code

I have a file called sumit.scala with the following contents
object sumit {
def main(args: Array[String]) = {
val start:Double = System.nanoTime
total_select_values(1 to 15000, {e => true})
val end:Double = System.nanoTime
println("time " + (end - start)/ 1000000000.0)
println("")
}
}
def total_select_values(list: Range, selector : Int => Boolean) = {
var sum = 0
list.foreach { e =>
if (selector(e)) sum += e
}
sum
}
i'm trying to compile it on the command line
scalac sumit.scala
which compiles without error but when i run it
scala sumit
i get a bunch of errors, i'm new to scala and i'm just trying to time this code once it's compiled to see the performance difference. I've tried putting my "total_select_values" in the object and out (as shown here) with no difference.
Thanks for any help!
Updated with Scala info and the actual error
Scala version 2.11.4 Java 1.7.0_40
java.lang.NoSuchMethodException: sumit.main([Ljava.lang.String;)
at java.lang.Class.getMethod(Unknown Source)
at scala.reflect.internal.util.ScalaClassLoader$class.run(ScalaClassLoader.scala:66)
at scala.reflect.internal.util.ScalaClassLoader$URLClassLoader.run(ScalaClassLoader.scala:101)
at scala.tools.nsc.CommonRunner$class.run(ObjectRunner.scala:22)
at scala.tools.nsc.ObjectRunner$.run(ObjectRunner.scala:39)
at scala.tools.nsc.CommonRunner$class.runAndCatch(ObjectRunner.scala:29)
at scala.tools.nsc.ObjectRunner$.runAndCatch(ObjectRunner.scala:39)
at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:65)
at scala.tools.nsc.MainGenericRunner.run$1(MainGenericRunner.scala:87)
at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:98)
at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:103)
at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)
The function
def total_select_values ...
Has to go inside an object or class. This appears to be a constraint of Scala based on the
JVM; can't have true free functions.
Try to run it like this
scala sumit.scala
i.e., add the extension of the file.
The code you have gives me the following error when I run scalac:
sumit.scala:11: error: expected class or object definition
def total_select_values(list: Range, selector : Int => Boolean) = {
^
one error found
But, if I change the code to put total_select_values inside the sumit object (as suggested in the comments):
object sumit {
def main(args: Array[String]) = {
val start:Double = System.nanoTime
total_select_values(1 to 15000, {e => true})
val end:Double = System.nanoTime
println("time " + (end - start)/ 1000000000.0)
println("")
}
def total_select_values(list: Range, selector : Int => Boolean) = {
var sum = 0
list.foreach { e =>
if (selector(e)) sum += e
}
sum
}
}
Then, when I run:
scalac sumit.scala
scala sumit
it produces:
time 0.003286401time 0.003286401
But, also, since scala can be run interactively. Just running:
scala sumit.scala
also works. (The scalac step can be left out.)

Possible to conditionally elide out a subclass method?

In Scala, I'd like to have a subclass method conditionally elided out (and revert to the base class implementation) based on the command line priority. As an example, suppose I have the following code:
// File: mini.scala
import scala.annotation._, elidable._
trait FooBase {
def bar(msg: String) = println("FooBase: " + msg)
}
object Foo extends FooBase {
#elidable(INFO)
override def bar(msg: String) = println("Foo: " + msg)
}
object App {
def main(args: Array[String]) {
println("before")
Foo.bar("message")
println("after")
}
}
If I compile with:
scalac -Xelide-below MINIMUM mini.scala
And run it, I see (as expected):
before
Foo: message
after
Now if I compile with:
scalac -Xelide-below MAXIMUM mini.scala
Then, I hoped to see:
before
FooBase: message
after
But I actually see:
before
after
So instead of just the subclass method being elided out (as hoped) the base class seems to be gone too.
Any explanation appreciated.
Based on the response from #som-snytt, it seems that the best way to achieve the effect I'm after is something like:
trait DebugBase {
def on = false
}
object Debug extends DebugBase {
#elidable(INFO)
override def on = true
}
object Foo {
def bar(msg: String) = if (Debug.on) println("Foo (Debug): " + msg) else println("Foo: " + msg)
}
But then there is still a runtime check on the status of Debug.on instead of completely eliding out the debug version as you might with an #ifdef in C/C++.
That's a good one. I've looked at the mechanism once upon a time, but I wasn't sure about the use case.
The answer is commented:
/** Called if a tree's symbol is elidable. If it's a DefDef,
* replace only the body/rhs with 0/false/()/null; otherwise replace
* the whole tree with it.
*/
So in eliding Foo.bar, you don't remove the method definition. You merely turn it into def bar = () and any invocation into the unit value ().
You can elide a template method usefully:
scala> class X { def f() = { if (g() < 1) println("default") } ; def g() = 1 }
defined class X
scala> :se -Xelide-below 200
scala> class Y extends X { #elidable(100) override def g() = 2 }
defined class Y
scala> new Y().f()
default
scala> :se -Xelide-below 5
scala> class Y extends X { #elidable(100) override def g() = 2 }
defined class Y
scala> new Y().f()

code error in code sample from "Beginning Scala"

trying to run the sample code in the Apress book called "Beginning Scala". I even downloaded the code from their website to make sure I didn't goof. Getting the following message:
/root/sum.scala:19: error: missing arguments for method collect in trait Iterator;
follow this method with `_' if you want to treat it as a partially applied function
val lines = input.getLines.collect
^
one error found
and here is the source code i used (running Scala version 2.8.1.final (Java HotSpot(TM) Server VM, Java 1.6.0_22 on Fedora 13)
import scala.io._
def toInt(in: String): Option[Int] =
try {
Some(Integer.parseInt(in.trim))
} catch {
case e: NumberFormatException => None
}
def sum(in: Seq[String]) = {
val ints = in.flatMap(s => toInt(s))
ints.foldLeft(0)((a, b) => a + b)
}
println("Enter some numbers and press ctrl-D (Unix/Mac) ctrl-C (Windows)")
val input = Source.fromInputStream(System.in)
val lines = input.getLines.collect
println("Sum "+sum(lines))
looks like this is the relevant change:
The Iterator.collect() method in 2.7.7 returns a Seq. In 2.8, it is used to perform a conditional map using a PartialFunction. You can use input.getLines.toSeq instead.
Ah, I remember this:
EDIT: replaced with more in depth answer
The code was written against Scala
2.7.3 and 2.8 introduces some breaking changes.
Here's an update to the code that
works under Scala 2.8.0:
import scala.io._
object Sum {
def main(args: Array[String]): Unit = {
println("Enter some numbers and press ctrl-D (Unix/Mac) ctrl-Z (Windows)")
val input = Source.fromInputStream(System.in)
val lines = input.getLines.toList
println("Sum " + sum(lines))
}
def toInt(s: String): Option[Int] = {
try {
Some(Integer.parseInt(s))
} catch {
case e: NumberFormatException => None
}
}
def sum(in: Seq[String]): Int = {
val ints = in.flatMap(toInt(_))
ints.foldLeft(0)((a, b) => a + b)
}
}
Source: http://scala-programming-language.1934581.n4.nabble.com/Beginning-Scala-book-problem-td2966867.html