org.aspectj.org.eclipse.jdt.internal.eval.CodeSnippetMessageSend Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aspectjtools Show documentation
Show all versions of aspectjtools Show documentation
Tools from the AspectJ project
/*******************************************************************************
* Copyright (c) 2000, 2014 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Andy Clement (GoPivotal, Inc) [email protected] - Contributions for
* Bug 409245 - [1.8][compiler] Type annotations dropped when call is routed through a synthetic bridge method
* Bug 409250 - [1.8][compiler] Various loose ends in 308 code generation
* Stephan Herrmann - Contribution for
* Bug 400874 - [1.8][compiler] Inference infrastructure should evolve to meet JLS8 18.x (Part G of JSR335 spec)
* Bug 424415 - [1.8][compiler] Eventual resolution of ReferenceExpression is not seen to be happening.
* Bug 427438 - [1.8][compiler] NPE at org.aspectj.org.eclipse.jdt.internal.compiler.ast.ConditionalExpression.generateCode(ConditionalExpression.java:280)
*******************************************************************************/
package org.aspectj.org.eclipse.jdt.internal.eval;
import static org.aspectj.org.eclipse.jdt.internal.compiler.ast.ExpressionContext.INVOCATION_CONTEXT;
import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.CastExpression;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Expression;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.InnerInferenceHelper;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.NameReference;
import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.aspectj.org.eclipse.jdt.internal.compiler.codegen.CodeStream;
import org.aspectj.org.eclipse.jdt.internal.compiler.codegen.Opcodes;
import org.aspectj.org.eclipse.jdt.internal.compiler.flow.FlowInfo;
import org.aspectj.org.eclipse.jdt.internal.compiler.impl.Constant;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ProblemMethodBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
public class CodeSnippetMessageSend extends MessageSend {
EvaluationContext evaluationContext;
FieldBinding delegateThis;
/**
* CodeSnippetMessageSend constructor comment.
*/
public CodeSnippetMessageSend(EvaluationContext evaluationContext) {
this.evaluationContext = evaluationContext;
}
/**
* MessageSend code generation
*
* @param currentScope org.aspectj.org.eclipse.jdt.internal.compiler.lookup.BlockScope
* @param codeStream org.aspectj.org.eclipse.jdt.internal.compiler.codegen.CodeStream
* @param valueRequired boolean
*/
public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
int pc = codeStream.position;
MethodBinding codegenBinding = this.binding.original();
if (codegenBinding.canBeSeenBy(this.actualReceiverType, this, currentScope)) {
// generate receiver/enclosing instance access
boolean isStatic = codegenBinding.isStatic();
// outer access ?
if (!isStatic && ((this.bits & DepthMASK) != 0)) {
// outer method can be reached through emulation
ReferenceBinding targetType = currentScope.enclosingSourceType().enclosingTypeAt((this.bits & DepthMASK) >> DepthSHIFT);
Object[] path = currentScope.getEmulationPath(targetType, true /*only exact match*/, false/*consider enclosing arg*/);
if (path == null) {
// emulation was not possible (should not happen per construction)
currentScope.problemReporter().needImplementation(this);
} else {
codeStream.generateOuterAccess(path, this, targetType, currentScope);
}
} else {
this.receiver.generateCode(currentScope, codeStream, !isStatic);
if ((this.bits & NeedReceiverGenericCast) != 0) {
codeStream.checkcast(this.actualReceiverType);
}
codeStream.recordPositionsFrom(pc, this.sourceStart);
}
// generate arguments
generateArguments(this.binding, this.arguments, currentScope, codeStream);
// actual message invocation
TypeBinding constantPoolDeclaringClass = CodeStream.getConstantPoolDeclaringClass(currentScope, codegenBinding, this.actualReceiverType, this.receiver.isImplicitThis());
if (isStatic) {
codeStream.invoke(Opcodes.OPC_invokestatic, codegenBinding, constantPoolDeclaringClass, this.typeArguments);
} else if( (this.receiver.isSuper()) || codegenBinding.isPrivate()){
codeStream.invoke(Opcodes.OPC_invokespecial, codegenBinding, constantPoolDeclaringClass, this.typeArguments);
} else {
if (constantPoolDeclaringClass.isInterface()) { // interface or annotation type
codeStream.invoke(Opcodes.OPC_invokeinterface, codegenBinding, constantPoolDeclaringClass, this.typeArguments);
} else {
codeStream.invoke(Opcodes.OPC_invokevirtual, codegenBinding, constantPoolDeclaringClass, this.typeArguments);
}
}
} else {
codeStream.generateEmulationForMethod(currentScope, codegenBinding);
// generate receiver/enclosing instance access
boolean isStatic = codegenBinding.isStatic();
// outer access ?
if (!isStatic && ((this.bits & DepthMASK) != 0)) {
// not supported yet
currentScope.problemReporter().needImplementation(this);
} else {
this.receiver.generateCode(currentScope, codeStream, !isStatic);
if ((this.bits & NeedReceiverGenericCast) != 0) {
codeStream.checkcast(this.actualReceiverType);
}
codeStream.recordPositionsFrom(pc, this.sourceStart);
}
if (isStatic) {
// we need an object on the stack which is ignored for the method invocation
codeStream.aconst_null();
}
// generate arguments
if (this.arguments != null) {
int argsLength = this.arguments.length;
codeStream.generateInlinedValue(argsLength);
codeStream.newArray(currentScope.createArrayType(currentScope.getType(TypeConstants.JAVA_LANG_OBJECT, 3), 1));
codeStream.dup();
for (int i = 0; i < argsLength; i++) {
codeStream.generateInlinedValue(i);
this.arguments[i].generateCode(currentScope, codeStream, true);
TypeBinding parameterBinding = codegenBinding.parameters[i];
if (parameterBinding.isBaseType() && parameterBinding != TypeBinding.NULL) {
codeStream.generateBoxingConversion(codegenBinding.parameters[i].id);
}
codeStream.aastore();
if (i < argsLength - 1) {
codeStream.dup();
}
}
} else {
codeStream.generateInlinedValue(0);
codeStream.newArray(currentScope.createArrayType(currentScope.getType(TypeConstants.JAVA_LANG_OBJECT, 3), 1));
}
codeStream.invokeJavaLangReflectMethodInvoke();
// convert the return value to the appropriate type for primitive types
if (codegenBinding.returnType.isBaseType()) {
int typeID = codegenBinding.returnType.id;
if (typeID == T_void) {
// remove the null from the stack
codeStream.pop();
}
codeStream.checkcast(typeID);
codeStream.getBaseTypeValue(typeID);
} else {
codeStream.checkcast(codegenBinding.returnType);
}
}
// required cast must occur even if no value is required
if (this.valueCast != null) codeStream.checkcast(this.valueCast);
if (valueRequired){
// implicit conversion if necessary
codeStream.generateImplicitConversion(this.implicitConversion);
} else {
boolean isUnboxing = (this.implicitConversion & TypeIds.UNBOXING) != 0;
// conversion only generated if unboxing
if (isUnboxing) codeStream.generateImplicitConversion(this.implicitConversion);
switch (isUnboxing ? postConversionType(currentScope).id : codegenBinding.returnType.id) {
case T_long :
case T_double :
codeStream.pop2();
break;
case T_void :
break;
default :
codeStream.pop();
}
}
codeStream.recordPositionsFrom(pc, (int)(this.nameSourcePosition >>> 32)); // highlight selector
}
public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) {
if ((flowInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) == 0) {
// if method from parameterized type got found, use the original method at codegen time
MethodBinding codegenBinding = this.binding.original();
if (codegenBinding != this.binding) {
// extra cast needed if method return type was type variable
if (codegenBinding.returnType.isTypeVariable()) {
TypeVariableBinding variableReturnType = (TypeVariableBinding) codegenBinding.returnType;
if (TypeBinding.notEquals(variableReturnType.firstBound, this.binding.returnType)) { // no need for extra cast if same as first bound anyway
this.valueCast = this.binding.returnType;
}
}
}
}
}
public TypeBinding resolveType(BlockScope scope) {
// Answer the signature return type
// Base type promotion
this.constant = Constant.NotAConstant;
boolean receiverCast = false, argsContainCast = false;
if (this.receiver instanceof CastExpression) {
this.receiver.bits |= DisableUnnecessaryCastCheck; // will check later on
receiverCast = true;
}
this.actualReceiverType = this.receiver.resolveType(scope);
if (receiverCast && this.actualReceiverType != null) {
// due to change of declaring class with receiver type, only identity cast should be notified
if (TypeBinding.equalsEquals(((CastExpression)this.receiver).expression.resolvedType, this.actualReceiverType)) {
scope.problemReporter().unnecessaryCast((CastExpression)this.receiver);
}
}
// resolve type arguments (for generic constructor call)
if (this.typeArguments != null) {
int length = this.typeArguments.length;
boolean argHasError = false; // typeChecks all arguments
this.genericTypeArguments = new TypeBinding[length];
for (int i = 0; i < length; i++) {
if ((this.genericTypeArguments[i] = this.typeArguments[i].resolveType(scope, true /* check bounds*/)) == null) {
argHasError = true;
}
}
if (argHasError) {
return null;
}
}
// will check for null after args are resolved
TypeBinding[] argumentTypes = Binding.NO_PARAMETERS;
if (this.arguments != null) {
boolean argHasError = false; // typeChecks all arguments
int length = this.arguments.length;
argumentTypes = new TypeBinding[length];
TypeBinding argumentType;
for (int i = 0; i < length; i++) {
Expression argument = this.arguments[i];
if (argument instanceof CastExpression) {
argument.bits |= DisableUnnecessaryCastCheck; // will check later on
argsContainCast = true;
}
argument.setExpressionContext(INVOCATION_CONTEXT);
if ((argumentType = argumentTypes[i] = this.arguments[i].resolveType(scope)) == null)
argHasError = true;
if (argumentType != null && argumentType.kind() == Binding.POLY_TYPE) {
if (this.innerInferenceHelper == null)
this.innerInferenceHelper = new InnerInferenceHelper();
}
}
if (argHasError) {
if(this.actualReceiverType instanceof ReferenceBinding) {
// record any selector match, for clients who may still need hint about possible method match
this.binding = scope.findMethod((ReferenceBinding)this.actualReceiverType, this.selector, new TypeBinding[]{}, this, false);
}
return null;
}
}
if (this.actualReceiverType == null) {
return null;
}
// base type cannot receive any message
if (this.actualReceiverType.isBaseType()) {
scope.problemReporter().errorNoMethodFor(this, this.actualReceiverType, argumentTypes);
return null;
}
findMethodBinding(scope, argumentTypes);
if (!this.binding.isValidBinding()) {
if (this.binding instanceof ProblemMethodBinding
&& ((ProblemMethodBinding) this.binding).problemId() == ProblemReasons.NotVisible) {
if (this.evaluationContext.declaringTypeName != null) {
this.delegateThis = scope.getField(scope.enclosingSourceType(), EvaluationConstants.DELEGATE_THIS, this);
if (this.delegateThis == null){ // if not found then internal error, field should have been found
this.constant = Constant.NotAConstant;
scope.problemReporter().invalidMethod(this, this.binding);
return null;
}
} else {
this.constant = Constant.NotAConstant;
scope.problemReporter().invalidMethod(this, this.binding);
return null;
}
CodeSnippetScope localScope = new CodeSnippetScope(scope);
MethodBinding privateBinding =
this.receiver instanceof CodeSnippetThisReference && ((CodeSnippetThisReference) this.receiver).isImplicit
? localScope.getImplicitMethod((ReferenceBinding)this.delegateThis.type, this.selector, argumentTypes, this)
: localScope.getMethod(this.delegateThis.type, this.selector, argumentTypes, this);
if (!privateBinding.isValidBinding()) {
if (this.binding.declaringClass == null) {
if (this.actualReceiverType instanceof ReferenceBinding) {
this.binding.declaringClass = (ReferenceBinding) this.actualReceiverType;
} else { // really bad error ....
scope.problemReporter().errorNoMethodFor(this, this.actualReceiverType, argumentTypes);
return null;
}
}
scope.problemReporter().invalidMethod(this, this.binding);
return null;
} else {
this.binding = privateBinding;
}
} else {
if (this.binding.declaringClass == null) {
if (this.actualReceiverType instanceof ReferenceBinding) {
this.binding.declaringClass = (ReferenceBinding) this.actualReceiverType;
} else { // really bad error ....
scope.problemReporter().errorNoMethodFor(this, this.actualReceiverType, argumentTypes);
return null;
}
}
scope.problemReporter().invalidMethod(this, this.binding);
return null;
}
}
if (!this.binding.isStatic()) {
// the "receiver" must not be a type, in other words, a NameReference that the TC has bound to a Type
if (this.receiver instanceof NameReference
&& (((NameReference) this.receiver).bits & Binding.TYPE) != 0) {
scope.problemReporter().mustUseAStaticMethod(this, this.binding);
} else {
// handle indirect inheritance thru variable secondary bound
// receiver may receive generic cast, as part of implicit conversion
TypeBinding oldReceiverType = this.actualReceiverType;
this.actualReceiverType = this.actualReceiverType.getErasureCompatibleType(this.binding.declaringClass);
this.receiver.computeConversion(scope, this.actualReceiverType, this.actualReceiverType);
if (TypeBinding.notEquals(this.actualReceiverType, oldReceiverType) && TypeBinding.notEquals(this.receiver.postConversionType(scope), this.actualReceiverType)) { // record need for explicit cast at codegen since receiver could not handle it
this.bits |= NeedReceiverGenericCast;
}
}
}
if (checkInvocationArguments(scope, this.receiver, this.actualReceiverType, this.binding, this.arguments, argumentTypes, argsContainCast, this)) {
this.bits |= ASTNode.Unchecked;
}
//-------message send that are known to fail at compile time-----------
if (this.binding.isAbstract()) {
if (this.receiver.isSuper()) {
scope.problemReporter().cannotDireclyInvokeAbstractMethod(this, this.binding);
}
// abstract private methods cannot occur nor abstract static............
}
if (isMethodUseDeprecated(this.binding, scope, true))
scope.problemReporter().deprecatedMethod(this.binding, this);
// from 1.5 compliance on, array#clone() returns the array type (but binding still shows Object)
if (this.actualReceiverType.isArrayType()
&& this.binding.parameters == Binding.NO_PARAMETERS
&& scope.compilerOptions().complianceLevel >= ClassFileConstants.JDK1_5
&& CharOperation.equals(this.binding.selector, CLONE)) {
this.resolvedType = this.actualReceiverType;
} else {
TypeBinding returnType = this.binding.returnType;
if (returnType != null) {
if ((this.bits & ASTNode.Unchecked) != 0 && this.genericTypeArguments == null) {
returnType = scope.environment().convertToRawType(returnType.erasure(), true);
}
returnType = returnType.capture(scope, this.sourceEnd);
}
this.resolvedType = returnType;
}
return this.resolvedType;
}
}