#530 Ctor bug with default params

brian Wed 15 Apr 2009

class UserData
{
  const Str name
  const Str email
  const Str salt
  const Str digest
  User user

  new make(Str name, Str email := name.replace(" ", ".").lower)
  {
    this.name   = name
    this.email  = email
    this.salt   = Buf.random(40).toBase64
    this.digest = "$email:password:$salt".toBuf.toDigest("SHA1").toBase64
  }
}


Error: Target 'genrun' failed [/C:/dev/bespin-demo/build.fan]
sys::Err: java.lang.VerifyError: (class: fan/bespinDemo/UserData, method: make signature: (Ljava/lang/String;)Lfan/bespinDemo/UserData;) Accessing value fro
m uninitialized register 1
  bespinDemo::BuildUsers.instance$init$bespinDemo$BuildUsers (BuildUsers.fan:25)
  bespinDemo::BuildUsers.make$ (BuildUsers.fan:45)
  bespinDemo::BuildUsers.make (BuildUsers.fan)
  bespinDemo::BuildDemo.gen (BuildDemo.fan:21)
  build_0::Build.gen (/C:/dev/bespin-demo/build.fan:47)
  build_0::Build.genrun (/C:/dev/bespin-demo/build.fan:33)
  sun.reflect.NativeMethodAccessorImpl.invoke0 (Unknown)
  sun.reflect.NativeMethodAccessorImpl.invoke (Unknown)
  sun.reflect.DelegatingMethodAccessorImpl.invoke (Unknown)
  java.lang.reflect.Method.invoke (Unknown)
  fan.sys.Method.invoke (Method.java:558)
  fan.sys.Method$MethodFunc.callOn (Method.java:248)
  fan.sys.Method.callOn (Method.java:179)
  build::BuildScript.toFunc (BuildScript.fan:250)
  build::Target.run (Target.fan:78)
  build::BuildScript.main (BuildScript.fan:468)
  fan.sys.List.each (List.java:509)
  build::BuildScript.main (BuildScript.fan:468)
  sun.reflect.NativeMethodAccessorImpl.invoke0 (Unknown)
  More...

brian Wed 15 Apr 2009

Promoted to ticket #530 and assigned to brian

brian Sat 2 May 2009

Ticket resolved in 1.0.42

This is a really tricky bug. The way I originally designed the JVM ctor emit is like this:

// Fan
class Foo
{
  new make(Str a, Int b := a.size) {}
}

// Java
class Foo
{
  static Foo make(String a, long b) { x = new Foo(); make$(x, a, b); return x; }
  static Foo make(String a) { x = new Foo(); make$(x, a, s.size); return x; }
  static void make$(Foo x, String a, long b) { }
  static void make$(Foo x, String a) { make$(x, a, s.size); }
}

That design inlines the param default expressions into both make and make$ which optimizes both the external factory call path and the internal subclass call path (at the expense of code size).

The problem with that design is when you have default parameters which use other parameters in their default expression. The Fan compiler generates constructors as basically instance methods with an implied this param, so trying to use those expressions in make can seriously breaks things.

I changed the JVM emitter to have each make factory call the matching make$ body method. So things now look like this:

// Java
class Foo
{
  ...
  static Foo make(String a) { x = new Foo(); make$(x, a); return x; }
  ...
}

Login or Signup to reply.