001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.eval;
011:
012: import org.eclipse.jdt.core.compiler.CharOperation;
013: import org.eclipse.jdt.internal.compiler.ast.CastExpression;
014: import org.eclipse.jdt.internal.compiler.ast.Expression;
015: import org.eclipse.jdt.internal.compiler.ast.MessageSend;
016: import org.eclipse.jdt.internal.compiler.ast.NameReference;
017: import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
018: import org.eclipse.jdt.internal.compiler.codegen.CodeStream;
019: import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
020: import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
021: import org.eclipse.jdt.internal.compiler.impl.Constant;
022: import org.eclipse.jdt.internal.compiler.lookup.Binding;
023: import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
024: import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
025: import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
026: import org.eclipse.jdt.internal.compiler.lookup.ProblemMethodBinding;
027: import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
028: import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
029: import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
030: import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
031: import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
032: import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
033:
034: public class CodeSnippetMessageSend extends MessageSend implements
035: ProblemReasons, EvaluationConstants {
036: EvaluationContext evaluationContext;
037: FieldBinding delegateThis;
038:
039: /**
040: * CodeSnippetMessageSend constructor comment.
041: */
042: public CodeSnippetMessageSend(EvaluationContext evaluationContext) {
043: this .evaluationContext = evaluationContext;
044: }
045:
046: /**
047: * MessageSend code generation
048: *
049: * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
050: * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
051: * @param valueRequired boolean
052: */
053: public void generateCode(BlockScope currentScope,
054: CodeStream codeStream, boolean valueRequired) {
055:
056: int pc = codeStream.position;
057:
058: if (this .codegenBinding.canBeSeenBy(this .actualReceiverType,
059: this , currentScope)) {
060: // generate receiver/enclosing instance access
061: boolean isStatic = this .codegenBinding.isStatic();
062: // outer access ?
063: if (!isStatic && ((this .bits & DepthMASK) != 0)) {
064: // outer method can be reached through emulation
065: ReferenceBinding targetType = currentScope
066: .enclosingSourceType().enclosingTypeAt(
067: (this .bits & DepthMASK) >> DepthSHIFT);
068: Object[] path = currentScope
069: .getEmulationPath(targetType,
070: true /*only exact match*/, false/*consider enclosing arg*/);
071: if (path == null) {
072: // emulation was not possible (should not happen per construction)
073: currentScope.problemReporter().needImplementation();
074: } else {
075: codeStream.generateOuterAccess(path, this ,
076: targetType, currentScope);
077: }
078: } else {
079: this .receiver.generateCode(currentScope, codeStream,
080: !isStatic);
081: if (this .receiverGenericCast != null)
082: codeStream.checkcast(this .receiverGenericCast);
083: codeStream.recordPositionsFrom(pc, this .sourceStart);
084: }
085: // generate arguments
086: generateArguments(binding, arguments, currentScope,
087: codeStream);
088: // actual message invocation
089: if (isStatic) {
090: codeStream.invokestatic(this .codegenBinding);
091: } else {
092: if (this .receiver.isSuper()) {
093: codeStream.invokespecial(this .codegenBinding);
094: } else {
095: if (this .codegenBinding.declaringClass
096: .isInterface()) {
097: codeStream.invokeinterface(this .codegenBinding);
098: } else {
099: codeStream.invokevirtual(this .codegenBinding);
100: }
101: }
102: }
103: } else {
104: codeStream.generateEmulationForMethod(currentScope,
105: this .codegenBinding);
106: // generate receiver/enclosing instance access
107: boolean isStatic = this .codegenBinding.isStatic();
108: // outer access ?
109: if (!isStatic && ((this .bits & DepthMASK) != 0)) {
110: // not supported yet
111: currentScope.problemReporter().needImplementation();
112: } else {
113: this .receiver.generateCode(currentScope, codeStream,
114: !isStatic);
115: if (this .receiverGenericCast != null)
116: codeStream.checkcast(this .receiverGenericCast);
117: codeStream.recordPositionsFrom(pc, this .sourceStart);
118: }
119: if (isStatic) {
120: // we need an object on the stack which is ignored for the method invocation
121: codeStream.aconst_null();
122: }
123: // generate arguments
124: if (this .arguments != null) {
125: int argsLength = this .arguments.length;
126: codeStream.generateInlinedValue(argsLength);
127: codeStream.newArray(currentScope.createArrayType(
128: currentScope.getType(
129: TypeConstants.JAVA_LANG_OBJECT, 3), 1));
130: codeStream.dup();
131: for (int i = 0; i < argsLength; i++) {
132: codeStream.generateInlinedValue(i);
133: this .arguments[i].generateCode(currentScope,
134: codeStream, true);
135: TypeBinding parameterBinding = this .codegenBinding.parameters[i];
136: if (parameterBinding.isBaseType()
137: && parameterBinding != TypeBinding.NULL) {
138: codeStream
139: .generateBoxingConversion(this .codegenBinding.parameters[i].id);
140: }
141: codeStream.aastore();
142: if (i < argsLength - 1) {
143: codeStream.dup();
144: }
145: }
146: } else {
147: codeStream.generateInlinedValue(0);
148: codeStream.newArray(currentScope.createArrayType(
149: currentScope.getType(
150: TypeConstants.JAVA_LANG_OBJECT, 3), 1));
151: }
152: codeStream.invokeJavaLangReflectMethodInvoke();
153:
154: // convert the return value to the appropriate type for primitive types
155: if (this .codegenBinding.returnType.isBaseType()) {
156: int typeID = this .codegenBinding.returnType.id;
157: if (typeID == T_void) {
158: // remove the null from the stack
159: codeStream.pop();
160: }
161: codeStream.checkcast(typeID);
162: codeStream.getBaseTypeValue(typeID);
163: } else {
164: codeStream.checkcast(this .codegenBinding.returnType);
165: }
166: }
167: // required cast must occur even if no value is required
168: if (this .valueCast != null)
169: codeStream.checkcast(this .valueCast);
170: if (valueRequired) {
171: // implicit conversion if necessary
172: codeStream.generateImplicitConversion(implicitConversion);
173: } else {
174: boolean isUnboxing = (implicitConversion & TypeIds.UNBOXING) != 0;
175: // conversion only generated if unboxing
176: if (isUnboxing)
177: codeStream
178: .generateImplicitConversion(implicitConversion);
179: switch (isUnboxing ? postConversionType(currentScope).id
180: : this .codegenBinding.returnType.id) {
181: case T_long:
182: case T_double:
183: codeStream.pop2();
184: break;
185: case T_void:
186: break;
187: default:
188: codeStream.pop();
189: }
190: }
191: codeStream.recordPositionsFrom(pc,
192: (int) (this .nameSourcePosition >>> 32)); // highlight selector
193: }
194:
195: public void manageSyntheticAccessIfNecessary(
196: BlockScope currentScope, FlowInfo flowInfo) {
197:
198: if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) {
199:
200: // if method from parameterized type got found, use the original method at codegen time
201: this .codegenBinding = this .binding.original();
202: if (this .codegenBinding != this .binding) {
203: // extra cast needed if method return type was type variable
204: if (this .codegenBinding.returnType.isTypeVariable()) {
205: TypeVariableBinding variableReturnType = (TypeVariableBinding) this .codegenBinding.returnType;
206: if (variableReturnType.firstBound != this .binding.returnType) { // no need for extra cast if same as first bound anyway
207: this .valueCast = this .binding.returnType;
208: }
209: }
210: }
211:
212: // if the binding declaring class is not visible, need special action
213: // for runtime compatibility on 1.2 VMs : change the declaring class of the binding
214: // NOTE: from target 1.2 on, method's declaring class is touched if any different from receiver type
215: // and not from Object or implicit static method call.
216: if (this .binding.declaringClass != this .actualReceiverType
217: && !this .actualReceiverType.isArrayType()) {
218: CompilerOptions options = currentScope
219: .compilerOptions();
220: if ((options.targetJDK >= ClassFileConstants.JDK1_2
221: && (options.complianceLevel >= ClassFileConstants.JDK1_4
222: || !receiver.isImplicitThis() || !this .codegenBinding
223: .isStatic()) && this .binding.declaringClass.id != T_JavaLangObject) // no change for Object methods
224: || !this .binding.declaringClass
225: .canBeSeenBy(currentScope)) {
226:
227: this .codegenBinding = currentScope
228: .enclosingSourceType()
229: .getUpdatedMethodBinding(
230: this .codegenBinding,
231: (ReferenceBinding) this .actualReceiverType
232: .erasure());
233: }
234: // Post 1.4.0 target, array clone() invocations are qualified with array type
235: // This is handled in array type #clone method binding resolution (see Scope and UpdatedMethodBinding)
236: }
237: }
238: }
239:
240: public TypeBinding resolveType(BlockScope scope) {
241: // Answer the signature return type
242: // Base type promotion
243:
244: this .constant = Constant.NotAConstant;
245: boolean receiverCast = false, argsContainCast = false;
246: if (this .receiver instanceof CastExpression) {
247: this .receiver.bits |= DisableUnnecessaryCastCheck; // will check later on
248: receiverCast = true;
249: }
250: this .actualReceiverType = receiver.resolveType(scope);
251: if (receiverCast && this .actualReceiverType != null) {
252: // due to change of declaring class with receiver type, only identity cast should be notified
253: if (((CastExpression) this .receiver).expression.resolvedType == this .actualReceiverType) {
254: scope.problemReporter().unnecessaryCast(
255: (CastExpression) this .receiver);
256: }
257: }
258: // resolve type arguments (for generic constructor call)
259: if (this .typeArguments != null) {
260: int length = this .typeArguments.length;
261: boolean argHasError = false; // typeChecks all arguments
262: this .genericTypeArguments = new TypeBinding[length];
263: for (int i = 0; i < length; i++) {
264: if ((this .genericTypeArguments[i] = this .typeArguments[i]
265: .resolveType(scope, true /* check bounds*/)) == null) {
266: argHasError = true;
267: }
268: }
269: if (argHasError) {
270: return null;
271: }
272: }
273: // will check for null after args are resolved
274: TypeBinding[] argumentTypes = Binding.NO_PARAMETERS;
275: if (this .arguments != null) {
276: boolean argHasError = false; // typeChecks all arguments
277: int length = this .arguments.length;
278: argumentTypes = new TypeBinding[length];
279: for (int i = 0; i < length; i++) {
280: Expression argument = arguments[i];
281: if (argument instanceof CastExpression) {
282: argument.bits |= DisableUnnecessaryCastCheck; // will check later on
283: argsContainCast = true;
284: }
285: if ((argumentTypes[i] = this .arguments[i]
286: .resolveType(scope)) == null)
287: argHasError = true;
288: }
289: if (argHasError) {
290: if (actualReceiverType instanceof ReferenceBinding) {
291: // record any selector match, for clients who may still need hint about possible method match
292: this .binding = scope.findMethod(
293: (ReferenceBinding) actualReceiverType,
294: selector, new TypeBinding[] {}, this );
295: }
296: return null;
297: }
298: }
299: if (this .actualReceiverType == null) {
300: return null;
301: }
302: // base type cannot receive any message
303: if (this .actualReceiverType.isBaseType()) {
304: scope.problemReporter().errorNoMethodFor(this ,
305: this .actualReceiverType, argumentTypes);
306: return null;
307: }
308:
309: this .binding = this .receiver.isImplicitThis() ? scope
310: .getImplicitMethod(this .selector, argumentTypes, this )
311: : scope.getMethod(this .actualReceiverType,
312: this .selector, argumentTypes, this );
313: if (!this .binding.isValidBinding()) {
314: if (this .binding instanceof ProblemMethodBinding
315: && ((ProblemMethodBinding) this .binding)
316: .problemId() == NotVisible) {
317: if (this .evaluationContext.declaringTypeName != null) {
318: this .delegateThis = scope
319: .getField(scope.enclosingSourceType(),
320: DELEGATE_THIS, this );
321: if (this .delegateThis == null) { // if not found then internal error, field should have been found
322: this .constant = Constant.NotAConstant;
323: scope.problemReporter().invalidMethod(this ,
324: this .binding);
325: return null;
326: }
327: } else {
328: this .constant = Constant.NotAConstant;
329: scope.problemReporter().invalidMethod(this ,
330: this .binding);
331: return null;
332: }
333: CodeSnippetScope localScope = new CodeSnippetScope(
334: scope);
335: MethodBinding privateBinding = this .receiver instanceof CodeSnippetThisReference
336: && ((CodeSnippetThisReference) this .receiver).isImplicit ? localScope
337: .getImplicitMethod(
338: (ReferenceBinding) this .delegateThis.type,
339: this .selector, argumentTypes, this )
340: : localScope.getMethod(this .delegateThis.type,
341: this .selector, argumentTypes, this );
342: if (!privateBinding.isValidBinding()) {
343: if (this .binding.declaringClass == null) {
344: if (this .actualReceiverType instanceof ReferenceBinding) {
345: this .binding.declaringClass = (ReferenceBinding) this .actualReceiverType;
346: } else { // really bad error ....
347: scope.problemReporter().errorNoMethodFor(
348: this , this .actualReceiverType,
349: argumentTypes);
350: return null;
351: }
352: }
353: scope.problemReporter().invalidMethod(this ,
354: this .binding);
355: return null;
356: } else {
357: this .binding = privateBinding;
358: }
359: } else {
360: if (this .binding.declaringClass == null) {
361: if (this .actualReceiverType instanceof ReferenceBinding) {
362: this .binding.declaringClass = (ReferenceBinding) this .actualReceiverType;
363: } else { // really bad error ....
364: scope.problemReporter().errorNoMethodFor(this ,
365: this .actualReceiverType, argumentTypes);
366: return null;
367: }
368: }
369: scope.problemReporter().invalidMethod(this ,
370: this .binding);
371: return null;
372: }
373: }
374: if (!this .binding.isStatic()) {
375: // the "receiver" must not be a type, in other words, a NameReference that the TC has bound to a Type
376: if (receiver instanceof NameReference
377: && (((NameReference) receiver).bits & Binding.TYPE) != 0) {
378: scope.problemReporter().mustUseAStaticMethod(this ,
379: binding);
380: } else {
381: // compute generic cast if necessary
382: TypeBinding receiverErasure = this .actualReceiverType
383: .erasure();
384: if (receiverErasure instanceof ReferenceBinding) {
385: if (receiverErasure
386: .findSuperTypeWithSameErasure(this .binding.declaringClass) == null) {
387: this .actualReceiverType = this .binding.declaringClass; // handle indirect inheritance thru variable secondary bound
388: }
389: }
390: receiver.computeConversion(scope,
391: this .actualReceiverType,
392: this .actualReceiverType);
393: }
394: }
395: checkInvocationArguments(scope, this .receiver,
396: actualReceiverType, binding, this .arguments,
397: argumentTypes, argsContainCast, this );
398:
399: //-------message send that are known to fail at compile time-----------
400: if (binding.isAbstract()) {
401: if (receiver.isSuper()) {
402: scope.problemReporter()
403: .cannotDireclyInvokeAbstractMethod(this ,
404: binding);
405: }
406: // abstract private methods cannot occur nor abstract static............
407: }
408: if (isMethodUseDeprecated(binding, scope, true))
409: scope.problemReporter().deprecatedMethod(binding, this );
410:
411: // from 1.5 compliance on, array#clone() returns the array type (but binding still shows Object)
412: if (actualReceiverType.isArrayType()
413: && this .binding.parameters == Binding.NO_PARAMETERS
414: && scope.compilerOptions().complianceLevel >= ClassFileConstants.JDK1_5
415: && CharOperation.equals(this .binding.selector, CLONE)) {
416: this .resolvedType = actualReceiverType;
417: } else {
418: TypeBinding returnType = this.binding.returnType;
419: if (returnType != null)
420: returnType = returnType.capture(scope, this.sourceEnd);
421: this.resolvedType = returnType;
422: }
423: return this.resolvedType;
424: }
425: }
|