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