addJavaBeanMethods

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

addJavaBeanMethods

Judson, Ross
I was thinking about Scala's property support and how nice it is, and it
bothered me that Java tools wouldn't be able to see it.  So I added this
to the Scala compiler as an experiment (Typers.scala) and called it
right after addSyntheticMethods:

  private def addJavaBeanMethods(cd: ClassDef, templ: Template):
Template = {
     
      def checkBody(body: List[Tree]): List[Tree] = body match {
        case (d1 @ DefDef(mods1, name1, tparams1, vparamss1, tpt1,
rhs1)) ::
        (d2 @ DefDef(mods2, name2, tparams2, vparamss2, tpt2, rhs2)) ::
rest
              if (mods1.isAccessor && mods2.isAccessor &&
                  name2.toString() == (name1.toString() + "_$eq")) => {
                val n1s = d1.symbol.name.toString();
                val getter = newTermName("get" +
Character.toUpperCase(n1s.charAt(0)) + n1s.substring(1));
                val getterSymbol = cd.symbol.newMethod(d1.pos, getter);
                getterSymbol.resetFlag(symtab.Flags.ACCESSOR);
                getterSymbol.owner.info.decls.enter(getterSymbol);
                val getMethod = DefDef(getterSymbol, Nil,
                  Select(This(cd.symbol), d1.symbol));
                Console.println(getMethod);
                d1 :: d2 :: typed(getMethod) :: checkBody(rest)
              }
        case head :: tail => head :: checkBody(tail)              
        case Nil => Nil
      }
     
      copy.Template(templ, templ.parents, checkBody(templ.body))
    }

What I want to do is fairly simple -- find pairs of accessor methods
(name and name_=), then generate two extra methods using Java naming
conventions that call the first ones.  In other words,

class T {
  var name: String = _;
}

Becomes:

class T {
  var name: String = _;
  def getName(): String = name;
  def setName(s: String);
}

Well, my first shot at it didn't work :) I never could seem to arrange
things such that the code generator would accept the AST.  My first
attempt was to put a new phase in just prior to code generation, but
doing it there required me to insert a lot of nonobvious type
information.  There's obviously a "right way" to do things like create
synthetic methods.  Hopefully we'll see a little "compiler cookbook" at
some point to help us amateurs mangle our own private versions of Scala
;)

RJ

Reply | Threaded
Open this post in threaded view
|

Re: addJavaBeanMethods

Nikolay Mihaylov
Hi Ross

In the LAMP we had the idea to introduce an attribute that one can apply
to a variable to designate it as having Java Beans getter/setter. I just
added the scala.runtime.compat.BeanProperty class to use for that
purpose. The nice thing is, it only required a small change in the code
generator. But it lets you write

[BeanProperty]
var MyProperty: ...

and the generated accessors will be named getMyProperty/setMyProperty. I
don't do any capitalization (as seen in your example) so 'name' will
obtain getname/setname accessors.

There is one caveat to this solution. Currently, the attributes are not
dumped in the classfile. So if you use such variable from Scala code
with separate compilation the compiler will generate calls to the
getter/setter methods according to the Scala convention. If you compile
them together there should be no problem. In any case, we are working on
an overhaul of Scala's attributes support. Dumping attributes to the
classfile is one of the goals and will fix this problem.

I'm very much interested in feedback on this solution because no one
here is doing Java Beans and I might be overlooking sth.

Nikolay


On Thu, 2006-02-09 at 08:11 -0500, Judson, Ross wrote:

> I was thinking about Scala's property support and how nice it is, and it
> bothered me that Java tools wouldn't be able to see it.  So I added this
> to the Scala compiler as an experiment (Typers.scala) and called it
> right after addSyntheticMethods:
>
>   private def addJavaBeanMethods(cd: ClassDef, templ: Template):
> Template = {
>      
>       def checkBody(body: List[Tree]): List[Tree] = body match {
>         case (d1 @ DefDef(mods1, name1, tparams1, vparamss1, tpt1,
> rhs1)) ::
>         (d2 @ DefDef(mods2, name2, tparams2, vparamss2, tpt2, rhs2)) ::
> rest
>               if (mods1.isAccessor && mods2.isAccessor &&
>                   name2.toString() == (name1.toString() + "_$eq")) => {
>                 val n1s = d1.symbol.name.toString();
>                 val getter = newTermName("get" +
> Character.toUpperCase(n1s.charAt(0)) + n1s.substring(1));
>                 val getterSymbol = cd.symbol.newMethod(d1.pos, getter);
>                 getterSymbol.resetFlag(symtab.Flags.ACCESSOR);
>                 getterSymbol.owner.info.decls.enter(getterSymbol);
>                 val getMethod = DefDef(getterSymbol, Nil,
>                   Select(This(cd.symbol), d1.symbol));
>                 Console.println(getMethod);
>                 d1 :: d2 :: typed(getMethod) :: checkBody(rest)
>               }
>         case head :: tail => head :: checkBody(tail)              
>         case Nil => Nil
>       }
>      
>       copy.Template(templ, templ.parents, checkBody(templ.body))
>     }
>
> What I want to do is fairly simple -- find pairs of accessor methods
> (name and name_=), then generate two extra methods using Java naming
> conventions that call the first ones.  In other words,
>
> class T {
>   var name: String = _;
> }
>
> Becomes:
>
> class T {
>   var name: String = _;
>   def getName(): String = name;
>   def setName(s: String);
> }
>
> Well, my first shot at it didn't work :) I never could seem to arrange
> things such that the code generator would accept the AST.  My first
> attempt was to put a new phase in just prior to code generation, but
> doing it there required me to insert a lot of nonobvious type
> information.  There's obviously a "right way" to do things like create
> synthetic methods.  Hopefully we'll see a little "compiler cookbook" at
> some point to help us amateurs mangle our own private versions of Scala
> ;)
>
> RJ
>

