001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 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.internal.compiler.ast.Assignment;
013: import org.eclipse.jdt.internal.compiler.ast.CompoundAssignment;
014: import org.eclipse.jdt.internal.compiler.ast.Expression;
015: import org.eclipse.jdt.internal.compiler.ast.FieldReference;
016: import org.eclipse.jdt.internal.compiler.ast.IntLiteral;
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.BlockScope;
023: import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
024: import org.eclipse.jdt.internal.compiler.lookup.ParameterizedFieldBinding;
025: import org.eclipse.jdt.internal.compiler.lookup.ProblemFieldBinding;
026: import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
027: import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
028: import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
029: import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
030:
031: public class CodeSnippetFieldReference extends FieldReference implements
032: ProblemReasons, EvaluationConstants {
033:
034: EvaluationContext evaluationContext;
035: FieldBinding delegateThis;
036:
037: /**
038: * CodeSnippetFieldReference constructor comment.
039: * @param source char[]
040: * @param pos long
041: */
042: public CodeSnippetFieldReference(char[] source, long pos,
043: EvaluationContext evaluationContext) {
044: super (source, pos);
045: this .evaluationContext = evaluationContext;
046: }
047:
048: public void generateAssignment(BlockScope currentScope,
049: CodeStream codeStream, Assignment assignment,
050: boolean valueRequired) {
051:
052: if (this .codegenBinding.canBeSeenBy(this .receiverType, this ,
053: currentScope)) {
054: this .receiver.generateCode(currentScope, codeStream,
055: !this .codegenBinding.isStatic());
056: assignment.expression.generateCode(currentScope,
057: codeStream, true);
058: fieldStore(codeStream, this .codegenBinding, null,
059: valueRequired);
060: } else {
061: codeStream.generateEmulationForField(this .codegenBinding);
062: this .receiver.generateCode(currentScope, codeStream,
063: !this .codegenBinding.isStatic());
064: if (this .codegenBinding.isStatic()) { // need a receiver?
065: codeStream.aconst_null();
066: }
067: assignment.expression.generateCode(currentScope,
068: codeStream, true);
069: if (valueRequired) {
070: if ((this .codegenBinding.type == TypeBinding.LONG)
071: || (this .codegenBinding.type == TypeBinding.DOUBLE)) {
072: codeStream.dup2_x2();
073: } else {
074: codeStream.dup_x2();
075: }
076: }
077: codeStream
078: .generateEmulatedWriteAccessForField(this .codegenBinding);
079: }
080: if (valueRequired) {
081: codeStream
082: .generateImplicitConversion(assignment.implicitConversion);
083: }
084: }
085:
086: /**
087: * Field reference code generation
088: *
089: * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
090: * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
091: * @param valueRequired boolean
092: */
093: public void generateCode(BlockScope currentScope,
094: CodeStream codeStream, boolean valueRequired) {
095:
096: int pc = codeStream.position;
097: if (this .constant != Constant.NotAConstant) {
098: if (valueRequired) {
099: codeStream.generateConstant(this .constant,
100: this .implicitConversion);
101: }
102: } else {
103: boolean isStatic = this .codegenBinding.isStatic();
104: this .receiver.generateCode(currentScope, codeStream,
105: !isStatic);
106: if (valueRequired) {
107: Constant fieldConstant = this .codegenBinding.constant();
108: if (fieldConstant == Constant.NotAConstant) {
109: if (this .codegenBinding.declaringClass == null) { // array length
110: codeStream.arraylength();
111: } else {
112: if (this .codegenBinding.canBeSeenBy(
113: this .receiverType, this , currentScope)) {
114: if (isStatic) {
115: codeStream
116: .getstatic(this .codegenBinding);
117: } else {
118: codeStream
119: .getfield(this .codegenBinding);
120: }
121: } else {
122: if (isStatic) {
123: // we need a null on the stack to use the reflect emulation
124: codeStream.aconst_null();
125: }
126: codeStream
127: .generateEmulatedReadAccessForField(this .codegenBinding);
128: }
129: }
130: codeStream
131: .generateImplicitConversion(this .implicitConversion);
132: } else {
133: if (!isStatic) {
134: codeStream.invokeObjectGetClass(); // perform null check
135: codeStream.pop();
136: }
137: codeStream.generateConstant(fieldConstant,
138: this .implicitConversion);
139: }
140: } else {
141: if (!isStatic) {
142: codeStream.invokeObjectGetClass(); // perform null check
143: codeStream.pop();
144: }
145: }
146: }
147: codeStream.recordPositionsFrom(pc, this .sourceStart);
148: }
149:
150: public void generateCompoundAssignment(BlockScope currentScope,
151: CodeStream codeStream, Expression expression, int operator,
152: int assignmentImplicitConversion, boolean valueRequired) {
153:
154: boolean isStatic;
155: if (this .codegenBinding.canBeSeenBy(this .receiverType, this ,
156: currentScope)) {
157: this .receiver.generateCode(currentScope, codeStream,
158: !(isStatic = this .codegenBinding.isStatic()));
159: if (isStatic) {
160: codeStream.getstatic(this .codegenBinding);
161: } else {
162: codeStream.dup();
163: codeStream.getfield(this .codegenBinding);
164: }
165: int operationTypeID;
166: switch (operationTypeID = (this .implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) {
167: case T_JavaLangString:
168: case T_JavaLangObject:
169: case T_undefined:
170: codeStream.generateStringConcatenationAppend(
171: currentScope, null, expression);
172: break;
173: default:
174: // promote the array reference to the suitable operation type
175: codeStream
176: .generateImplicitConversion(this .implicitConversion);
177: // generate the increment value (will by itself be promoted to the operation value)
178: if (expression == IntLiteral.One) { // prefix operation
179: codeStream.generateConstant(expression.constant,
180: this .implicitConversion);
181: } else {
182: expression.generateCode(currentScope, codeStream,
183: true);
184: }
185: // perform the operation
186: codeStream.sendOperator(operator, operationTypeID);
187: // cast the value back to the array reference type
188: codeStream
189: .generateImplicitConversion(assignmentImplicitConversion);
190: }
191: fieldStore(codeStream, this .codegenBinding, null,
192: valueRequired);
193: } else {
194: this .receiver.generateCode(currentScope, codeStream,
195: !(isStatic = this .codegenBinding.isStatic()));
196: if (isStatic) {
197: // used to store the value
198: codeStream
199: .generateEmulationForField(this .codegenBinding);
200: codeStream.aconst_null();
201:
202: // used to retrieve the actual value
203: codeStream.aconst_null();
204: codeStream
205: .generateEmulatedReadAccessForField(this .codegenBinding);
206: } else {
207: // used to store the value
208: codeStream.generateEmulationForField(this .binding);
209: this .receiver.generateCode(currentScope, codeStream,
210: !isStatic);
211:
212: // used to retrieve the actual value
213: codeStream.dup();
214: codeStream
215: .generateEmulatedReadAccessForField(this .codegenBinding);
216: }
217: int operationTypeID;
218: if ((operationTypeID = (this .implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) == T_JavaLangString) {
219: codeStream.generateStringConcatenationAppend(
220: currentScope, null, expression);
221: } else {
222: // promote the array reference to the suitable operation type
223: codeStream
224: .generateImplicitConversion(this .implicitConversion);
225: // generate the increment value (will by itself be promoted to the operation value)
226: if (expression == IntLiteral.One) { // prefix operation
227: codeStream.generateConstant(expression.constant,
228: this .implicitConversion);
229: } else {
230: expression.generateCode(currentScope, codeStream,
231: true);
232: }
233: // perform the operation
234: codeStream.sendOperator(operator, operationTypeID);
235: // cast the value back to the array reference type
236: codeStream
237: .generateImplicitConversion(assignmentImplicitConversion);
238: }
239: // current stack is:
240: // field receiver value
241: if (valueRequired) {
242: if ((this .codegenBinding.type == TypeBinding.LONG)
243: || (this .codegenBinding.type == TypeBinding.DOUBLE)) {
244: codeStream.dup2_x2();
245: } else {
246: codeStream.dup_x2();
247: }
248: }
249: // current stack is:
250: // value field receiver value
251: codeStream
252: .generateEmulatedWriteAccessForField(this .codegenBinding);
253: }
254: }
255:
256: public void generatePostIncrement(BlockScope currentScope,
257: CodeStream codeStream, CompoundAssignment postIncrement,
258: boolean valueRequired) {
259: boolean isStatic;
260: if (this .codegenBinding.canBeSeenBy(this .receiverType, this ,
261: currentScope)) {
262: this .receiver.generateCode(currentScope, codeStream,
263: !(isStatic = this .codegenBinding.isStatic()));
264: if (isStatic) {
265: codeStream.getstatic(this .codegenBinding);
266: } else {
267: codeStream.dup();
268: codeStream.getfield(this .codegenBinding);
269: }
270: if (valueRequired) {
271: if (isStatic) {
272: if ((this .codegenBinding.type == TypeBinding.LONG)
273: || (this .codegenBinding.type == TypeBinding.DOUBLE)) {
274: codeStream.dup2();
275: } else {
276: codeStream.dup();
277: }
278: } else { // Stack: [owner][old field value] ---> [old field value][owner][old field value]
279: if ((this .codegenBinding.type == TypeBinding.LONG)
280: || (this .codegenBinding.type == TypeBinding.DOUBLE)) {
281: codeStream.dup2_x1();
282: } else {
283: codeStream.dup_x1();
284: }
285: }
286: }
287: codeStream.generateConstant(
288: postIncrement.expression.constant,
289: this .implicitConversion);
290: codeStream.sendOperator(postIncrement.operator,
291: this .codegenBinding.type.id);
292: codeStream
293: .generateImplicitConversion(postIncrement.preAssignImplicitConversion);
294: fieldStore(codeStream, this .codegenBinding, null, false);
295: } else {
296: this .receiver.generateCode(currentScope, codeStream,
297: !(isStatic = this .codegenBinding.isStatic()));
298: if (isStatic) {
299: codeStream.aconst_null();
300: }
301: // the actual stack is: receiver
302: codeStream.dup();
303: // the actual stack is: receiver receiver
304: codeStream
305: .generateEmulatedReadAccessForField(this .codegenBinding);
306: // the actual stack is: receiver value
307: // receiver value
308: // value receiver value dup_x1 or dup2_x1 if value required
309: // value value receiver value dup_x1 or dup2_x1
310: // value value receiver pop or pop2
311: // value value receiver field generateEmulationForField
312: // value value field receiver swap
313: // value field receiver value field receiver dup2_x1 or dup2_x2
314: // value field receiver value pop2
315: // value field receiver newvalue generate constant + op
316: // value store
317: if (valueRequired) {
318: if ((this .codegenBinding.type == TypeBinding.LONG)
319: || (this .codegenBinding.type == TypeBinding.DOUBLE)) {
320: codeStream.dup2_x1();
321: } else {
322: codeStream.dup_x1();
323: }
324: }
325: if ((this .codegenBinding.type == TypeBinding.LONG)
326: || (this .codegenBinding.type == TypeBinding.DOUBLE)) {
327: codeStream.dup2_x1();
328: codeStream.pop2();
329: } else {
330: codeStream.dup_x1();
331: codeStream.pop();
332: }
333: codeStream.generateEmulationForField(this .codegenBinding);
334: codeStream.swap();
335:
336: if ((this .codegenBinding.type == TypeBinding.LONG)
337: || (this .codegenBinding.type == TypeBinding.DOUBLE)) {
338: codeStream.dup2_x2();
339: } else {
340: codeStream.dup2_x1();
341: }
342: codeStream.pop2();
343:
344: codeStream.generateConstant(
345: postIncrement.expression.constant,
346: this .implicitConversion);
347: codeStream.sendOperator(postIncrement.operator,
348: this .codegenBinding.type.id);
349: codeStream
350: .generateImplicitConversion(postIncrement.preAssignImplicitConversion);
351: codeStream
352: .generateEmulatedWriteAccessForField(this .codegenBinding);
353: }
354: }
355:
356: /*
357: * No need to emulate access to protected fields since not implicitly accessed
358: */
359: public void manageSyntheticAccessIfNecessary(
360: BlockScope currentScope, FlowInfo flowInfo,
361: boolean isReadAccess) {
362: // The private access will be managed through the code generation
363:
364: if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0)
365: return;
366:
367: // if field from parameterized type got found, use the original field at codegen time
368: if (this .binding instanceof ParameterizedFieldBinding) {
369: ParameterizedFieldBinding parameterizedField = (ParameterizedFieldBinding) this .binding;
370: this .codegenBinding = parameterizedField.originalField;
371: // extra cast needed if field type was type variable
372: if (this .codegenBinding.type.isTypeVariable()) {
373: TypeVariableBinding variableReturnType = (TypeVariableBinding) this .codegenBinding.type;
374: if (variableReturnType.firstBound != parameterizedField.type) { // no need for extra cast if same as first bound anyway
375: this .genericCast = parameterizedField.type
376: .erasure();
377: }
378: }
379: } else {
380: this .codegenBinding = this .binding;
381: }
382:
383: // if the binding declaring class is not visible, need special action
384: // for runtime compatibility on 1.2 VMs : change the declaring class of the binding
385: // NOTE: from target 1.2 on, field's declaring class is touched if any different from receiver type
386: TypeBinding someReceiverType = this .delegateThis != null ? this .delegateThis.type
387: : this .receiverType;
388: if (this .binding.declaringClass != someReceiverType
389: && !someReceiverType.isArrayType()
390: && this .binding.declaringClass != null // array.length
391: && this .binding.constant() == Constant.NotAConstant) {
392:
393: CompilerOptions options = currentScope.compilerOptions();
394: if ((options.targetJDK >= ClassFileConstants.JDK1_2
395: && (options.complianceLevel >= ClassFileConstants.JDK1_4
396: || !receiver.isImplicitThis() || !this .codegenBinding
397: .isStatic()) && this .binding.declaringClass.id != T_JavaLangObject) // no change for Object fields
398: || !this .binding.declaringClass
399: .canBeSeenBy(currentScope)) {
400:
401: this .codegenBinding = currentScope
402: .enclosingSourceType().getUpdatedFieldBinding(
403: this .codegenBinding,
404: (ReferenceBinding) someReceiverType
405: .erasure());
406: }
407: }
408: }
409:
410: public TypeBinding resolveType(BlockScope scope) {
411: // Answer the signature type of the field.
412: // constants are propaged when the field is final
413: // and initialized with a (compile time) constant
414:
415: // regular receiver reference
416: this .receiverType = this .receiver.resolveType(scope);
417: if (this .receiverType == null) {
418: this .constant = Constant.NotAConstant;
419: return null;
420: }
421: // the case receiverType.isArrayType and token = 'length' is handled by the scope API
422: this .codegenBinding = this .binding = scope.getField(
423: this .receiverType, this .token, this );
424: FieldBinding firstAttempt = this .binding;
425: boolean isNotVisible = false;
426: if (!this .binding.isValidBinding()) {
427: if (this .binding instanceof ProblemFieldBinding
428: && ((ProblemFieldBinding) this .binding).problemId() == NotVisible) {
429: isNotVisible = true;
430: if (this .evaluationContext.declaringTypeName != null) {
431: this .delegateThis = scope
432: .getField(scope.enclosingSourceType(),
433: DELEGATE_THIS, this );
434: if (this .delegateThis == null) { // if not found then internal error, field should have been found
435: this .constant = Constant.NotAConstant;
436: scope.problemReporter().invalidField(this ,
437: this .receiverType);
438: return null;
439: }
440: } else {
441: this .constant = Constant.NotAConstant;
442: scope.problemReporter().invalidField(this ,
443: this .receiverType);
444: return null;
445: }
446: CodeSnippetScope localScope = new CodeSnippetScope(
447: scope);
448: this .codegenBinding = this .binding = localScope
449: .getFieldForCodeSnippet(this .delegateThis.type,
450: this .token, this );
451: }
452: }
453:
454: if (!this .binding.isValidBinding()) {
455: this .constant = Constant.NotAConstant;
456: if (isNotVisible) {
457: this .codegenBinding = this .binding = firstAttempt;
458: }
459: scope.problemReporter().invalidField(this ,
460: this .receiverType);
461: return null;
462: }
463:
464: if (isFieldUseDeprecated(this .binding, scope,
465: (this .bits & IsStrictlyAssigned) != 0)) {
466: scope.problemReporter().deprecatedField(this .binding, this );
467: }
468: // check for this.x in static is done in the resolution of the receiver
469: this .constant = this .receiver.isImplicitThis() ? this .binding
470: .constant() : Constant.NotAConstant;
471: if (!this .receiver.isThis()) { // TODO need to check if shouldn't be isImplicitThis check (and then removed)
472: this.constant = Constant.NotAConstant;
473: }
474: return this.resolvedType = this.binding.type;
475: }
476: }
|