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 		if (is(O == struct) || is(O == class) || is(O == interface))
101 	{
102 		static assert(O.sizeof % m_value[0].sizeof == 0, "Sizeof object ("~O.stringof~") must be a multiple of a pointer size.");
103 		static assert(O.sizeof <= maxSize, "Object ("~O.stringof~") is too big to be stored in an InterfaceProxy.");
104 		import std.conv : emplace;
105 
106 		static if (is(O == class) || is(O == interface)) {
107 			if (!object) return;
108 		}
109 
110 		m_intf = ProxyImpl!O.get();
111 		static if (is(O == struct))
112 			emplace!O(m_value[0 .. O.sizeof/m_value[0].sizeof]);
113 		swap((cast(O[])m_value[0 .. O.sizeof/m_value[0].sizeof])[0], object);
114 	}
115 
116 	this(typeof(null))
117 	{
118 	}
119 
120 	~this() @safe
121 	{
122 		clear();
123 	}
124 
125 	this(this) @safe
126 	{
127 		if (m_intf) m_intf._postblit(m_value);
128 	}
129 
130 	void clear() @safe nothrow
131 	{
132 		if (m_intf) {
133 			m_intf._destroy(m_value);
134 			m_intf = null;
135 			m_value[] = null;
136 		}
137 	}
138 
139 	T extract(T)()
140 	@trusted nothrow {
141 		if (!m_intf || m_intf._typeInfo() !is typeid(T))
142 			assert(false, "Extraction of wrong type from InterfaceProxy.");
143 		return (cast(T[])m_value[0 .. T.sizeof/m_value[0].sizeof])[0];
144 	}
145 
146 	void opAssign(IP : InterfaceProxy!J, J)(IP proxy) @safe
147 	{
148 		static assert(is(J : I), "Can only assign InterfaceProxy instances of derived interfaces.");
149 
150 		clear();
151 		if (proxy.m_intf) {
152 			m_intf = proxy.m_intf;
153 			m_value[] = proxy.m_value[];
154 			proxy.m_intf = null;
155 		}
156 	}
157 
158 	void opAssign(O)(O object) @trusted
159 		if (checkInterfaceConformance!(O, I) is null)
160 	{
161 		static assert(O.sizeof % m_value[0].sizeof == 0, "Sizeof object ("~O.stringof~") must be a multiple of a pointer size.");
162 		static assert(O.sizeof <= maxSize, "Object is too big to be stored in an InterfaceProxy.");
163 		import std.conv : emplace;
164 		clear();
165 		m_intf = ProxyImpl!O.get();
166 		static if (is(O == class))
167 			(cast(O[])m_value[0 .. O.sizeof/m_value[0].sizeof])[0] = object;
168 		else emplace!O(m_value[0 .. O.sizeof/m_value[0].sizeof]);
169 		swap((cast(O[])m_value[0 .. O.sizeof/m_value[0].sizeof])[0], object);
170 	}
171 
172 	bool opCast(T)() const @safe nothrow if (is(T == bool)) { return m_intf !is null; }
173 
174 	mixin allMethods!0;
175 
176 	private mixin template allMethods(size_t idx) {
177 		alias Members = AliasSeq!(__traits(allMembers, I));
178 		static if (idx < Members.length) {
179 			static if (__traits(compiles, __traits(getMember, I, Members[idx])))
180 				mixin overloadMethods!(Members[idx]);
181 			mixin allMethods!(idx+1);
182 		}
183 	}
184 
185 	private mixin template overloadMethods(string member) {
186 		alias Overloads = AliasSeq!(__traits(getOverloads, I, member));
187 
188 		private static string impl()
189 		{
190 			import std.format : format;
191 			string ret;
192 			foreach (idx, F; Overloads) {
193 				enum attribs = functionAttributeString!F(false);
194 				enum is_prop = functionAttributes!F & FunctionAttribute.property;
195 				ret ~= q{%s ReturnType!(Overloads[%s]) %s(%s) { return m_intf.%s(m_value, %s); }}
196 					.format(attribs, idx, member, parameterDecls!(F, idx), member, parameterNames!F);
197 			}
198 			return ret;
199 		}
200 
201 		mixin(impl());
202 	}
203 
204 	private interface Proxy : staticMap!(ProxyOf, BaseTypeTuple!I) {
205 		import std.meta : AliasSeq;
206 		import std.traits : FunctionAttribute, MemberFunctionsTuple, ReturnType, ParameterTypeTuple, functionAttributes;
207 
208 		void _destroy(void[] stor) @safe nothrow;
209 		void _postblit(void[] stor) @safe nothrow;
210 		TypeInfo _typeInfo() @safe nothrow;
211 
212 		mixin methodDecls!0;
213 
214 		private mixin template methodDecls(size_t idx) {
215 			alias Members = AliasSeq!(__traits(derivedMembers, I));
216 			static if (idx < Members.length) {
217 				static if (__traits(compiles, __traits(getMember, I, Members[idx])))
218 					mixin overloadDecls!(Members[idx]);
219 				mixin methodDecls!(idx+1);
220 			}
221 		}
222 
223 		private mixin template overloadDecls(string mem) {
224 			alias Overloads = AliasSeq!(__traits(getOverloads, I, mem));
225 
226 			private static string impl()
227 			{
228 				import std.format : format;
229 				string ret;
230 				foreach (idx, F; Overloads) {
231 					enum attribs = functionAttributeString!F(false);
232 					enum vtype = functionAttributeThisType!F("void[]");
233 					ret ~= q{ReturnType!(Overloads[%s]) %s(%s obj, %s) %s;}
234 						.format(idx, mem, vtype, parameterDecls!(F, idx), attribs);
235 				}
236 				return ret;
237 			}
238 
239 			mixin(impl());
240 		}
241 	}
242 
243 	static final class ProxyImpl(O) : Proxy {
244 		static auto get()
245 		{
246 			static ProxyImpl impl;
247 			if (!impl) impl = new ProxyImpl;
248 			return impl;
249 		}
250 
251 		override void _destroy(void[] stor)
252 		@trusted nothrow {
253 			static if (is(O == struct)) {
254 				try destroy(_extract(stor));
255 				catch (Exception e) assert(false, "Destructor has thrown: "~e.msg);
256 			}
257 		}
258 
259 		override void _postblit(void[] stor)
260 		@trusted nothrow {
261 			static if (is(O == struct)) {
262 				try typeid(O).postblit(stor.ptr);
263 				catch (Exception e) assert(false, "Postblit contructor has thrown: "~e.msg);
264 			}
265 		}
266 
267 		override TypeInfo _typeInfo()
268 		@safe nothrow {
269 			return typeid(O);
270 		}
271 
272 		static ref inout(O) _extract(inout(void)[] stor)
273 		@trusted nothrow pure @nogc {
274 			if (stor.length < O.sizeof) assert(false);
275 			return *cast(inout(O)*)stor.ptr;
276 		}
277 
278 		mixin methodDefs!0;
279 
280 		private mixin template methodDefs(size_t idx) {
281 			alias Members = AliasSeq!(__traits(allMembers, I));
282 			static if (idx < Members.length) {
283 				static if (__traits(compiles, __traits(getMember, I, Members[idx])))
284 					mixin overloadDefs!(Members[idx]);
285 				mixin methodDefs!(idx+1);
286 			}
287 		}
288 
289 		private mixin template overloadDefs(string mem) {
290 			alias Overloads = AliasSeq!(__traits(getOverloads, I, mem));
291 
292 			private static string impl()
293 			{
294 				import std.format : format;
295 				string ret;
296 				foreach (idx, F; Overloads) {
297 					alias R = ReturnType!F;
298 					alias P = ParameterTypeTuple!F;
299 					enum attribs = functionAttributeString!F(false);
300 					enum vtype = functionAttributeThisType!F("void[]");
301 
302 					static if (is(R == void))
303 						ret ~= q{override void %s(%s obj, %s) %s { _extract(obj).%s(%s); }}
304 							.format(mem, vtype, parameterDecls!(F, idx), attribs, mem, parameterNames!F);
305 					else
306 						ret ~= q{override ReturnType!(Overloads[%s]) %s(%s obj, %s) %s { return _extract(obj).%s(%s); }}
307 							.format(idx, mem, vtype, parameterDecls!(F, idx), attribs, mem, parameterNames!F);
308 				}
309 				return ret;
310 			}
311 
312 			mixin(impl());
313 		}
314 	}
315 }
316 
317 unittest {
318 	static interface I {}
319 	assert(!InterfaceProxy!I(null));
320 	assert(!InterfaceProxy!I(cast(I)null));
321 }
322 
323 private string parameterDecls(alias F, size_t idx)()
324 {
325 	import std.format : format;
326 	import std.traits : ParameterTypeTuple, ParameterStorageClass, ParameterStorageClassTuple;
327 
328 	string ret;
329 	alias PST = ParameterStorageClassTuple!F;
330 	foreach (i, PT; ParameterTypeTuple!F) {
331 		static if (i > 0) ret ~= ", ";
332 		static if (PST[i] & ParameterStorageClass.scope_) ret ~= "scope ";
333 		static if (PST[i] & ParameterStorageClass.out_) ret ~= "out ";
334 		static if (PST[i] & ParameterStorageClass.ref_) ret ~= "ref ";
335 		static if (PST[i] & ParameterStorageClass.lazy_) ret ~= "lazy ";
336 		ret ~= format("ParameterTypeTuple!(Overloads[%s])[%s] param_%s", idx, i, i);
337 	}
338 	return ret;
339 }
340 
341 private string parameterNames(alias F)()
342 {
343 	import std.format : format;
344 	import std.traits : ParameterTypeTuple;
345 
346 	string ret;
347 	foreach (i, PT; ParameterTypeTuple!F) {
348 		static if (i > 0) ret ~= ", ";
349 		ret ~= format("param_%s", i);
350 	}
351 	return ret;
352 }
353 
354 private alias ProxyOf(I) = InterfaceProxy!I.Proxy;
355 
356 
357 unittest {
358 	static interface I {
359 		@property int count() const;
360 	}
361 
362 	static struct S {
363 		int* cnt;
364 		this(bool) { cnt = new int; *cnt = 1; }
365 		this(this) { if (cnt) (*cnt)++; }
366 		~this() { if (cnt) (*cnt)--; }
367 		@property int count() const { return cnt ? *cnt : 0; }
368 	}
369 
370 	auto s = S(true);
371 	assert(s.count == 1);
372 
373 	auto t = interfaceProxy!I(s);
374 	assert(s.count == 2);
375 
376 	t = interfaceProxy!I(s);
377 	assert(s.count == 2);
378 
379 	t = s;
380 	assert(s.count == 2);
381 
382 	s = S.init;
383 	assert(t.count == 1);
384 
385 	s = t.extract!S;
386 	assert(s.count == 2);
387 
388 	t = InterfaceProxy!I.init;
389 	assert(s.count == 1);
390 
391 	t = s;
392 	assert(s.count == 2);
393 
394 	s = S(true);
395 	assert(s.count == 1);
396 	assert(t.count == 1);
397 
398 	{
399 		InterfaceProxy!I u;
400 		u = s;
401 		assert(u.count == 2);
402 	}
403 	assert(s.count == 1);
404 }