fjgandrade / sharpkit

Automatically exported from code.google.com/p/sharpkit
0 stars 0 forks source link

Incorrect code emitted for classes implementing one or more generic interfaces (e.g. visitor pattern) #300

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
Given a simplified example of the visitor pattern:
We have to classes A, B which are IVisitables and a visitor BVisitor (which is 
able to visit only B)

  public interface IVisitor
  {
  }

  public interface IVisitor<T> : IVisitor
  {
    void Visit(T visitable);
  }

  public interface IVisitable
  {
    void Accept(IVisitor visitor);
  }

  //----------------------------------------------

  public class A : IVisitable
  {
    #region IVisitable Members

    public void Accept(IVisitor visitor)
    {
      IVisitor<A> aVisitor = visitor as IVisitor<A>;
      if (aVisitor != null)
      {
        // calls visitor.Visit(A a) if visitor implements IVisitor<A>
        aVisitor.Visit(this);
      }
    }

    #endregion
  }

  //----------------------------------------------

  public class B : IVisitable
  {
    #region IVisitable Members

    public void Accept(IVisitor visitor)
    {
      IVisitor<B> bVisitor = visitor as IVisitor<B>;
      if (bVisitor != null)
      {
        // calls visitor.Visit(B b) if visitor implements IVisitor<B>
        bVisitor.Visit(this);
      }
    }

    #endregion
  }

  //----------------------------------------------

  public class BVisitor :
    IVisitor<B>
  {
    #region IVisitor<B> Members

    public void Visit(B b)
    {
      // do something with b
    }

    #endregion
  }

This code gets compiled to:

var TestSharpKitApp$IVisitor = {fullname: "TestSharpKitApp.IVisitor", 
baseTypeName: "System.Object", assemblyName: "TestSharpKitApp", Kind: 
"Interface"};
JsTypes.push(TestSharpKitApp$IVisitor);
var TestSharpKitApp$IVisitor$1 = {fullname: "TestSharpKitApp.IVisitor$1", 
baseTypeName: "System.Object", assemblyName: "TestSharpKitApp", interfaceNames: 
["TestSharpKitApp.IVisitor"], Kind: "Interface"};
JsTypes.push(TestSharpKitApp$IVisitor$1);
var TestSharpKitApp$IVisitable = {fullname: "TestSharpKitApp.IVisitable", 
baseTypeName: "System.Object", assemblyName: "TestSharpKitApp", Kind: 
"Interface"};
JsTypes.push(TestSharpKitApp$IVisitable);
var TestSharpKitApp$A =
{
    fullname: "TestSharpKitApp.A",
    baseTypeName: "System.Object",
    assemblyName: "TestSharpKitApp",
    interfaceNames: ["TestSharpKitApp.IVisitable"],
    Kind: "Class",
    definition:
    {
        ctor: function ()
        {
            System.Object.ctor.call(this);
        },
        Accept: function (visitor)
        {
            var aVisitor = As(visitor, TestSharpKitApp.IVisitor$1.ctor);
            if (aVisitor != null)
            {
                aVisitor.Visit(this);
            }
        }
    }
};
JsTypes.push(TestSharpKitApp$A);
var TestSharpKitApp$B =
{
    fullname: "TestSharpKitApp.B",
    baseTypeName: "System.Object",
    assemblyName: "TestSharpKitApp",
    interfaceNames: ["TestSharpKitApp.IVisitable"],
    Kind: "Class",
    definition:
    {
        ctor: function ()
        {
            System.Object.ctor.call(this);
        },
        Accept: function (visitor)
        {
            var bVisitor = As(visitor, TestSharpKitApp.IVisitor$1.ctor);
            if (bVisitor != null)
            {
                bVisitor.Visit(this);
            }
        }
    }
};
JsTypes.push(TestSharpKitApp$B);
var TestSharpKitApp$BVisitor =
{
    fullname: "TestSharpKitApp.BVisitor",
    baseTypeName: "System.Object",
    assemblyName: "TestSharpKitApp",
    interfaceNames: ["TestSharpKitApp.IVisitor$1"],
    Kind: "Class",
    definition:
    {
        ctor: function ()
        {
            System.Object.ctor.call(this);
        },
        Visit: function (b)
        {
        }
    }
};
JsTypes.push(TestSharpKitApp$BVisitor);

Client code:

      IVisitable a = new A();
      IVisitable b = new B();

      BVisitor bVisitor = new BVisitor();
      a.Accept(bVisitor);    // shouldn't do anything inside A.Accept(IVisitor), since BVisitor does not implement IVisitor<A>
      b.Accept(bVisitor);    // should call bVisitor.Visit(B b) from inside B.Accept(IVisitor)

This compiles correctly to

    var a = new TestSharpKitApp.A.ctor();
    var b = new TestSharpKitApp.B.ctor();

    var bVisitor = new TestSharpKitApp.BVisitor.ctor();
    a.Accept(bVisitor);
    b.Accept(bVisitor);

Executing this code leads to the following problem:
Calling 
    a.Accept(bVisitor) 
in C# would execute the following code    
    public void Accept(IVisitor visitor)
    {
      IVisitor<A> aVisitor = visitor as IVisitor<A>;
      // in C# the condition above is false, since BVisitor only implements IVisitor<B>, but not IVisitor<A>
      if (aVisitor != null)
      {
        // in C# we only come here for visitors implementing IVisitor<A>
        aVisitor.Visit(this);
      }
    }

The code above is translated to
    Accept: function (visitor)
    {
    var aVisitor = As(visitor, TestSharpKitApp.IVisitor$1.ctor);
        // this condition is always true, but it should only be true for visitors implementing IVisitor<A>
    if (aVisitor != null)
    {
        // in js the condition above is incorrectly always true, even for a BVisitor which only implements IVisitor<B>, but not IVisitor<A>
        aVisitor.Visit(this);
    }
    }

We also see that both A.Accept(IVisitor) and B.Accept(IVisitor) compile to the 
same code.

  IVisitor<A> aVisitor = visitor as IVisitor<A>;
should compile to something like
  var aVisitor = As(visitor, TestSharpKitApp.IVisitor$A.ctor);
and 
  aVisitor.Visit(this);
should compile to 
  aVisitor.Visit$$A(this);

Original issue reported on code.google.com by zoomer...@gmail.com on 11 Jul 2013 at 7:55

GoogleCodeExporter commented 9 years ago

Original comment by zoomer...@gmail.com on 11 Jul 2013 at 8:52

GoogleCodeExporter commented 9 years ago

Original comment by zoomer...@gmail.com on 11 Jul 2013 at 10:45