#1915 Inject new field through DSL

Yuri Strot Sat 16 Jun 2012

I'm playing with Fantom DSL for a while. However I have a problem with injecting custom field to a type where DSL was declared. There is a test I have:

class InjectFieldTest : Test
{

  Str inject := InjectField<||>

  Void test1()
  {
    verifyNotNull(field)
  }

  Void test2()
  {
    field.set(this, "a")
    verifyEq("a", field.get(this))
  }

  Void test3()
  {
    verifyEq("", field.get(this))
  }

  private Field? field() { typeof.field("name") }

}

As you can see InjectField<||> should inject Str name := "". But something goes wrong:

  • test1 passed. So new field was generated.
  • test2 passed. So I can set value to the field and get it.
  • test3 failed. field.get(this) return null.

My DSL plugin:

class InjectFieldPlugin : DslPlugin
{

  new make(Compiler c) : super(c) {}

  override Expr compile(DslExpr dsl)
  {
    loc := dsl.loc
    parent := types.find { it.loc.file == loc.file }
    type := ns.resolveType("sys::Str")

    f := FieldDef(loc, parent, "name")
    f.flags = FConst.Public
    f.fieldType = TypeRef(loc, type)
    f.init = LiteralExpr.makeStr(loc, ns, "")

    //generate synthetic getter/setter
    defGet(f)
    defSet(f)

    f.flags += FConst.Storage
    parent.addSlot(f)
    return LiteralExpr.makeStr(dsl.loc, ns, "")
  }

  ...

}

I initialized custom field with an empty string f.init = LiteralExpr.makeStr(loc, ns, ""). Without this line I will get Non-nullable field name must be assigned in constructor make as expected. But for some reason initialized field returns null. Am I missed something or is it a bug?

There is full source I used: https://bitbucket.org/ystrot/play/src/8190f89645b4/fan/InjectFieldPlugin.fan

brian Sat 16 Jun 2012

It looks like you are not actually adding the field to the type definition. Try adding a line like this:

parent.addSlot(f)

Yuri Strot Sat 16 Jun 2012

I do. There is one from the bottom line of InjectFieldPlugin.compile.

brian Sat 16 Jun 2012

Actually I don't understand what the problem is. You are saying reflection works for getting/setting the field. The problem is the initialization of the field in the constructor? That happens in Normalize step by running thru all the fields and adding init statements into your constructor.

Yuri Strot Sun 17 Jun 2012

Oh, I see. DSL parsing happens in the ResolveExpr step which appears after Normalize step. I just added constructor initialization into my DSL plugin and it works correctly now. Thanks for help!

Login or Signup to reply.