1 module vibe.internal.interfaceproxy;
2 
3 import vibe.internal.traits;
4 import vibe.internal.freelistref;
5 import vibe.internal.allocator;
6 import std.algorithm.mutation : move, swap;
7 import std.meta : staticMap;
8 import std.traits : BaseTypeTuple;
9 
10 
11 O asInterface(I, O)(O obj) if (is(I == interface) && is(O : I)) { return obj; }
12 InterfaceProxyClass!(I, O) asInterface(I, O)(O obj) if (is(I == interface) && !is(O : I)) { return new InterfaceProxyClass!(I, O)(obj); }
13 
14 InterfaceProxyClass!(I, O) asInterface(I, O)(O obj, IAllocator allocator)
15 @trusted if (is(I == interface) && !is(O : I))
16 {
17 	alias R = InterfaceProxyClass!(I, O);
18 	return allocator.makeGCSafe!R(obj);
19 }
20 
21 void freeInterface(I, O)(InterfaceProxyClass!(I, O) inst, IAllocator allocator)
22 {
23 	allocator.disposeGCSafe(inst);
24 }
25 
26 FreeListRef!(InterfaceProxyClass!(I, O)) asInterfaceFL(I, O)(O obj) { return FreeListRef!(InterfaceProxyClass!(I, O))(obj); }
27 
28 InterfaceProxy!I interfaceProxy(I, O)(O o) { return InterfaceProxy!I(o); }
29 
30 private final class InterfaceProxyClass(I, O) : I
31 {
32 	import std.meta : AliasSeq;
33 	import std.traits : FunctionAttribute, MemberFunctionsTuple, ReturnType, ParameterTypeTuple, functionAttributes;
34 
35 	private {
36 		O m_obj;
37 	}
38 
39 	this(ref O obj) { swap(m_obj, obj); }
40 
41 	mixin methodDefs!0;
42 
43 	private mixin template methodDefs(size_t idx) {
44 		alias Members = AliasSeq!(__traits(allMembers, I));
45 		static if (idx < Members.length) {
46 			mixin overloadDefs!(Members[idx]);
47 			mixin methodDefs!(idx+1);
48 		}
49 	}
50 
51 	mixin template overloadDefs(string mem) {
52 		alias Overloads = MemberFunctionsTuple!(I, mem);
53 
54 		private static string impl()
55 		{
56 			import std.format : format;
57 			string ret;
58 			foreach (idx, F; Overloads) {
59 				alias R = ReturnType!F;
60 				enum attribs = functionAttributeString!F(false);
61 				static if (__traits(isVirtualMethod, F)) {
62 					static if (is(R == void))
63 						ret ~= q{override %s void %s(ParameterTypeTuple!(Overloads[%s]) params) { m_obj.%s(params); }}
64 							.format(attribs, mem, idx, mem);
65 					else
66 						ret ~= q{override %s ReturnType!(Overloads[%s]) %s(ParameterTypeTuple!(Overloads[%s]) params) { return m_obj.%s(params); }}
67 							.format(attribs, idx, mem, idx, mem);
68 				}
69 			}
70 			return ret;
71 		}
72 
73 		mixin(impl());
74 	}
75 }
76 
77 
78 
79 struct InterfaceProxy(I) if (is(I == interface)) {
80 	import std.meta : AliasSeq;
81 	import std.traits : FunctionAttribute, MemberFunctionsTuple, ReturnType, ParameterTypeTuple, functionAttributes;
82 	import vibe.internal.traits : checkInterfaceConformance;
83 
84 	private {
85 		void*[4] m_value;
86 		enum maxSize = m_value.length * m_value[0].sizeof;
87 		Proxy m_intf;
88 	}
89 
90 	this(IP : InterfaceProxy!J, J)(IP proxy) @safe
91 	{
92 		() @trusted {
93 			m_intf = proxy.m_intf;
94 			swap(proxy.m_value, m_value);
95 			proxy.m_intf = null;
96 		} ();
97 	}
98 
99 	this(O)(O object) @trusted
100 	{
101 		static assert(O.sizeof % m_value[0].sizeof == 0, "Sizeof object ("~O.stringof~") must be a multiple of a pointer size.");
102 		static assert(O.sizeof <= maxSize, "Object ("~O.stringof~") is too big to be stored in an InterfaceProxy.");
103 		import std.conv : emplace;
104 		m_intf = ProxyImpl!O.get();
105 		static if (is(O == struct))
106 			emplace!O(m_value[0 .. O.sizeof/m_value[0].sizeof]);
107 		swap((cast(O[])m_value[0 .. O.sizeof/m_value[0].sizeof])[0], object);
108 	}
109 
110 	~this() @safe
111 	{
112 		clear();
113 	}
114 
115 	this(this) @safe
116 	{
117 		if (m_intf) m_intf._postblit(m_value);
118 	}
119 
120 	void clear() @safe nothrow
121 	{
122 		if (m_intf) {
123 			m_intf._destroy(m_value);
124 			m_intf = null;
125 			m_value[] = null;
126 		}
127 	}
128 
129 	T extract(T)()
130 	@trusted nothrow {
131 		if (!m_intf || m_intf._typeInfo() !is typeid(T))
132 			assert(false, "Extraction of wrong type from InterfaceProxy.");
133 		return (cast(T[])m_value[0 .. T.sizeof/m_value[0].sizeof])[0];
134 	}
135 
136 	void opAssign(IP : InterfaceProxy!J, J)(IP proxy) @safe
137 	{
138 		static assert(is(J : I), "Can only assign InterfaceProxy instances of derived interfaces.");
139 
140 		clear();
141 		if (proxy.m_intf) {
142 			m_intf = proxy.m_intf;
143 			m_value[] = proxy.m_value[];
144 			proxy.m_intf = null;
145 		}
146 	}
147 
148 	void opAssign(O)(O object) @trusted
149 		if (checkInterfaceConformance!(O, I) is null)
150 	{
151 		static assert(O.sizeof % m_value[0].sizeof == 0, "Sizeof object ("~O.stringof~") must be a multiple of a pointer size.");
152 		static assert(O.sizeof <= maxSize, "Object is too big to be stored in an InterfaceProxy.");
153 		import std.conv : emplace;
154 		clear();
155 		m_intf = ProxyImpl!O.get();
156 		if (is(O == class))
157 			(cast(O[])m_value[0 .. O.sizeof/m_value[0].sizeof])[0] = object;
158 		else emplace!O(m_value[0 .. O.sizeof/m_value[0].sizeof]);
159 		swap((cast(O[])m_value[0 .. O.sizeof/m_value[0].sizeof])[0], object);
160 	}
161 
162 	bool opCast(T)() const @safe nothrow if (is(T == bool)) { return m_intf !is null; }
163 
164 	mixin allMethods!0;
165 
166 	private mixin template allMethods(size_t idx) {
167 		alias Members = AliasSeq!(__traits(allMembers, I));
168 		static if (idx < Members.length) {
169 			static if (__traits(compiles, __traits(getMember, I, Members[idx])))
170 				mixin overloadMethods!(Members[idx]);
171 			mixin allMethods!(idx+1);
172 		}
173 	}
174 
175 	private mixin template overloadMethods(string member) {
176 		alias Overloads = AliasSeq!(__traits(getOverloads, I, member));
177 
178 		private static string impl()
179 		{
180 			import std.format : format;
181 			string ret;
182 			foreach (idx, F; Overloads) {
183 				enum attribs = functionAttributeString!F(false);
184 				enum is_prop = functionAttributes!F & FunctionAttribute.property;
185 				ret ~= q{%s ReturnType!(Overloads[%s]) %s(%s) { return m_intf.%s(m_value, %s); }}
186 					.format(attribs, idx, member, parameterDecls!(F, idx), member, parameterNames!F);
187 			}
188 			return ret;
189 		}
190 
191 		mixin(impl());
192 	}
193 
194 	private interface Proxy : staticMap!(ProxyOf, BaseTypeTuple!I) {
195 		import std.meta : AliasSeq;
196 		import std.traits : FunctionAttribute, MemberFunctionsTuple, ReturnType, ParameterTypeTuple, functionAttributes;
197 
198 		void _destroy(void[] stor) @safe nothrow;
199 		void _postblit(void[] stor) @safe nothrow;
200 		TypeInfo _typeInfo() @safe nothrow;
201 
202 		mixin methodDecls!0;
203 
204 		private mixin template methodDecls(size_t idx) {
205 			alias Members = AliasSeq!(__traits(derivedMembers, I));
206 			static if (idx < Members.length) {
207 				static if (__traits(compiles, __traits(getMember, I, Members[idx])))
208 					mixin overloadDecls!(Members[idx]);
209 				mixin methodDecls!(idx+1);
210 			}
211 		}
212 
213 		private mixin template overloadDecls(string mem) {
214 			alias Overloads = AliasSeq!(__traits(getOverloads, I, mem));
215 
216 			private static string impl()
217 			{
218 				import std.format : format;
219 				string ret;
220 				foreach (idx, F; Overloads) {
221 					enum attribs = functionAttributeString!F(false);
222 					enum vtype = functionAttributeThisType!F("void[]");
223 					ret ~= q{ReturnType!(Overloads[%s]) %s(%s obj, %s) %s;}
224 						.format(idx, mem, vtype, parameterDecls!(F, idx), attribs);
225 				}
226 				return ret;
227 			}
228 
229 			mixin(impl());
230 		}
231 	}
232 
233 	static final class ProxyImpl(O) : Proxy {
234 		static auto get()
235 		{
236 			static ProxyImpl impl;
237 			if (!impl) impl = new ProxyImpl;
238 			return impl;
239 		}
240 
241 		override void _destroy(void[] stor)
242 		@trusted nothrow {
243 			static if (is(O == struct)) {
244 				try destroy(_extract(stor));
245 				catch (Exception e) assert(false, "Destructor has thrown: "~e.msg);
246 			}
247 		}
248 
249 		override void _postblit(void[] stor)
250 		@trusted nothrow {
251 			static if (is(O == struct)) {
252 				try typeid(O).postblit(stor.ptr);
253 				catch (Exception e) assert(false, "Postblit contructor has thrown: "~e.msg);
254 			}
255 		}
256 
257 		override TypeInfo _typeInfo()
258 		@safe nothrow {
259 			return typeid(O);
260 		}
261 
262 		static ref inout(O) _extract(inout(void)[] stor)
263 		@trusted nothrow pure @nogc {
264 			if (stor.length < O.sizeof) assert(false);
265 			return *cast(inout(O)*)stor.ptr;
266 		}
267 
268 		mixin methodDefs!0;
269 
270 		private mixin template methodDefs(size_t idx) {
271 			alias Members = AliasSeq!(__traits(allMembers, I));
272 			static if (idx < Members.length) {
273 				static if (__traits(compiles, __traits(getMember, I, Members[idx])))
274 					mixin overloadDefs!(Members[idx]);
275 				mixin methodDefs!(idx+1);
276 			}
277 		}
278 
279 		private mixin template overloadDefs(string mem) {
280 			alias Overloads = AliasSeq!(__traits(getOverloads, I, mem));
281 
282 			private static string impl()
283 			{
284 				import std.format : format;
285 				string ret;
286 				foreach (idx, F; Overloads) {
287 					alias R = ReturnType!F;
288 					alias P = ParameterTypeTuple!F;
289 					enum attribs = functionAttributeString!F(false);
290 					enum vtype = functionAttributeThisType!F("void[]");
291 
292 					static if (is(R == void))
293 						ret ~= q{override void %s(%s obj, %s) %s { _extract(obj).%s(%s); }}
294 							.format(mem, vtype, parameterDecls!(F, idx), attribs, mem, parameterNames!F);
295 					else
296 						ret ~= q{override ReturnType!(Overloads[%s]) %s(%s obj, %s) %s { return _extract(obj).%s(%s); }}
297 							.format(idx, mem, vtype, parameterDecls!(F, idx), attribs, mem, parameterNames!F);
298 				}
299 				return ret;
300 			}
301 
302 			mixin(impl());
303 		}
304 	}
305 }
306 
307 private string parameterDecls(alias F, size_t idx)()
308 {
309 	import std.format : format;
310 	import std.traits : ParameterTypeTuple, ParameterStorageClass, ParameterStorageClassTuple;
311 
312 	string ret;
313 	alias PST = ParameterStorageClassTuple!F;
314 	foreach (i, PT; ParameterTypeTuple!F) {
315 		static if (i > 0) ret ~= ", ";
316 		static if (PST[i] & ParameterStorageClass.scope_) ret ~= "scope ";
317 		static if (PST[i] & ParameterStorageClass.out_) ret ~= "out ";
318 		static if (PST[i] & ParameterStorageClass.ref_) ret ~= "ref ";
319 		static if (PST[i] & ParameterStorageClass.lazy_) ret ~= "lazy ";
320 		ret ~= format("ParameterTypeTuple!(Overloads[%s])[%s] param_%s", idx, i, i);
321 	}
322 	return ret;
323 }
324 
325 private string parameterNames(alias F)()
326 {
327 	import std.format : format;
328 	import std.traits : ParameterTypeTuple;
329 
330 	string ret;
331 	foreach (i, PT; ParameterTypeTuple!F) {
332 		static if (i > 0) ret ~= ", ";
333 		ret ~= format("param_%s", i);
334 	}
335 	return ret;
336 }
337 
338 private alias ProxyOf(I) = InterfaceProxy!I.Proxy;
339 
340 
341 unittest {
342 	static interface I {
343 		@property int count() const;
344 	}
345 
346 	static struct S {
347 		int* cnt;
348 		this(bool) { cnt = new int; *cnt = 1; }
349 		this(this) { if (cnt) (*cnt)++; }
350 		~this() { if (cnt) (*cnt)--; }
351 		@property int count() const { return cnt ? *cnt : 0; }
352 	}
353 
354 	auto s = S(true);
355 	assert(s.count == 1);
356 
357 	auto t = interfaceProxy!I(s);
358 	assert(s.count == 2);
359 
360 	t = interfaceProxy!I(s);
361 	assert(s.count == 2);
362 
363 	t = s;
364 	assert(s.count == 2);
365 
366 	s = S.init;
367 	assert(t.count == 1);
368 
369 	s = t.extract!S;
370 	assert(s.count == 2);
371 
372 	t = InterfaceProxy!I.init;
373 	assert(s.count == 1);
374 
375 	t = s;
376 	assert(s.count == 2);
377 
378 	s = S(true);
379 	assert(s.count == 1);
380 	assert(t.count == 1);
381 
382 	{
383 		InterfaceProxy!I u;
384 		u = s;
385 		assert(u.count == 2);
386 	}
387 	assert(s.count == 1);
388 }