Managing Binary Compatibility in Scala (Scala Days 2011)

14

Click here to load reader

description

The following is the abstract submitted for my Scala Days 2011 talk: Binary compatibility is not a topic specific to the Scala language, but rather a concern for all languages targeting the JVM, Java included. Scala shares with Java many sources of potential binary incompatibilities, however, because of Scala greater expressiveness, Scala code has unique sources of incompatibility. The Scala programming language offers several language constructs that do not have an equivalent in Java and are not natively supported by the JVM. Because of this, the Scala compiler (scalac) transforms these constructs into lower-lever, Java compatible, patterns that can be then easily translated into bytecode. Good examples of such high-level Scala constructs are traits, for mixin-based inheritance, and functions as first data citizens. During this presentation we will review the main sources of binary incompatibility for the Scala language, providing you with useful insights about how you should evolve your codebase to avoid binary incompatibilities. Furthermore, we will show a tool, the Migration Manager, that can be used to automatically diagnose binary incompatibilities between two versions of a same library.

Transcript of Managing Binary Compatibility in Scala (Scala Days 2011)

Page 1: Managing Binary Compatibility in Scala (Scala Days 2011)

Managing Binary Compatibility in Scala

Mirco Dotta

Typesafe

June 3, 2011

Mirco Dotta Managing Binary Compatibility in Scala

Page 2: Managing Binary Compatibility in Scala (Scala Days 2011)

Introduction Sources of Incompatibility Conclusion

Outline

IntroductionExampleScala vs. Java

Sources of IncompatibilityType InferencerTrait

Conclusion

Mirco Dotta Managing Binary Compatibility in Scala

Page 3: Managing Binary Compatibility in Scala (Scala Days 2011)

Introduction Sources of Incompatibility Conclusion Example Scala vs. Java

Example

Assume Analyzer is part of a library we produce. We decide thatits API has to evolve as follows:

class Analyzer { // old versiondef analyze(issues: HashMap[ , ]) {...}

}

class Analyzer { // new versiondef analyze(issues: Map[ , ]) {...}

}

Further, assume the next expression was compiled against the oldlibrary

new Analyzer().analyze(new HashMap[Any,Any]())

Would the compiled code work if run against the new library?

The answer lies in the bytecode...

Mirco Dotta Managing Binary Compatibility in Scala

Page 4: Managing Binary Compatibility in Scala (Scala Days 2011)

Introduction Sources of Incompatibility Conclusion Example Scala vs. Java

Example: Bytecode

Let’s take look at the generated bytecode for the two versions:

class Analyzer { // old versionanalyze(Lscala/collection/immutable/HashMap);V}}

class Analyzer { // new versionanalyze(Lscala/collection/immutable/Map);V}}

The expression compiled against the old library would look like:

...invokevirtual #9;// #9 == Analyzer.analyze:(Lscala/collection/immutable/HashMap;)V

⇒ The method’s name has been statically resolved atcompile-time.

Running it against the new library would result in the JVMthrowing a NoSuchMethodException.

⇒ The evolution of class Analyzer breaks compatibility withpre-existing binaries.

Mirco Dotta Managing Binary Compatibility in Scala

Page 5: Managing Binary Compatibility in Scala (Scala Days 2011)

Introduction Sources of Incompatibility Conclusion Example Scala vs. Java

Is Binary Compatibility a Scala issue?

The short answer is No. The discussed example can be easilyported in Java.

Scala shares with Java many sources of binary incompatibility.

But Scala offers many language features not available in Java:

I Type Inferencer

I First-class functions

I Multiple inheritance via mixin composition (i.e., traits)

. . . Just to cite a few.

⇒ Scala code has new “unique” sources of binary incompatibility.

Mirco Dotta Managing Binary Compatibility in Scala

Page 6: Managing Binary Compatibility in Scala (Scala Days 2011)

Introduction Sources of Incompatibility Conclusion Type Inferencer Trait

Type Inferencer: Member Signature

Does the following evolution break binary compatibility?

class TextAnalyzer { // old versiondef analyze(text: String) = {val issues = Map[String,Any]()// ...issues

}}

class TextAnalyzer { // new versiondef analyze(text: String) = {val issues = new HashMap[String,Any]()// ...issues

}}

QuestionWhat is the inferred return type of analyze?

Let’s compare the two methods’ signature.

class TextAnalyzer { // old versionpublic scala.collection.immutable.Mapanalyze(java.lang.String);

}

class TextAnalyzer { // new versionpublic scala.collection.immutable.HashMapanalyze(java.lang.String);

}

Mirco Dotta Managing Binary Compatibility in Scala

Page 7: Managing Binary Compatibility in Scala (Scala Days 2011)

Introduction Sources of Incompatibility Conclusion Type Inferencer Trait

Type Inferencer: Member Signature (2)

QuestionCan we prevent the method’s signature change?

That’s easy! The method’s return type has to be explicitlydeclared:

class TextAnalyzer { // bytecode compatible new versiondef analyze(text: String): Map[String,Any] = {val issues = new HashMap()// ...issues

}}

Take Home Message

Always declare the member’s type. If you don’t, you mayinadvertently change the member’s signature.

Mirco Dotta Managing Binary Compatibility in Scala

