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 }