001    /* Copyright (c) 2014, Erik Hogeman <Erik.Hogemn@gmail.com>
002     * All rights reserved.
003     *
004     * Redistribution and use in source and binary forms, with or without
005     * modification, are permitted provided that the following conditions are met:
006     *
007     *     * Redistributions of source code must retain the above copyright notice,
008     *       this list of conditions and the following disclaimer.
009     *     * Redistributions in binary form must reproduce the above copyright
010     *       notice, this list of conditions and the following disclaimer in the
011     *       documentation and/or other materials provided with the distribution.
012     *     * Neither the name of the Lund University nor the names of its
013     *       contributors may be used to endorse or promote products derived from
014     *       this software without specific prior written permission.
015     *
016     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
017     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
018     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
019     * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
020     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
021     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
022     * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
023     * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
024     * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
025     * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
026     * POSSIBILITY OF SUCH DAMAGE.
027     */
028    aspect MethodReferenceToClass {
029      // Name used when creating field declaration
030      private String ExprMethodReference.syntheticFieldName() {
031        return "#1";
032      }
033    
034      // Build an anonymous class which will be converted to byte code
035      // Since method access can target a generic method, type variables have to be taken into account
036      // by using an access that bypasses the normal type lookup
037      syn nta lazy ClassInstanceExpr ExprMethodReference.toClass() {
038        List<Access> implementsList = new List<Access>();
039        InterfaceDecl iDecl = targetInterface();
040    
041        // First compute the interface implemented by the anonymous class
042        Access implementsInterface = iDecl.createQualifiedAccess();
043        implementsList.add(implementsInterface);
044    
045        // Next compute the BodyDecls for the anonymous class
046        List<BodyDecl> bodyDecls = new List<BodyDecl>();
047    
048        FieldDeclaration fieldDecl = null;
049        // If this reference uses a primary or expression name, must evaluate that part first
050        if (!(getExpr() instanceof Access) || !(((Access) getExpr()).lastAccess() instanceof SuperAccess)) {
051          fieldDecl = new FieldDeclaration(new Modifiers(),
052              getExpr().type().createQualifiedAccess(), syntheticFieldName(), (Expr) getExpr().treeCopyNoTransform());
053          bodyDecls.add(fieldDecl);
054        }
055    
056        // Then we must build the method overriding the abstract methods
057    
058        Modifiers methodModifiers = new Modifiers(new List<Modifier>().add(new Modifier("public")));
059        Access returnType = new SyntheticTypeAccess(iDecl.functionDescriptor().method.type());
060        List<ParameterDeclaration> methodParams = toParameterList();
061        List<Access> methodThrows = new List<Access>();
062        for (TypeDecl throwsType : iDecl.functionDescriptor().throwsList) {
063          methodThrows.add(new SyntheticTypeAccess(throwsType));
064        }
065        Opt<Block> methodBlock = new Opt<Block>(toBlock());
066        MethodDecl method = new MethodDecl(methodModifiers, returnType, iDecl.functionDescriptor().method.name(),
067                          methodParams, methodThrows, methodBlock);
068    
069        bodyDecls.add(method);
070    
071        /* Now the anonymous class can be built. Must use the type LambdaAnonymousDecl instead
072        of a normal AnonymousDecl in order for this and super keywords to get the type of the outer
073        scope. */
074        LambdaAnonymousDecl anonymousDecl = new LambdaAnonymousDecl(new Modifiers(), "MethodReference", bodyDecls);
075        for (Access impl: implementsList) {
076          anonymousDecl.addImplements(impl);
077        }
078    
079        return new ClassInstanceExpr((Access) implementsInterface.treeCopyNoTransform(), new List<Expr>(), new Opt<TypeDecl>(anonymousDecl));
080      }
081    
082      syn lazy List<ParameterDeclaration> MethodReference.toParameterList() {
083        List<ParameterDeclaration> list = new List<ParameterDeclaration>();
084        for (int i = 0; i < targetInterface().functionDescriptor().method.getNumParameter(); i++) {
085          TypeDecl paramType = targetInterface().functionDescriptor().method.getParameter(i).type();
086          String paramName = targetInterface().functionDescriptor().method.getParameter(i).name();
087          list.add(new ParameterDeclaration(new SyntheticTypeAccess(paramType), paramName));
088        }
089        return list;
090      }
091    
092      // The input variable is the field declaration that stores the evaluated expression
093      syn lazy Block ExprMethodReference.toBlock() {
094        Expr qualifier = null;
095        if (getExpr() instanceof Access && ((Access) getExpr()).lastAccess() instanceof SuperAccess) {
096          qualifier = (Expr) getExpr().treeCopyNoTransform();
097        } else {
098          qualifier = new VarAccess(syntheticFieldName());
099        }
100    
101        List<Expr> arguments = new List<Expr>();
102        for (int i = 0; i < targetInterface().functionDescriptor().method.getNumParameter(); i++) {
103          String paramName = targetInterface().functionDescriptor().method.getParameter(i).name();
104          arguments.add(new VarAccess(paramName));
105        }
106    
107        MethodAccess m = null;
108        if (!hasTypeArgument()) {
109          m = new MethodAccess(name(), arguments);
110        } else {
111          m = new ParMethodAccess(name(), arguments, (List<Access>)getTypeArgumentList().treeCopyNoTransform());
112        }
113        Access qualifiedMethod = qualifier.qualifiesAccess(m);
114        Stmt blockStmt = null;
115        if (targetInterface().functionDescriptor().method.type().isVoid()) {
116          blockStmt = new ExprStmt(qualifiedMethod);
117        } else {
118          blockStmt = new ReturnStmt(qualifiedMethod);
119        }
120        List<Stmt> stmtList = new List<Stmt>();
121        stmtList.add(blockStmt);
122        return new Block(stmtList);
123      }
124    
125    
126    
127      // Below is the code for TypeMethodReferences
128    
129      syn nta lazy ClassInstanceExpr TypeMethodReference.toClass() {
130        List<Access> implementsList = new List<Access>();
131        InterfaceDecl iDecl = targetInterface();
132    
133        // First compute the interface implemented by the anonymous class
134        Access implementsInterface = iDecl.createQualifiedAccess();
135        implementsList.add(implementsInterface);
136    
137        // Next compute the BodyDecl for the anonymous class
138        List<BodyDecl> bodyDecls = new List<BodyDecl>();
139    
140        // For TypeMethodReferenes, there is only one body decl, the method
141    
142        Modifiers methodModifiers = new Modifiers(new List<Modifier>().add(new Modifier("public")));
143        Access returnType = new SyntheticTypeAccess(iDecl.functionDescriptor().method.type());
144        List<ParameterDeclaration> methodParams = toParameterList();
145        List<Access> methodThrows = new List<Access>();
146        for (TypeDecl throwsType : iDecl.functionDescriptor().throwsList) {
147          methodThrows.add(new SyntheticTypeAccess(throwsType));
148        }
149        Opt<Block> methodBlock = new Opt<Block>(toBlock());
150        MethodDecl method = new MethodDecl(methodModifiers, returnType, iDecl.functionDescriptor().method.name(),
151                          methodParams, methodThrows, methodBlock);
152    
153        bodyDecls.add(method);
154    
155        /* Now the anonymous class can be built. Must use the type LambdaAnonymousDecl instead
156        of a normal AnonymousDecl in order for this and super keywords to get the type of the outer
157        scope. */
158        LambdaAnonymousDecl anonymousDecl = new LambdaAnonymousDecl(new Modifiers(), "MethodReference", bodyDecls);
159        for (Access impl: implementsList) {
160          anonymousDecl.addImplements(impl);
161        }
162    
163        return new ClassInstanceExpr((Access) implementsInterface.treeCopyNoTransform(), new List<Expr>(), new Opt<TypeDecl>(anonymousDecl));
164      }
165    
166      syn lazy Block TypeMethodReference.toBlock() {
167        Expr qualifier = null;
168        List<Expr> arguments = new List<Expr>();
169        FunctionDescriptor f = targetInterface().functionDescriptor();
170        // Should create access to instance method
171        if (!validStaticMethod(f)) {
172          qualifier = new VarAccess(targetInterface().functionDescriptor().method.getParameter(0).name());
173          for (int i = 1; i < targetInterface().functionDescriptor().method.getNumParameter(); i++) {
174            String paramName = targetInterface().functionDescriptor().method.getParameter(i).name();
175            arguments.add(new VarAccess(paramName));
176          }
177        }
178        // Should create access to static method
179        else {
180          qualifier = (Access) getTypeAccess().treeCopyNoTransform();
181          for (int i = 0; i < targetInterface().functionDescriptor().method.getNumParameter(); i++) {
182            String paramName = targetInterface().functionDescriptor().method.getParameter(i).name();
183            arguments.add(new VarAccess(paramName));
184          }
185        }
186    
187        MethodAccess m = null;
188        if (!hasTypeArgument()) {
189          m = new MethodAccess(name(), arguments);
190        } else {
191          m = new ParMethodAccess(name(), arguments, (List<Access>)getTypeArgumentList().treeCopyNoTransform());
192        }
193        Access qualifiedMethod = qualifier.qualifiesAccess(m);
194        Stmt blockStmt = null;
195        if (targetInterface().functionDescriptor().method.type().isVoid()) {
196          blockStmt = new ExprStmt(qualifiedMethod);
197        } else {
198          blockStmt = new ReturnStmt(qualifiedMethod);
199        }
200        List<Stmt> stmtList = new List<Stmt>();
201        stmtList.add(blockStmt);
202        return new Block(stmtList);
203      }
204    }