Fragile field introspection in MetaRecord

24 views
Skip to first unread message

Florian Hars

unread,
Apr 13, 2011, 5:27:45 AM4/13/11
to lif...@googlegroups.com
There seems to be some unfortunate interaction between the magic the compiler
performs statically to get some types working on the JVM and the magic
MetaRecord performs at runtime via reflection that results in MetaRecord
reporting some fields twice if you do anything moderatly fancy with the types
of your fields. (It looks like the last person to notice this then went on
to implement all the MyFirstName, MyPassword, MyEmail and so on classes in
record.ProtoUser as a workaround to give all the fields a trivial type.)

Here is a failing test case:

import net.liftweb.record._
import field._
import org.specs.Specification

trait T {
val s: StringField[_]
}
class C extends Record[C] with T {
val meta = C
object s extends StringField(this, 80)
}
object C extends C with MetaRecord[C]

object RecordIntrospectionSpec extends Specification {
"fields of a Record" should {
"all be different" in {
(Set() ++ C.metaFields).size must_== C.metaFields.length
}
}
}

This results in:
[info] == RecordIntrospectionSpec ==
[error] x fields of a Record should
[error] x all be different
[error] '1' is not equal to '2' (Test.scala:17)
[info] == RecordIntrospectionSpec ==

The deeper reason is that both of these methods pass the isAssignableFrom
test in MetaRecord.isField:

scala> C.getClass.getMethods filter (_.getName == "s") foreach println
public final C$s$ C.s()
public final net.liftweb.record.field.StringField C.s()

Since these methods just forward to one another and all return the same
object in the end, it might be sufficient to make MetaRecord.introspect
keep track of which fields it has already seen.

- Florian.
--
#!/bin/sh -
set - `type -p $0` 'tr [a-m][n-z]RUXJAKBOZ [n-z][a-m]EH$W/@OBM' fu XUBZRA.fvt\
angher echo;while [ "$5" != "" ];do shift;done;$4 "gbhpu $3;fraqznvy sKunef.q\
r<$3&&frq -a -rc "`$4 "$0"|$1`">$3;rpub 'Jr ner Svt bs Obet.'"|$1|`$4 $2|$1`

David Whittaker

unread,
Apr 13, 2011, 11:17:58 AM4/13/11
to lif...@googlegroups.com
Huh.  I'm not that familiar with how the Scala compiler creates bytecode for nested objects.  Is the issue that the second method is just a bridge created to handle the generic return override?  If so, I wonder if we could filter out methods where Method.isSynthetic or Method.isBridge == true?


--
You received this message because you are subscribed to the Google Groups "Lift" group.
To post to this group, send email to lif...@googlegroups.com.
To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.


Florian Hars

unread,
Apr 13, 2011, 11:53:38 AM4/13/11
to lif...@googlegroups.com
On Wed, Apr 13, 2011 at 11:17:58AM -0400, David Whittaker wrote:
> Huh. I'm not that familiar with how the Scala compiler creates bytecode for
> nested objects.

Me neither. I was just confused by the additional fields popping up.

> Is the issue that the second method is just a bridge
> created to handle the generic return override? If so, I wonder if we could
> filter out methods where Method.isSynthetic or Method.isBridge == true?

Either might work:
scala> classOf[C].getMethods filter (_.getName == "s") foreach (m => println(m.isBridge+"\t"+m.isSynthetic+"\t"+m))
false false public final C$s$ C.s()
true true public final net.liftweb.record.field.StringField C.s()

Are there situations where only one of them is true?

David Pollak

unread,
Apr 13, 2011, 12:19:23 PM4/13/11
to lif...@googlegroups.com
I think we get it more right in Mapper.  Perhaps we can migrate some of the Mapper mechanisms for determining the difference between the field declaration and a method exposing that field over to Record.

--
You received this message because you are subscribed to the Google Groups "Lift" group.
To post to this group, send email to lif...@googlegroups.com.
To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.




--
Lift, the simply functional web framework http://liftweb.net

David Whittaker

unread,
Apr 13, 2011, 12:47:45 PM4/13/11
to lif...@googlegroups.com
Are there situations where only one of them is true?

I think there are situations other than the creation of bridge methods where the compiler will mark a method as synthetic.  Since it seems that all synthetic methods don't have any corresponding source construct I think filtering them all would work.

I think we get it more right in Mapper.  Perhaps we can migrate some of the Mapper mechanisms for determining the difference between the field declaration and a method exposing that field over to Record.

Is there an "owner" for the base Record code outside of the individual modules?  I could probably take a look at this in the next week or so if no one else is in a better position. 

David Pollak

unread,
Apr 18, 2011, 9:10:02 AM4/18/11
to lif...@googlegroups.com
On Wed, Apr 13, 2011 at 9:47 AM, David Whittaker <da...@iradix.com> wrote:
Are there situations where only one of them is true?

I think there are situations other than the creation of bridge methods where the compiler will mark a method as synthetic.  Since it seems that all synthetic methods don't have any corresponding source construct I think filtering them all would work.

I think we get it more right in Mapper.  Perhaps we can migrate some of the Mapper mechanisms for determining the difference between the field declaration and a method exposing that field over to Record.

Is there an "owner" for the base Record code outside of the individual modules?

Not at this time.  It'd be most excellent if you could pick up Record.
 

David Whittaker

unread,
Apr 18, 2011, 11:43:18 AM4/18/11
to lif...@googlegroups.com
Ok, at the very least I can add this one to my list and keep my eye out for other Record issues.

Florian, would you mind creating an Assembla ticket that references this thread and assign it to me?
Reply all
Reply to author
Forward
0 new messages