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.
-
+
@@ -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.
-
+
+
+
+
+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.
+
+
+
@@ -2654,7 +2745,7 @@ public class ExtendMe : global::System.IDisposable {
-
+
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;
}