Reply | Threaded
Open this post in threaded view
|

Re: addJavaBeanMethods

Martin Odersky
Nikolay Mihaylov wrote:

>
> There is one caveat to this solution. Currently, the attributes are not
> dumped in the classfile. So if you use such variable from Scala code
> with separate compilation the compiler will generate calls to the
> getter/setter methods according to the Scala convention. If you compile
> them together there should be no problem.

I thought the set/get methods would simply forward to Scala's native
setter and getter? In that case what difference does it make which
method is called?

  -- Martin
Reply | Threaded
Open this post in threaded view
|

Re: addJavaBeanMethods

Nikolay Mihaylov
On Fri, 2006-02-10 at 16:12 +0100, Martin Odersky wrote:
> I thought the set/get methods would simply forward to Scala's native
> setter and getter? In that case what difference does it make which
> method is called?

Certainly. I did it like that because (1) that was the easiest to
implement, just name translation, and (2) if we have true-and-through
attribute support what difference would the name of the actual method
being called make? The frontend will know the methods by the names from
the symbol table it reads from the classfile, while the backend will
translate names if necessary.

Anyway, I'm not religious about that. I hacked the backend because
that's what I'm most familliar with. If the Beans-style getter/setter
are additional forwarders that be in the tree so it is visible via the
-print option. Also, I think it's easier to generate the tree for the
forwarders than to emit additional method definitions/bytecode by hand
in the backend. I'll try to implement proper attribute support before
anyone can abuse this new feature. Otherwise, anyone is free to refine
it in the way that seems most appropriate.

Nikolay

Reply | Threaded
Open this post in threaded view
|

Re: addJavaBeanMethods

joeltt

I'd definitely vote to have it capitalize the initial character. That would
certainly be the Java way, and this is intended purely for Java anyway.

Although, there isn't a way to specify (public/protected/private/pkg)
is there?

ie.

class X {
  private String name;

  public String getName();
  public void setName(String val);

Reply | Threaded
Open this post in threaded view
|

Re: addJavaBeanMethods

Nikolay Mihaylov
On Fri, 2006-02-10 at 08:27 -0800, [hidden email] wrote:
> I'd definitely vote to have it capitalize the initial character. That would
> certainly be the Java way, and this is intended purely for Java anyway.

We could have a separate attribute that does the same as BeanProperty
but with capitalization. I'm just kidding. But once you get locked in a
serious attribute collection, the tendency is to push it as far as you
can...

Seriously, what you suggest is easy to arrange. But I'm having seconds
thoughts on the way I implemented it. My primary concern is what happens
if, say, getMyProperty already exists. Obviously, we cannot introduce
new methods without making sure we're not stepping on someone else's
toes. This should be handled as early as possible. In any case, I don't
think the code generator should perform any lookups.

> Although, there isn't a way to specify (public/protected/private/pkg)
> is there?

> ie.
>
> class X {
>   private String name;
>
>   public String getName();
>   public void setName(String val);

Not sure what you mean. If you declare a variable in Scala it is
transformed in sth like the code above (modulo the naming convention).
The quirk is that you don't have direct access to the private field.

As far as user definitions are concerned, you can declare them as public
(no access modifiers), protected or private. In addition, Scala2 allows
you to make definitions private to a particular enclosing class or
package (although this is only enforced at Scala level - the emitted
method is public). You can find more details in

http://scala.epfl.ch/nsc/files/Changes.pdf

Nikolay

Reply | Threaded
Open this post in threaded view
|

RE: addJavaBeanMethods

Judson, Ross
In reply to this post by Judson, Ross
The other way to go with Java bean compatibility is to generate
"BeanInfo" classes for Scala classes.  This is probably the way to go,
although slightly more involved.  There's no possibility of collision
between function names and more information can be specified.  With
attributes on Scala classes that are standardized we can attach
descriptive information (like text, icon, etc) to properties and
generate the "right" thing on different platforms (.NET and JVM).

Given appropriate attributes the code generator would write a class that
assembles property descriptors, method descriptors, etc.  The nice thing
about the descriptors is that you can specify the method to call
directly, and no naming convention is necessary.

RJ

-----Original Message-----
From: Nikolay Mihaylov [mailto:[hidden email]]
Sent: Friday, February 10, 2006 4:22 PM
To: [hidden email]
Cc: [hidden email]
Subject: Re: addJavaBeanMethods

On Fri, 2006-02-10 at 08:27 -0800, [hidden email] wrote:
> I'd definitely vote to have it capitalize the initial character. That
> would certainly be the Java way, and this is intended purely for Java
anyway.

We could have a separate attribute that does the same as BeanProperty
but with capitalization. I'm just kidding. But once you get locked in a
serious attribute collection, the tendency is to push it as far as you
can...

Seriously, what you suggest is easy to arrange. But I'm having seconds
thoughts on the way I implemented it. My primary concern is what happens
if, say, getMyProperty already exists. Obviously, we cannot introduce
new methods without making sure we're not stepping on someone else's
toes. This should be handled as early as possible. In any case, I don't
think the code generator should perform any lookups.