Scramasax / sharpkit

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

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

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
(this a variation of issue 300)
Given a simplified example of the visitor pattern:
We have to classes A, B which are IVisitables and a visitor ABVisitor (which is 
able to visit both A and 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)
      {
        // should call visitor.Visit(A 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)
      {
        // should call visitor.Visit(B b)
        bVisitor.Visit(this);
      }
    }

    #endregion
  }

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

  public class ABVisitor :
    IVisitor<A>,
    IVisitor<B>
  {
    #region IVisitor<A> Members

    public void Visit(A a)
    {
      // do something with a
    }

    #endregion

    #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$ABVisitor =
{
    fullname: "TestSharpKitApp.ABVisitor",
    baseTypeName: "System.Object",
    assemblyName: "TestSharpKitApp",
    interfaceNames: ["TestSharpKitApp.IVisitor$1", "TestSharpKitApp.IVisitor$1"],
    Kind: "Class",
    definition:
    {
        ctor: function ()
        {
            System.Object.ctor.call(this);
        },
        Visit$$A: function (a)
        {
        },
        Visit$$B: function (b)
        {
        }
    }
};
JsTypes.push(TestSharpKitApp$ABVisitor);

Client code:

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

      ABVisitor abVisitor = new ABVisitor();
      a.Accept(abVisitor);    // should call abVisitor.Visit(A a) from inside A.Accept(IVisitor)
      b.Accept(abVisitor);    // should call abVisitor.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 abVisitor = new TestSharpKitApp.ABVisitor.ctor();
    a.Accept(abVisitor);
    b.Accept(abVisitor);

However executing this code leads to a problem:
Inside 
    a.Accept(abVisitor) 
the following code is executed:
    var aVisitor = As(visitor, TestSharpKitApp.IVisitor$1.ctor);
    if (aVisitor != null)
    {
        aVisitor.Visit(this);
    }
We get a runtime error on aVisitor.Visit(this);
>> Microsoft JScript runtime error: Object doesn't support property or method 
'Visit'

The generated code should rather look like this:
    var aVisitor = As(visitor, TestSharpKitApp.IVisitor$A.ctor);
    if (aVisitor != null)
    {
        aVisitor.Visit$$A(this);
    }

We also see that for ABVisitor we have the following interface definitions
    interfaceNames: ["TestSharpKitApp.IVisitor$1", "TestSharpKitApp.IVisitor$1"],
instead of something like
    interfaceNames: ["TestSharpKitApp.IVisitor$A", "TestSharpKitApp.IVisitor$B"],

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

GoogleCodeExporter commented 9 years ago

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

GoogleCodeExporter commented 9 years ago

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

GoogleCodeExporter commented 9 years ago
Here's another repro for the same issue:

namespace Test
{
    public class Foo { }
    public class Bar { }
    public interface IDoStuff<T>
    {
        void DoStuff(T dummy);
    }

    public class TestClass : IDoStuff<Foo>, IDoStuff<Bar>
    {
        public void DoStuff(Foo foo) { }
        public void DoStuff(Bar bar) { }
    }

    public static class Test
    {
        private static void DoStuff<T>(IDoStuff<T> comp, T dummy)
        {
            comp.DoStuff(dummy);
        }

        public static void RunTest()
        {
            TestClass tc = new TestClass();
            DoStuff(tc, new Foo());
            DoStuff(tc, new Bar());
        }
    }
}

Attempting to run the generated JS will throw an exception because DoStuff is 
not implemented.

Original comment by polofili...@gmail.com on 3 Sep 2014 at 1:48