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 |
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 > |
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 |
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 |
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); |
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 |
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. |
Free forum by Nabble | Edit this page |