From ee17f8d04f40bfc25ecaf146a6ebe667eabcffb6 Mon Sep 17 00:00:00 2001 From: William S Fulton Date: Fri, 11 May 2018 18:09:51 +0100 Subject: [PATCH] C#, D, Java methodmodifiers on destructors Add support so that the %csmethodmodifiers, %dmethodmodifiers, %javamethodmodifiers can modify the method modifiers for the destructor wrappers in the proxy class: dispose, Dispose, delete. With this feature, it is now possible to make a C# proxy class sealed, eg when wrapping a class X, the virtual method modifiers can be removed using: %typemap(csclassmodifiers) X "public sealed class" %csmethodmodifiers X::~X "public /*virtual*/"; --- CHANGES.current | 10 ++ Doc/Manual/CSharp.html | 99 ++++++++++++++++++- Doc/Manual/Contents.html | 3 +- Doc/Manual/Java.html | 4 + Examples/test-suite/common.mk | 1 + .../test-suite/destructor_methodmodifiers.i | 61 ++++++++++++ Source/Modules/csharp.cxx | 16 ++- Source/Modules/d.cxx | 15 ++- Source/Modules/java.cxx | 14 ++- 9 files changed, 210 insertions(+), 13 deletions(-) create mode 100644 Examples/test-suite/destructor_methodmodifiers.i diff --git a/CHANGES.current b/CHANGES.current index f15c99e90..0acfddc7d 100644 --- a/CHANGES.current +++ b/CHANGES.current @@ -7,6 +7,16 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/ Version 4.0.0 (in progress) =========================== +2018-05-11: wsfulton + [C#, D, Java] Add support so that the %csmethodmodifiers, %dmethodmodifiers, + %javamethodmodifiers can modify the method modifiers for the destructor wrappers + in the proxy class: dispose, Dispose, delete. With this feature, it is now possible + to make a C# proxy class sealed, eg when wrapping a class X, the virtual method modifiers + can be removed using: + + %typemap(csclassmodifiers) X "public sealed class" + %csmethodmodifiers X::~X "public /*virtual*/"; + 2018-04-18: olly [Python] Suppress new pycodestyle warning: E252 missing whitespace around parameter equals diff --git a/Doc/Manual/CSharp.html b/Doc/Manual/CSharp.html index 13b6e4997..e96940626 100644 --- a/Doc/Manual/CSharp.html +++ b/Doc/Manual/CSharp.html @@ -44,7 +44,8 @@
  • Date marshalling using the csin typemap and associated attributes
  • A date example demonstrating marshalling of C# properties
  • Date example demonstrating the 'pre' and 'post' typemap attributes for directors -
  • Turning wrapped classes into partial classes +
  • Turning proxy classes into partial classes +
  • Turning proxy classes into sealed classes
  • Extending proxy classes with additional C# code
  • Underlying type for enums @@ -2515,7 +2516,7 @@ Pay special attention to the memory management issues, using these attributes.

    -

    20.8.6 Turning wrapped classes into partial classes

    +

    20.8.6 Turning proxy classes into partial classes

    @@ -2615,7 +2616,97 @@ demonstrating that the class contains methods calling both unmanaged code - The following example is an alternative approach to adding managed code to the generated proxy class.

    -

    20.8.7 Extending proxy classes with additional C# code

    +

    20.8.7 Turning proxy classes into sealed classes

    + + +

    +The technique in the previous section can be used to make the proxy class a sealed class. +Consider a C++ class NotABaseClass that you don't want to be derived from in C#: +

    + +
    +
    +struct NotABaseClass {
    +  NotABaseClass();
    +  ~NotABaseClass();
    +};
    +
    +
    + +

    +The default C# proxy class method generated with Dispose method is: +

    + +
    +
    +public class NotABaseClass : global::System.IDisposable {
    +  ...
    +  public virtual void Dispose() {
    +    ...
    +  }
    +}
    +
    +
    + +

    +The csclassmodifiers typemap can be used to modify the class modifiers and +the csmethodmodifiers feature can be used on the destructor to modify the proxy's Dispose method: +

    + +
    +
    +%typemap(csclassmodifiers) NotABaseClass "public sealed class"
    +%csmethodmodifiers NotABaseClass::~NotABaseClass "public /*virtual*/";
    +
    +
    + +

    +The relevant generated code is thus: +

    + +
    +
    +public sealed class NotABaseClass : global::System.IDisposable {
    +  ...
    +  public /*virtual*/ void Dispose() {
    +    ...
    +  }
    +}
    +
    +
    + +

    +Any attempt to derive from the NotABaseClass in C# will result in a C# compiler error, for example: +

    + +
    +
    +public class Derived : NotABaseClass {
    +};
    +
    +
    +
    +
    +runme.cs(6,14): error CS0509: `Derived': cannot derive from sealed type `NotABaseClass'
    +
    +
    + +

    +Finally, if you get a warning about use of 'protected' in the generated base class: +

    + +
    +
    +NotABaseClass.cs(14,18): warning CS0628: `NotABaseClass.swigCMemOwn': new protected member declared in sealed class
    +
    +
    + +

    +Either suppress the warning or modify the generated code by copying and tweaking the default +'csbody' typemap code in csharp.swg by modifying swigCMemOwn to not be protected. +

    + +

    20.8.8 Extending proxy classes with additional C# code

    @@ -2654,7 +2745,7 @@ public class ExtendMe : global::System.IDisposable { -

    20.8.8 Underlying type for enums

    +

    20.8.9 Underlying type for enums

    diff --git a/Doc/Manual/Contents.html b/Doc/Manual/Contents.html index 5cc796c8d..f77823d20 100644 --- a/Doc/Manual/Contents.html +++ b/Doc/Manual/Contents.html @@ -787,7 +787,8 @@

  • Date marshalling using the csin typemap and associated attributes
  • A date example demonstrating marshalling of C# properties
  • Date example demonstrating the 'pre' and 'post' typemap attributes for directors -
  • Turning wrapped classes into partial classes +
  • Turning proxy classes into partial classes +
  • Turning proxy classes into sealed classes
  • Extending proxy classes with additional C# code
  • Underlying type for enums diff --git a/Doc/Manual/Java.html b/Doc/Manual/Java.html index 3b6cb3a05..6b84d540f 100644 --- a/Doc/Manual/Java.html +++ b/Doc/Manual/Java.html @@ -6560,6 +6560,8 @@ used for all proxy classes except those which have a base class
    Note that the delete() method name is configurable and is specified by the methodname attribute. The method modifiers are also configurable via the methodmodifiers attribute. +If a %javamethodmodifiers is attached to the class' destructor, +it will be used in preference to the methodmodifiers typemap attribute for the class.

    %typemap(javadestruct_derived, methodname="delete", methodmodifiers="public synchronized")

    @@ -6571,6 +6573,8 @@ same as "javadestruct" but only used for derived proxy classes
    Note that the delete() method name is configurable and is specified by the methodname attribute. The method modifiers are also configurable via the methodmodifiers attribute. +If a %javamethodmodifiers is attached to the class' destructor, +it will be used in preference to the methodmodifiers typemap attribute for the class.

    %typemap(javaimports)

    diff --git a/Examples/test-suite/common.mk b/Examples/test-suite/common.mk index d75d3242e..72c49648b 100644 --- a/Examples/test-suite/common.mk +++ b/Examples/test-suite/common.mk @@ -170,6 +170,7 @@ CPP_TEST_CASES += \ defvalue_constructor \ derived_byvalue \ derived_nested \ + destructor_methodmodifiers \ destructor_reprotected \ director_abstract \ director_alternating \ diff --git a/Examples/test-suite/destructor_methodmodifiers.i b/Examples/test-suite/destructor_methodmodifiers.i new file mode 100644 index 000000000..93db7f2cc --- /dev/null +++ b/Examples/test-suite/destructor_methodmodifiers.i @@ -0,0 +1,61 @@ +%module destructor_methodmodifiers + +// This test changes the proxy classes so that they cannot be inherited from in the target language +// Previously the %csmethodmodifiers, %dmethodmodifiers, %javamethodmodifiers on destructors were ignored +// Now they can control the dispose/Dispose/delete method modifiers + +#if defined(SWIGCSHARP) + +// remove all use of protected and virtual keywords +%typemap(csclassmodifiers) NotForDeriving1, NotForDeriving2 "public sealed class" +%csmethodmodifiers NotForDeriving1::~NotForDeriving1 "public /*not virtual nor override*/"; +%csmethodmodifiers NotForDeriving2::~NotForDeriving2 "public /*not virtual nor override*/"; + +// remove protected keyword to remove compiler warning +%typemap(csbody) NotForDeriving1, NotForDeriving2 %{ + private global::System.Runtime.InteropServices.HandleRef swigCPtr; + private /*protected*/ bool swigCMemOwn; + + internal $csclassname(global::System.IntPtr cPtr, bool cMemoryOwn) { + swigCMemOwn = cMemoryOwn; + swigCPtr = new global::System.Runtime.InteropServices.HandleRef(this, cPtr); + } + + internal static global::System.Runtime.InteropServices.HandleRef getCPtr($csclassname obj) { + return (obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr; + } +%} + +#elif defined(SWIGD) + +%typemap(dclassmodifiers) NotForDeriving1, NotForDeriving2 "final class" +%dmethodmodifiers NotForDeriving1::~NotForDeriving1 "public final"; +%dmethodmodifiers NotForDeriving2::~NotForDeriving2 "public final"; + +#elif defined(SWIGJAVA) + +%typemap(javaclassmodifiers) NotForDeriving1, NotForDeriving2 "public final class" +%javamethodmodifiers NotForDeriving1::~NotForDeriving1 "public synchronized final"; +%javamethodmodifiers NotForDeriving2::~NotForDeriving2 "public synchronized final"; + +#endif + +%inline %{ +//#include +struct NotForDeriving1 { + void notvirtual() {} + ~NotForDeriving1() { +// std::cout << "~NotForDeriving1 called" << std::endl; + } +}; +struct NotForDeriving2 { + void notvirtual() {} +#if defined(SWIG) +%extend { + ~NotForDeriving2() { +// std::cout << "~NotForDeriving2 called" << std::endl; + } +} +#endif +}; +%} diff --git a/Source/Modules/csharp.cxx b/Source/Modules/csharp.cxx index 854872917..8bd25aa48 100644 --- a/Source/Modules/csharp.cxx +++ b/Source/Modules/csharp.cxx @@ -1910,9 +1910,15 @@ public: Replaceall(destruct, "$imcall", destructor_call); else Replaceall(destruct, "$imcall", "throw new global::System.MethodAccessException(\"C++ destructor does not have public access\")"); - if (*Char(destruct)) - Printv(proxy_class_def, "\n ", destruct_methodmodifiers, " ", derived ? "override" : "virtual", " void ", destruct_methodname, "() ", destruct, "\n", - NIL); + if (*Char(destruct)) { + Printv(proxy_class_def, "\n ", NIL); + const String *methodmods = Getattr(n, "destructmethodmodifiers"); + if (methodmods) + Printv(proxy_class_def, methodmods, NIL); + else + Printv(proxy_class_def, destruct_methodmodifiers, " ", derived ? "override" : "virtual", NIL); + Printv(proxy_class_def, " void ", destruct_methodname, "() ", destruct, "\n", NIL); + } } if (*Char(interface_upcasts)) Printv(proxy_class_def, interface_upcasts, NIL); @@ -2860,7 +2866,11 @@ public: if (proxy_flag) { Printv(destructor_call, full_imclass_name, ".", Swig_name_destroy(getNSpace(), symname), "(swigCPtr)", NIL); + const String *methodmods = Getattr(n, "feature:cs:methodmodifiers"); + if (methodmods) + Setattr(getCurrentClass(), "destructmethodmodifiers", methodmods); } + return SWIG_OK; } diff --git a/Source/Modules/d.cxx b/Source/Modules/d.cxx index 1837228d4..0c3f020a8 100644 --- a/Source/Modules/d.cxx +++ b/Source/Modules/d.cxx @@ -1298,7 +1298,12 @@ public: virtual int destructorHandler(Node *n) { Language::destructorHandler(n); String *symname = Getattr(n, "sym:name"); + Printv(destructor_call, im_dmodule_fq_name, ".", Swig_name_destroy(getNSpace(),symname), "(cast(void*)swigCPtr)", NIL); + const String *methodmods = Getattr(n, "feature:d:methodmodifiers"); + if (methodmods) + Setattr(getCurrentClass(), "destructmethodmodifiers", methodmods); + return SWIG_OK; } @@ -3312,9 +3317,13 @@ private: } if (*Char(dispose_code)) { - Printv(body, "\n", dispose_methodmodifiers, - (derived ? " override" : ""), " void ", dispose_methodname, "() ", - dispose_code, "\n", NIL); + Printv(body, "\n", NIL); + const String *methodmods = Getattr(n, "destructmethodmodifiers"); + if (methodmods) + Printv(body, methodmods, NIL); + else + Printv(body, dispose_methodmodifiers, (derived ? " override" : ""), NIL); + Printv(body, " void ", dispose_methodname, "() ", dispose_code, "\n", NIL); } } diff --git a/Source/Modules/java.cxx b/Source/Modules/java.cxx index 9ff8299c0..f89de4e12 100644 --- a/Source/Modules/java.cxx +++ b/Source/Modules/java.cxx @@ -1987,8 +1987,15 @@ public: Replaceall(destruct, "$jnicall", destructor_call); else Replaceall(destruct, "$jnicall", "throw new UnsupportedOperationException(\"C++ destructor does not have public access\")"); - if (*Char(destruct)) - Printv(proxy_class_def, "\n ", destruct_methodmodifiers, " void ", destruct_methodname, "()", destructor_throws_clause, " ", destruct, "\n", NIL); + if (*Char(destruct)) { + Printv(proxy_class_def, "\n ", NIL); + const String *methodmods = Getattr(n, "destructmethodmodifiers"); + if (methodmods) + Printv(proxy_class_def, methodmods, NIL); + else + Printv(proxy_class_def, destruct_methodmodifiers, NIL); + Printv(proxy_class_def, " void ", destruct_methodname, "()", destructor_throws_clause, " ", destruct, "\n", NIL); + } } if (*Char(interface_upcasts)) Printv(proxy_class_def, interface_upcasts, NIL); @@ -2830,6 +2837,9 @@ public: if (proxy_flag) { Printv(destructor_call, full_imclass_name, ".", Swig_name_destroy(getNSpace(), symname), "(swigCPtr)", NIL); generateThrowsClause(n, destructor_throws_clause); + const String *methodmods = Getattr(n, "feature:java:methodmodifiers"); + if (methodmods) + Setattr(getCurrentClass(), "destructmethodmodifiers", methodmods); } return SWIG_OK; }