Page 8: Managing Binary Compatibility in Scala (Scala Days 2011)

Introduction Sources of Incompatibility Conclusion Type Inferencer Trait

Trait Compilation

Traits are a powerful language construct that enablesmultiple-inheritance on top of a runtime – the JVM – that doesnot natively support it.

Understanding how traits are compiled is crucial if you need toensure release-to-release binary compatibility.

So, how does the Scala compiler generate the bytecode of a trait?

There are two key elements:

I A trait is compiled into an interface plus an abstract classcontaining only static methods.

I “Forwarder” methods are injected in classes inheriting traits.

Mirco Dotta Managing Binary Compatibility in Scala

Page 9: Managing Binary Compatibility in Scala (Scala Days 2011)

Introduction Sources of Incompatibility Conclusion Type Inferencer Trait

Trait Compilation Explained

An example will help visualize how traits get compiled:

// declared in a librarytrait TypeAnalyzer {def analyze(prog: Program) {...}

}

// client codeclass TypingPhase extends TypeAnalyzer

The following is the (pseudo-)bytecode generated by scalac:

interface TypeAnalyzer {void analyze(prog: Program);

}abstract class TypeAnalyzer$class {static void analyze($this: TypeAnalyzer,

prog: Program) {// the trait’s method impl code

}}

class TypingPhase implements TraitAnalyzer {// forwarder method injected by scalacvoid analyze(prog: Program) {// delegates to implementationTypeAnalyzer$class.analyze(this,prog)

}}

Mirco Dotta Managing Binary Compatibility in Scala

Page 10: Managing Binary Compatibility in Scala (Scala Days 2011)

Introduction Sources of Incompatibility Conclusion Type Inferencer Trait

Trait: Adding a concrete method

QuestionCan we add a member in a trait without breaking compatibilitywith pre-existing binaries?

trait TypeAnalyzer { // new versiondef analyze(prog: Program) {...}def analyze(clazz: ClassInfo) {...}

}

//TypeAnalyzer trait compiledinterface TypeAnalyzer {void analyze(prog: Program);void analyze(clazz: ClassInfo);

}abstract class TypeAnalyzer$class {static void analyze($this: TypeAnalyzer,

prog: Program{...}static void analyze($this: TypeAnalyzer,

clazz: ClassInfo) {...}}

// compiled against the old versionclass TypingPhase implements TraitAnalyzer {// forwarder method injected by scalacvoid analyze(prog: Program) {// delegates to implementationTypeAnalyzer$class.analyze(this,prog)

}// missing concrete implementation!??analyze(clazz: ClassInfo)??

}

Take Home Message

Adding a concrete method in a traitbreaks binary compatibility.

Mirco Dotta Managing Binary Compatibility in Scala

Page 11: Managing Binary Compatibility in Scala (Scala Days 2011)

Introduction Sources of Incompatibility Conclusion Conclusion Future Work Scala Migration Manager

Conclusion

I Ensuring release-to-release binary compatibility of Scalalibraries is possible.

I Though, sometimes it can be difficult to tell if a change in theAPI of a class/trait will break pre-existing binaries.

I In the discussed examples we have seen that:I Type inferencer may be at the root of changes in the signature

of a method.I Traits are a sensible source of binary incompatibilities.

It really looks like library’s maintainers’ life ain’t that easy...

Mirco Dotta Managing Binary Compatibility in Scala

Page 12: Managing Binary Compatibility in Scala (Scala Days 2011)

Introduction Sources of Incompatibility Conclusion Conclusion Future Work Scala Migration Manager

Introducing the Scala Migration Manager (MiMa)

Today we release the Scala Migration Manager! (beta)

I It’s free!!

I It will tell you, library maintainers, if your next release isbinary compatible with the current one.

I It will tell you, libraries users, if two releases of a library arebinary compatible.

MiMa can collect and report all sources of “syntactic” binaryincompatibilities between two releases of a same library.

I “Syntactic” means NO LinkageError (e.g.,NoSuchMethodException) will ever be thrown at runtime.

Now it’s time for a demo!

Mirco Dotta Managing Binary Compatibility in Scala

Page 13: Managing Binary Compatibility in Scala (Scala Days 2011)

Introduction Sources of Incompatibility Conclusion Conclusion Future Work Scala Migration Manager

Future Work

Reporting binary incompatibilities is only half of the story. We arealready working on a “companion” tool that will help you migratebinary incompatibilities.

For the reporting there are many ideas spinning around. Yourfeedback will help us decide what brings you immediate value

One that I believe is useful:

I Maven/Sbt integration.

Mirco Dotta Managing Binary Compatibility in Scala

Page 14: Managing Binary Compatibility in Scala (Scala Days 2011)

Introduction Sources of Incompatibility Conclusion Conclusion Future Work Scala Migration Manager

Scala Migration Manager

Visit http://typesafe.com/technology/migration-manager

I More information about the Migration Manager

I Download it and try it out, it’s free!

We want to hear back from you.

I Success stories

I Request new features

I Report bugs

Want to know more, make sure to get in touch!

Mirco Dotta, email: [email protected], twitter: @mircodotta

Mirco Dotta Managing Binary Compatibility in Scala