Home | History | Annotate | Line # | Download | only in dmd
lambdacomp.d revision 1.1
      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-2022 by The D Language Foundation, All Rights Reserved
      9  * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
     10  * License:     $(LINK2 https://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;
     29 import dmd.hdrgen;
     30 import dmd.mtype;
     31 import dmd.common.outbuffer;
     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('(');
    341         buf.writestring(EXPtoString(exp.op));
    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('(');
    374         buf.writestring(EXPtoString(exp.op).ptr);
    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 }
    497