]>
Commit | Line | Data |
---|---|---|
5fee5ec3 IB |
1 | /** |
2 | * Implements the serialization of a lambda function. | |
3 | * | |
4 | * The serializationis computed by visiting the abstract syntax subtree of the given lambda function. | |
5 | * The serialization is a string which contains the type of the parameters and the string | |
6 | * represantation of the lambda expression. | |
7 | * | |
8 | * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved | |
9 | * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) | |
10 | * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) | |
11 | * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/lamdbacomp.d, _lambdacomp.d) | |
12 | * Documentation: https://dlang.org/phobos/dmd_lambdacomp.html | |
13 | * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/lambdacomp.d | |
14 | */ | |
15 | ||
16 | module dmd.lambdacomp; | |
17 | ||
18 | import core.stdc.stdio; | |
19 | import core.stdc.string; | |
20 | ||
21 | import dmd.astenums; | |
22 | import dmd.declaration; | |
23 | import dmd.denum; | |
24 | import dmd.dsymbol; | |
25 | import dmd.dtemplate; | |
26 | import dmd.expression; | |
27 | import dmd.func; | |
28 | import dmd.dmangle; | |
9c7d5e88 | 29 | import dmd.hdrgen; |
5fee5ec3 | 30 | import dmd.mtype; |
0fb57034 | 31 | import dmd.common.outbuffer; |
5fee5ec3 IB |
32 | import dmd.root.rmem; |
33 | import dmd.root.stringtable; | |
34 | import dmd.dscope; | |
35 | import dmd.statement; | |
36 | import dmd.tokens; | |
37 | import dmd.visitor; | |
38 | ||
39 | enum LOG = false; | |
40 | ||
41 | /** | |
42 | * The type of the visited expression. | |
43 | */ | |
44 | private enum ExpType | |
45 | { | |
46 | None, | |
47 | EnumDecl, | |
48 | Arg | |
49 | } | |
50 | ||
51 | /** | |
52 | * Compares 2 lambda functions described by their serialization. | |
53 | * | |
54 | * Params: | |
55 | * l1 = first lambda to be compared | |
56 | * l2 = second lambda to be compared | |
57 | * sc = the scope where the lambdas are compared | |
58 | * | |
59 | * Returns: | |
60 | * `true` if the 2 lambda functions are equal, `false` otherwise | |
61 | */ | |
62 | bool isSameFuncLiteral(FuncLiteralDeclaration l1, FuncLiteralDeclaration l2, Scope* sc) | |
63 | { | |
64 | bool result; | |
65 | if (auto ser1 = getSerialization(l1, sc)) | |
66 | { | |
67 | //printf("l1 serialization: %.*s\n", cast(int)ser1.length, &ser1[0]); | |
68 | if (auto ser2 = getSerialization(l2, sc)) | |
69 | { | |
70 | //printf("l2 serialization: %.*s\n", cast(int)ser2.length, &ser2[0]); | |
71 | if (ser1 == ser2) | |
72 | result = true; | |
73 | mem.xfree(cast(void*)ser2.ptr); | |
74 | } | |
75 | mem.xfree(cast(void*)ser1.ptr); | |
76 | } | |
77 | return result; | |
78 | } | |
79 | ||
80 | /** | |
81 | * Computes the string representation of a | |
82 | * lambda function described by the subtree starting from a | |
83 | * $(REF dmd, func, FuncLiteralDeclaration). | |
84 | * | |
85 | * Limitations: only IntegerExps, Enums and function | |
86 | * arguments are supported in the lambda function body. The | |
87 | * arguments may be of any type (basic types, user defined types), | |
88 | * except template instantiations. If a function call, a local | |
89 | * variable or a template instance is encountered, the | |
90 | * serialization is dropped and the function is considered | |
91 | * uncomparable. | |
92 | * | |
93 | * Params: | |
94 | * fld = the starting AST node for the lambda function | |
95 | * sc = the scope in which the lambda function is located | |
96 | * | |
97 | * Returns: | |
98 | * The serialization of `fld` allocated with mem. | |
99 | */ | |
100 | private string getSerialization(FuncLiteralDeclaration fld, Scope* sc) | |
101 | { | |
102 | scope serVisitor = new SerializeVisitor(fld.parent._scope); | |
103 | fld.accept(serVisitor); | |
104 | const len = serVisitor.buf.length; | |
105 | if (len == 0) | |
106 | return null; | |
107 | ||
108 | return cast(string)serVisitor.buf.extractSlice(); | |
109 | } | |
110 | ||
111 | private extern (C++) class SerializeVisitor : SemanticTimeTransitiveVisitor | |
112 | { | |
113 | private: | |
114 | StringTable!(const(char)[]) arg_hash; | |
115 | Scope* sc; | |
116 | ExpType et; | |
117 | Dsymbol d; | |
118 | ||
119 | public: | |
120 | OutBuffer buf; | |
121 | alias visit = SemanticTimeTransitiveVisitor.visit; | |
122 | ||
123 | this(Scope* sc) | |
124 | { | |
125 | this.sc = sc; | |
126 | } | |
127 | ||
128 | /** | |
129 | * Entrypoint of the SerializeVisitor. | |
130 | * | |
131 | * Params: | |
132 | * fld = the lambda function for which the serialization is computed | |
133 | */ | |
134 | override void visit(FuncLiteralDeclaration fld) | |
135 | { | |
136 | assert(fld.type.ty != Terror); | |
137 | static if (LOG) | |
138 | printf("FuncLiteralDeclaration: %s\n", fld.toChars()); | |
139 | ||
140 | TypeFunction tf = cast(TypeFunction) fld.type; | |
141 | const dim = cast(uint) tf.parameterList.length; | |
142 | // Start the serialization by printing the number of | |
143 | // arguments the lambda has. | |
144 | buf.printf("%d:", dim); | |
145 | ||
146 | arg_hash._init(dim + 1); | |
147 | // For each argument | |
148 | foreach (i, fparam; tf.parameterList) | |
149 | { | |
150 | if (fparam.ident !is null) | |
151 | { | |
152 | // the variable name is introduced into a hashtable | |
153 | // where the key is the user defined name and the | |
154 | // value is the cannonically name (arg0, arg1 ...) | |
155 | auto key = fparam.ident.toString(); | |
156 | OutBuffer value; | |
157 | value.writestring("arg"); | |
158 | value.print(i); | |
159 | arg_hash.insert(key, value.extractSlice()); | |
160 | // and the type of the variable is serialized. | |
161 | fparam.accept(this); | |
162 | } | |
163 | } | |
164 | ||
165 | // Now the function body can be serialized. | |
166 | ReturnStatement rs = fld.fbody.endsWithReturnStatement(); | |
167 | if (rs && rs.exp) | |
168 | { | |
169 | rs.exp.accept(this); | |
170 | } | |
171 | else | |
172 | { | |
173 | buf.setsize(0); | |
174 | } | |
175 | } | |
176 | ||
177 | override void visit(DotIdExp exp) | |
178 | { | |
179 | static if (LOG) | |
180 | printf("DotIdExp: %s\n", exp.toChars()); | |
181 | if (buf.length == 0) | |
182 | return; | |
183 | ||
184 | // First we need to see what kind of expression e1 is. | |
185 | // It might an enum member (enum.value) or the field of | |
186 | // an argument (argX.value) if the argument is an aggregate | |
187 | // type. This is reported through the et variable. | |
188 | exp.e1.accept(this); | |
189 | if (buf.length == 0) | |
190 | return; | |
191 | ||
192 | if (et == ExpType.EnumDecl) | |
193 | { | |
194 | Dsymbol s = d.search(exp.loc, exp.ident); | |
195 | if (s) | |
196 | { | |
197 | if (auto em = s.isEnumMember()) | |
198 | { | |
199 | em.value.accept(this); | |
200 | } | |
201 | et = ExpType.None; | |
202 | d = null; | |
203 | } | |
204 | } | |
205 | ||
206 | else if (et == ExpType.Arg) | |
207 | { | |
208 | buf.setsize(buf.length -1); | |
209 | buf.writeByte('.'); | |
210 | buf.writestring(exp.ident.toString()); | |
211 | buf.writeByte('_'); | |
212 | } | |
213 | } | |
214 | ||
215 | bool checkArgument(const(char)* id) | |
216 | { | |
217 | // The identifier may be an argument | |
218 | auto stringtable_value = arg_hash.lookup(id, strlen(id)); | |
219 | if (stringtable_value) | |
220 | { | |
221 | // In which case we need to update the serialization accordingly | |
222 | const(char)[] gen_id = stringtable_value.value; | |
223 | buf.write(gen_id); | |
224 | buf.writeByte('_'); | |
225 | et = ExpType.Arg; | |
226 | return true; | |
227 | } | |
228 | return false; | |
229 | } | |
230 | ||
231 | override void visit(IdentifierExp exp) | |
232 | { | |
233 | static if (LOG) | |
234 | printf("IdentifierExp: %s\n", exp.toChars()); | |
235 | ||
236 | if (buf.length == 0) | |
237 | return; | |
238 | ||
239 | auto id = exp.ident.toChars(); | |
240 | ||
241 | // If it's not an argument | |
242 | if (!checkArgument(id)) | |
243 | { | |
244 | // we must check what the identifier expression is. | |
245 | Dsymbol scopesym; | |
246 | Dsymbol s = sc.search(exp.loc, exp.ident, &scopesym); | |
247 | if (s) | |
248 | { | |
249 | auto v = s.isVarDeclaration(); | |
250 | // If it's a VarDeclaration, it must be a manifest constant | |
251 | if (v && (v.storage_class & STC.manifest)) | |
252 | { | |
253 | v.getConstInitializer.accept(this); | |
254 | } | |
255 | else if (auto em = s.isEnumDeclaration()) | |
256 | { | |
257 | d = em; | |
258 | et = ExpType.EnumDecl; | |
259 | } | |
260 | else if (auto fd = s.isFuncDeclaration()) | |
261 | { | |
262 | writeMangledName(fd); | |
263 | } | |
264 | // For anything else, the function is deemed uncomparable | |
265 | else | |
266 | { | |
267 | buf.setsize(0); | |
268 | } | |
269 | } | |
270 | // If it's an unknown symbol, consider the function incomparable | |
271 | else | |
272 | { | |
273 | buf.setsize(0); | |
274 | } | |
275 | } | |
276 | } | |
277 | ||
278 | override void visit(DotVarExp exp) | |
279 | { | |
280 | static if (LOG) | |
281 | printf("DotVarExp: %s, var: %s, e1: %s\n", exp.toChars(), | |
282 | exp.var.toChars(), exp.e1.toChars()); | |
283 | ||
284 | exp.e1.accept(this); | |
285 | if (buf.length == 0) | |
286 | return; | |
287 | ||
288 | buf.setsize(buf.length -1); | |
289 | buf.writeByte('.'); | |
290 | buf.writestring(exp.var.toChars()); | |
291 | buf.writeByte('_'); | |
292 | } | |
293 | ||
294 | override void visit(VarExp exp) | |
295 | { | |
296 | static if (LOG) | |
297 | printf("VarExp: %s, var: %s\n", exp.toChars(), exp.var.toChars()); | |
298 | ||
299 | if (buf.length == 0) | |
300 | return; | |
301 | ||
302 | auto id = exp.var.ident.toChars(); | |
303 | if (!checkArgument(id)) | |
304 | { | |
305 | buf.setsize(0); | |
306 | } | |
307 | } | |
308 | ||
309 | // serialize function calls | |
310 | override void visit(CallExp exp) | |
311 | { | |
312 | static if (LOG) | |
313 | printf("CallExp: %s\n", exp.toChars()); | |
314 | ||
315 | if (buf.length == 0) | |
316 | return; | |
317 | ||
318 | if (!exp.f) | |
319 | { | |
320 | exp.e1.accept(this); | |
321 | } | |
322 | else | |
323 | { | |
324 | writeMangledName(exp.f); | |
325 | } | |
326 | ||
327 | buf.writeByte('('); | |
328 | foreach (arg; *(exp.arguments)) | |
329 | { | |
330 | arg.accept(this); | |
331 | } | |
332 | buf.writeByte(')'); | |
333 | } | |
334 | ||
335 | override void visit(UnaExp exp) | |
336 | { | |
337 | if (buf.length == 0) | |
338 | return; | |
339 | ||
340 | buf.writeByte('('); | |
9c7d5e88 | 341 | buf.writestring(EXPtoString(exp.op)); |
5fee5ec3 IB |
342 | exp.e1.accept(this); |
343 | if (buf.length != 0) | |
344 | buf.writestring(")_"); | |
345 | } | |
346 | ||
347 | override void visit(IntegerExp exp) | |
348 | { | |
349 | if (buf.length == 0) | |
350 | return; | |
351 | ||
352 | buf.print(exp.toInteger()); | |
353 | buf.writeByte('_'); | |
354 | } | |
355 | ||
356 | override void visit(RealExp exp) | |
357 | { | |
358 | if (buf.length == 0) | |
359 | return; | |
360 | ||
361 | buf.writestring(exp.toChars()); | |
362 | buf.writeByte('_'); | |
363 | } | |
364 | ||
365 | override void visit(BinExp exp) | |
366 | { | |
367 | static if (LOG) | |
368 | printf("BinExp: %s\n", exp.toChars()); | |
369 | ||
370 | if (buf.length == 0) | |
371 | return; | |
372 | ||
373 | buf.writeByte('('); | |
9c7d5e88 | 374 | buf.writestring(EXPtoString(exp.op).ptr); |
5fee5ec3 IB |
375 | |
376 | exp.e1.accept(this); | |
377 | if (buf.length == 0) | |
378 | return; | |
379 | ||
380 | exp.e2.accept(this); | |
381 | if (buf.length == 0) | |
382 | return; | |
383 | ||
384 | buf.writeByte(')'); | |
385 | } | |
386 | ||
387 | override void visit(TypeBasic t) | |
388 | { | |
389 | buf.writestring(t.dstring); | |
390 | buf.writeByte('_'); | |
391 | } | |
392 | ||
393 | void writeMangledName(Dsymbol s) | |
394 | { | |
395 | if (s) | |
396 | { | |
397 | OutBuffer mangledName; | |
398 | mangleToBuffer(s, &mangledName); | |
399 | buf.writestring(mangledName[]); | |
400 | buf.writeByte('_'); | |
401 | } | |
402 | else | |
403 | buf.setsize(0); | |
404 | } | |
405 | ||
406 | private bool checkTemplateInstance(T)(T t) | |
407 | if (is(T == TypeStruct) || is(T == TypeClass)) | |
408 | { | |
409 | if (t.sym.parent && t.sym.parent.isTemplateInstance()) | |
410 | { | |
411 | buf.setsize(0); | |
412 | return true; | |
413 | } | |
414 | return false; | |
415 | } | |
416 | ||
417 | override void visit(TypeStruct t) | |
418 | { | |
419 | static if (LOG) | |
420 | printf("TypeStruct: %s\n", t.toChars); | |
421 | ||
422 | if (!checkTemplateInstance!TypeStruct(t)) | |
423 | writeMangledName(t.sym); | |
424 | } | |
425 | ||
426 | override void visit(TypeClass t) | |
427 | { | |
428 | static if (LOG) | |
429 | printf("TypeClass: %s\n", t.toChars()); | |
430 | ||
431 | if (!checkTemplateInstance!TypeClass(t)) | |
432 | writeMangledName(t.sym); | |
433 | } | |
434 | ||
435 | override void visit(Parameter p) | |
436 | { | |
437 | if (p.type.ty == Tident | |
438 | && (cast(TypeIdentifier)p.type).ident.toString().length > 3 | |
439 | && strncmp((cast(TypeIdentifier)p.type).ident.toChars(), "__T", 3) == 0) | |
440 | { | |
441 | buf.writestring("none_"); | |
442 | } | |
443 | else | |
444 | visitType(p.type); | |
445 | } | |
446 | ||
447 | override void visit(StructLiteralExp e) { | |
448 | static if (LOG) | |
449 | printf("StructLiteralExp: %s\n", e.toChars); | |
450 | ||
451 | auto ty = cast(TypeStruct)e.stype; | |
452 | if (ty) | |
453 | { | |
454 | writeMangledName(ty.sym); | |
455 | auto dim = e.elements.dim; | |
456 | foreach (i; 0..dim) | |
457 | { | |
458 | auto elem = (*e.elements)[i]; | |
459 | if (elem) | |
460 | elem.accept(this); | |
461 | else | |
462 | buf.writestring("null_"); | |
463 | } | |
464 | } | |
465 | else | |
466 | buf.setsize(0); | |
467 | } | |
468 | ||
469 | override void visit(ArrayLiteralExp) { buf.setsize(0); } | |
470 | override void visit(AssocArrayLiteralExp) { buf.setsize(0); } | |
471 | override void visit(MixinExp) { buf.setsize(0); } | |
472 | override void visit(ComplexExp) { buf.setsize(0); } | |
473 | override void visit(DeclarationExp) { buf.setsize(0); } | |
474 | override void visit(DefaultInitExp) { buf.setsize(0); } | |
475 | override void visit(DsymbolExp) { buf.setsize(0); } | |
476 | override void visit(ErrorExp) { buf.setsize(0); } | |
477 | override void visit(FuncExp) { buf.setsize(0); } | |
478 | override void visit(HaltExp) { buf.setsize(0); } | |
479 | override void visit(IntervalExp) { buf.setsize(0); } | |
480 | override void visit(IsExp) { buf.setsize(0); } | |
481 | override void visit(NewAnonClassExp) { buf.setsize(0); } | |
482 | override void visit(NewExp) { buf.setsize(0); } | |
483 | override void visit(NullExp) { buf.setsize(0); } | |
484 | override void visit(ObjcClassReferenceExp) { buf.setsize(0); } | |
485 | override void visit(OverExp) { buf.setsize(0); } | |
486 | override void visit(ScopeExp) { buf.setsize(0); } | |
487 | override void visit(StringExp) { buf.setsize(0); } | |
488 | override void visit(SymbolExp) { buf.setsize(0); } | |
489 | override void visit(TemplateExp) { buf.setsize(0); } | |
490 | override void visit(ThisExp) { buf.setsize(0); } | |
491 | override void visit(TraitsExp) { buf.setsize(0); } | |
492 | override void visit(TupleExp) { buf.setsize(0); } | |
493 | override void visit(TypeExp) { buf.setsize(0); } | |
494 | override void visit(TypeidExp) { buf.setsize(0); } | |
495 | override void visit(VoidInitExp) { buf.setsize(0); } | |
496 | } |