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.compiler.ast;
011:
012: import org.eclipse.jdt.internal.compiler.ASTVisitor;
013: import org.eclipse.jdt.internal.compiler.impl.*;
014: import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
015: import org.eclipse.jdt.internal.compiler.codegen.*;
016: import org.eclipse.jdt.internal.compiler.flow.*;
017: import org.eclipse.jdt.internal.compiler.lookup.*;
018:
019: public class FieldReference extends Reference implements InvocationSite {
020:
021: public static final int READ = 0;
022: public static final int WRITE = 1;
023: public Expression receiver;
024: public char[] token;
025: public FieldBinding binding; // exact binding resulting from lookup
026: protected FieldBinding codegenBinding; // actual binding used for code generation (if no synthetic accessor)
027: public MethodBinding[] syntheticAccessors; // [0]=read accessor [1]=write accessor
028:
029: public long nameSourcePosition; //(start<<32)+end
030: public TypeBinding receiverType;
031: public TypeBinding genericCast;
032:
033: public FieldReference(char[] source, long pos) {
034: token = source;
035: nameSourcePosition = pos;
036: //by default the position are the one of the field (not true for super access)
037: sourceStart = (int) (pos >>> 32);
038: sourceEnd = (int) (pos & 0x00000000FFFFFFFFL);
039: bits |= Binding.FIELD;
040:
041: }
042:
043: public FlowInfo analyseAssignment(BlockScope currentScope,
044: FlowContext flowContext, FlowInfo flowInfo,
045: Assignment assignment, boolean isCompound) {
046: // compound assignment extra work
047: if (isCompound) { // check the variable part is initialized if blank final
048: if (binding.isBlankFinal()
049: && receiver.isThis()
050: && currentScope
051: .needBlankFinalFieldInitializationCheck(binding)
052: && (!flowInfo.isDefinitelyAssigned(binding))) {
053: currentScope.problemReporter()
054: .uninitializedBlankFinalField(binding, this );
055: // we could improve error msg here telling "cannot use compound assignment on final blank field"
056: }
057: manageSyntheticAccessIfNecessary(currentScope, flowInfo,
058: true /*read-access*/);
059: }
060: flowInfo = receiver.analyseCode(currentScope, flowContext,
061: flowInfo, !binding.isStatic()).unconditionalInits();
062: if (assignment.expression != null) {
063: flowInfo = assignment.expression.analyseCode(currentScope,
064: flowContext, flowInfo).unconditionalInits();
065: }
066: manageSyntheticAccessIfNecessary(currentScope, flowInfo, false /*write-access*/);
067:
068: // check if assigning a final field
069: if (binding.isFinal()) {
070: // in a context where it can be assigned?
071: if (binding.isBlankFinal()
072: && !isCompound
073: && receiver.isThis()
074: && !(receiver instanceof QualifiedThisReference)
075: && ((receiver.bits & ParenthesizedMASK) == 0) // (this).x is forbidden
076: && currentScope
077: .allowBlankFinalFieldAssignment(binding)) {
078: if (flowInfo.isPotentiallyAssigned(binding)) {
079: currentScope.problemReporter()
080: .duplicateInitializationOfBlankFinalField(
081: binding, this );
082: } else {
083: flowContext.recordSettingFinal(binding, this ,
084: flowInfo);
085: }
086: flowInfo.markAsDefinitelyAssigned(binding);
087: } else {
088: // assigning a final field outside an initializer or constructor or wrong reference
089: currentScope.problemReporter()
090: .cannotAssignToFinalField(binding, this );
091: }
092: }
093: return flowInfo;
094: }
095:
096: public FlowInfo analyseCode(BlockScope currentScope,
097: FlowContext flowContext, FlowInfo flowInfo) {
098: return analyseCode(currentScope, flowContext, flowInfo, true);
099: }
100:
101: public FlowInfo analyseCode(BlockScope currentScope,
102: FlowContext flowContext, FlowInfo flowInfo,
103: boolean valueRequired) {
104: boolean nonStatic = !binding.isStatic();
105: receiver.analyseCode(currentScope, flowContext, flowInfo,
106: nonStatic);
107: if (nonStatic) {
108: receiver.checkNPE(currentScope, flowContext, flowInfo);
109: }
110:
111: if (valueRequired
112: || currentScope.compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4) {
113: manageSyntheticAccessIfNecessary(currentScope, flowInfo,
114: true /*read-access*/);
115: }
116: return flowInfo;
117: }
118:
119: /**
120: * @see org.eclipse.jdt.internal.compiler.ast.Expression#computeConversion(org.eclipse.jdt.internal.compiler.lookup.Scope, org.eclipse.jdt.internal.compiler.lookup.TypeBinding, org.eclipse.jdt.internal.compiler.lookup.TypeBinding)
121: */
122: public void computeConversion(Scope scope,
123: TypeBinding runtimeTimeType, TypeBinding compileTimeType) {
124: if (runtimeTimeType == null || compileTimeType == null)
125: return;
126: // set the generic cast after the fact, once the type expectation is fully known (no need for strict cast)
127: if (this .binding != null && this .binding.isValidBinding()) {
128: FieldBinding originalBinding = this .binding.original();
129: TypeBinding originalType = originalBinding.type;
130: // extra cast needed if method return type is type variable
131: if (originalBinding != this .binding
132: && originalType != this .binding.type
133: && runtimeTimeType.id != T_JavaLangObject
134: && (originalType.tagBits & TagBits.HasTypeVariable) != 0) {
135: TypeBinding targetType = (!compileTimeType.isBaseType() && runtimeTimeType
136: .isBaseType()) ? compileTimeType // unboxing: checkcast before conversion
137: : runtimeTimeType;
138: this .genericCast = originalBinding.type
139: .genericCast(targetType);
140: }
141: }
142: super
143: .computeConversion(scope, runtimeTimeType,
144: compileTimeType);
145: }
146:
147: public FieldBinding fieldBinding() {
148: return binding;
149: }
150:
151: public void generateAssignment(BlockScope currentScope,
152: CodeStream codeStream, Assignment assignment,
153: boolean valueRequired) {
154: int pc = codeStream.position;
155: receiver.generateCode(currentScope, codeStream,
156: !this .codegenBinding.isStatic());
157: codeStream.recordPositionsFrom(pc, this .sourceStart);
158: assignment.expression.generateCode(currentScope, codeStream,
159: true);
160: fieldStore(codeStream, this .codegenBinding,
161: syntheticAccessors == null ? null
162: : syntheticAccessors[WRITE], valueRequired);
163: if (valueRequired) {
164: codeStream
165: .generateImplicitConversion(assignment.implicitConversion);
166: }
167: // no need for generic cast as value got dupped
168: }
169:
170: /**
171: * Field reference code generation
172: *
173: * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
174: * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
175: * @param valueRequired boolean
176: */
177: public void generateCode(BlockScope currentScope,
178: CodeStream codeStream, boolean valueRequired) {
179: int pc = codeStream.position;
180: if (constant != Constant.NotAConstant) {
181: if (valueRequired) {
182: codeStream.generateConstant(constant,
183: implicitConversion);
184: }
185: codeStream.recordPositionsFrom(pc, this .sourceStart);
186: return;
187: }
188: boolean isStatic = this .codegenBinding.isStatic();
189: boolean isThisReceiver = this .receiver instanceof ThisReference;
190: Constant fieldConstant = this .codegenBinding.constant();
191: if (fieldConstant != Constant.NotAConstant) {
192: if (!isThisReceiver) {
193: receiver.generateCode(currentScope, codeStream,
194: !isStatic);
195: if (!isStatic) {
196: codeStream.invokeObjectGetClass();
197: codeStream.pop();
198: }
199: }
200: if (valueRequired) {
201: codeStream.generateConstant(fieldConstant,
202: implicitConversion);
203: }
204: codeStream.recordPositionsFrom(pc, this .sourceStart);
205: return;
206: }
207: if (valueRequired
208: || (!isThisReceiver && currentScope.compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4)
209: || ((implicitConversion & TypeIds.UNBOXING) != 0)
210: || (this .genericCast != null)) {
211: receiver.generateCode(currentScope, codeStream, !isStatic);
212: pc = codeStream.position;
213: if (this .codegenBinding.declaringClass == null) { // array length
214: codeStream.arraylength();
215: if (valueRequired) {
216: codeStream
217: .generateImplicitConversion(implicitConversion);
218: } else {
219: // could occur if !valueRequired but compliance >= 1.4
220: codeStream.pop();
221: }
222: } else {
223: if (syntheticAccessors == null
224: || syntheticAccessors[READ] == null) {
225: if (isStatic) {
226: codeStream.getstatic(this .codegenBinding);
227: } else {
228: codeStream.getfield(this .codegenBinding);
229: }
230: } else {
231: codeStream.invokestatic(syntheticAccessors[READ]);
232: }
233: // required cast must occur even if no value is required
234: if (this .genericCast != null)
235: codeStream.checkcast(this .genericCast);
236: if (valueRequired) {
237: codeStream
238: .generateImplicitConversion(implicitConversion);
239: } else {
240: boolean isUnboxing = (implicitConversion & TypeIds.UNBOXING) != 0;
241: // conversion only generated if unboxing
242: if (isUnboxing)
243: codeStream
244: .generateImplicitConversion(implicitConversion);
245: switch (isUnboxing ? postConversionType(currentScope).id
246: : this .codegenBinding.type.id) {
247: case T_long:
248: case T_double:
249: codeStream.pop2();
250: break;
251: default:
252: codeStream.pop();
253: }
254: }
255: }
256: } else {
257: if (isThisReceiver) {
258: if (isStatic) {
259: // if no valueRequired, still need possible side-effects of <clinit> invocation, if field belongs to different class
260: if (this .binding.original().declaringClass != this .receiverType
261: .erasure()) {
262: MethodBinding accessor = syntheticAccessors == null ? null
263: : syntheticAccessors[READ];
264: if (accessor == null) {
265: codeStream.getstatic(this .codegenBinding);
266: } else {
267: codeStream.invokestatic(accessor);
268: }
269: switch (this .codegenBinding.type.id) {
270: case T_long:
271: case T_double:
272: codeStream.pop2();
273: break;
274: default:
275: codeStream.pop();
276: }
277: }
278: }
279: } else {
280: receiver.generateCode(currentScope, codeStream,
281: !isStatic);
282: if (!isStatic) {
283: codeStream.invokeObjectGetClass(); // perform null check
284: codeStream.pop();
285: }
286: }
287: }
288: codeStream.recordPositionsFrom(pc, this .sourceEnd);
289: }
290:
291: public void generateCompoundAssignment(BlockScope currentScope,
292: CodeStream codeStream, Expression expression, int operator,
293: int assignmentImplicitConversion, boolean valueRequired) {
294: boolean isStatic;
295: receiver.generateCode(currentScope, codeStream,
296: !(isStatic = this .codegenBinding.isStatic()));
297: if (isStatic) {
298: if (syntheticAccessors == null
299: || syntheticAccessors[READ] == null) {
300: codeStream.getstatic(this .codegenBinding);
301: } else {
302: codeStream.invokestatic(syntheticAccessors[READ]);
303: }
304: } else {
305: codeStream.dup();
306: if (syntheticAccessors == null
307: || syntheticAccessors[READ] == null) {
308: codeStream.getfield(this .codegenBinding);
309: } else {
310: codeStream.invokestatic(syntheticAccessors[READ]);
311: }
312: }
313: int operationTypeID;
314: switch (operationTypeID = (implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) {
315: case T_JavaLangString:
316: case T_JavaLangObject:
317: case T_undefined:
318: codeStream.generateStringConcatenationAppend(currentScope,
319: null, expression);
320: break;
321: default:
322: if (this .genericCast != null)
323: codeStream.checkcast(this .genericCast);
324: // promote the array reference to the suitable operation type
325: codeStream.generateImplicitConversion(implicitConversion);
326: // generate the increment value (will by itself be promoted to the operation value)
327: if (expression == IntLiteral.One) { // prefix operation
328: codeStream.generateConstant(expression.constant,
329: implicitConversion);
330: } else {
331: expression.generateCode(currentScope, codeStream, true);
332: }
333: // perform the operation
334: codeStream.sendOperator(operator, operationTypeID);
335: // cast the value back to the array reference type
336: codeStream
337: .generateImplicitConversion(assignmentImplicitConversion);
338: }
339: fieldStore(codeStream, this .codegenBinding,
340: syntheticAccessors == null ? null
341: : syntheticAccessors[WRITE], valueRequired);
342: // no need for generic cast as value got dupped
343: }
344:
345: public void generatePostIncrement(BlockScope currentScope,
346: CodeStream codeStream, CompoundAssignment postIncrement,
347: boolean valueRequired) {
348: boolean isStatic;
349: receiver.generateCode(currentScope, codeStream,
350: !(isStatic = this .codegenBinding.isStatic()));
351: if (isStatic) {
352: if (syntheticAccessors == null
353: || syntheticAccessors[READ] == null) {
354: codeStream.getstatic(this .codegenBinding);
355: } else {
356: codeStream.invokestatic(syntheticAccessors[READ]);
357: }
358: } else {
359: codeStream.dup();
360: if (syntheticAccessors == null
361: || syntheticAccessors[READ] == null) {
362: codeStream.getfield(this .codegenBinding);
363: } else {
364: codeStream.invokestatic(syntheticAccessors[READ]);
365: }
366: }
367: if (valueRequired) {
368: if (isStatic) {
369: if ((this .codegenBinding.type == TypeBinding.LONG)
370: || (this .codegenBinding.type == TypeBinding.DOUBLE)) {
371: codeStream.dup2();
372: } else {
373: codeStream.dup();
374: }
375: } else { // Stack: [owner][old field value] ---> [old field value][owner][old field value]
376: if ((this .codegenBinding.type == TypeBinding.LONG)
377: || (this .codegenBinding.type == TypeBinding.DOUBLE)) {
378: codeStream.dup2_x1();
379: } else {
380: codeStream.dup_x1();
381: }
382: }
383: }
384: if (this .genericCast != null)
385: codeStream.checkcast(this .genericCast);
386: codeStream.generateImplicitConversion(this .implicitConversion);
387: codeStream.generateConstant(postIncrement.expression.constant,
388: this .implicitConversion);
389: codeStream.sendOperator(postIncrement.operator,
390: this .implicitConversion & COMPILE_TYPE_MASK);
391: codeStream
392: .generateImplicitConversion(postIncrement.preAssignImplicitConversion);
393: fieldStore(codeStream, this .codegenBinding,
394: syntheticAccessors == null ? null
395: : syntheticAccessors[WRITE], false);
396: }
397:
398: /**
399: * @see org.eclipse.jdt.internal.compiler.lookup.InvocationSite#genericTypeArguments()
400: */
401: public TypeBinding[] genericTypeArguments() {
402: return null;
403: }
404:
405: public boolean isSuperAccess() {
406: return receiver.isSuper();
407: }
408:
409: public boolean isTypeAccess() {
410: return receiver != null && receiver.isTypeReference();
411: }
412:
413: /*
414: * No need to emulate access to protected fields since not implicitly accessed
415: */
416: public void manageSyntheticAccessIfNecessary(
417: BlockScope currentScope, FlowInfo flowInfo,
418: boolean isReadAccess) {
419: if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0)
420: return;
421:
422: // if field from parameterized type got found, use the original field at codegen time
423: this .codegenBinding = this .binding.original();
424:
425: if (binding.isPrivate()) {
426: if ((currentScope.enclosingSourceType() != this .codegenBinding.declaringClass)
427: && binding.constant() == Constant.NotAConstant) {
428: if (syntheticAccessors == null)
429: syntheticAccessors = new MethodBinding[2];
430: syntheticAccessors[isReadAccess ? READ : WRITE] = ((SourceTypeBinding) this .codegenBinding.declaringClass)
431: .addSyntheticMethod(this .codegenBinding,
432: isReadAccess);
433: currentScope.problemReporter()
434: .needToEmulateFieldAccess(this .codegenBinding,
435: this , isReadAccess);
436: return;
437: }
438:
439: } else if (receiver instanceof QualifiedSuperReference) { // qualified super
440:
441: // qualified super need emulation always
442: SourceTypeBinding destinationType = (SourceTypeBinding) (((QualifiedSuperReference) receiver).currentCompatibleType);
443: if (syntheticAccessors == null)
444: syntheticAccessors = new MethodBinding[2];
445: syntheticAccessors[isReadAccess ? READ : WRITE] = destinationType
446: .addSyntheticMethod(this .codegenBinding,
447: isReadAccess);
448: currentScope.problemReporter().needToEmulateFieldAccess(
449: this .codegenBinding, this , isReadAccess);
450: return;
451:
452: } else if (binding.isProtected()) {
453:
454: SourceTypeBinding enclosingSourceType;
455: if (((bits & DepthMASK) != 0)
456: && binding.declaringClass.getPackage() != (enclosingSourceType = currentScope
457: .enclosingSourceType()).getPackage()) {
458:
459: SourceTypeBinding currentCompatibleType = (SourceTypeBinding) enclosingSourceType
460: .enclosingTypeAt((bits & DepthMASK) >> DepthSHIFT);
461: if (syntheticAccessors == null)
462: syntheticAccessors = new MethodBinding[2];
463: syntheticAccessors[isReadAccess ? READ : WRITE] = currentCompatibleType
464: .addSyntheticMethod(this .codegenBinding,
465: isReadAccess);
466: currentScope.problemReporter()
467: .needToEmulateFieldAccess(this .codegenBinding,
468: this , isReadAccess);
469: return;
470: }
471: }
472: // if the binding declaring class is not visible, need special action
473: // for runtime compatibility on 1.2 VMs : change the declaring class of the binding
474: // NOTE: from target 1.2 on, field's declaring class is touched if any different from receiver type
475: // and not from Object or implicit static field access.
476: if (this .binding.declaringClass != this .receiverType
477: && !this .receiverType.isArrayType()
478: && this .binding.declaringClass != null // array.length
479: && this .binding.constant() == Constant.NotAConstant) {
480: CompilerOptions options = currentScope.compilerOptions();
481: if ((options.targetJDK >= ClassFileConstants.JDK1_2
482: && (options.complianceLevel >= ClassFileConstants.JDK1_4 || !(receiver
483: .isImplicitThis() && this .codegenBinding
484: .isStatic())) && this .binding.declaringClass.id != T_JavaLangObject) // no change for Object fields
485: || !this .binding.declaringClass
486: .canBeSeenBy(currentScope)) {
487:
488: this .codegenBinding = currentScope
489: .enclosingSourceType().getUpdatedFieldBinding(
490: this .codegenBinding,
491: (ReferenceBinding) this .receiverType
492: .erasure());
493: }
494: }
495: }
496:
497: public int nullStatus(FlowInfo flowInfo) {
498: return FlowInfo.UNKNOWN;
499: }
500:
501: public Constant optimizedBooleanConstant() {
502: switch (this .resolvedType.id) {
503: case T_boolean:
504: case T_JavaLangBoolean:
505: return this .constant != Constant.NotAConstant ? this .constant
506: : this .binding.constant();
507: default:
508: return Constant.NotAConstant;
509: }
510: }
511:
512: /**
513: * @see org.eclipse.jdt.internal.compiler.ast.Expression#postConversionType(Scope)
514: */
515: public TypeBinding postConversionType(Scope scope) {
516: TypeBinding convertedType = this .resolvedType;
517: if (this .genericCast != null)
518: convertedType = this .genericCast;
519: int runtimeType = (this .implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4;
520: switch (runtimeType) {
521: case T_boolean:
522: convertedType = TypeBinding.BOOLEAN;
523: break;
524: case T_byte:
525: convertedType = TypeBinding.BYTE;
526: break;
527: case T_short:
528: convertedType = TypeBinding.SHORT;
529: break;
530: case T_char:
531: convertedType = TypeBinding.CHAR;
532: break;
533: case T_int:
534: convertedType = TypeBinding.INT;
535: break;
536: case T_float:
537: convertedType = TypeBinding.FLOAT;
538: break;
539: case T_long:
540: convertedType = TypeBinding.LONG;
541: break;
542: case T_double:
543: convertedType = TypeBinding.DOUBLE;
544: break;
545: default:
546: }
547: if ((this .implicitConversion & BOXING) != 0) {
548: convertedType = scope.environment().computeBoxingType(
549: convertedType);
550: }
551: return convertedType;
552: }
553:
554: public StringBuffer printExpression(int indent, StringBuffer output) {
555: return receiver.printExpression(0, output).append('.').append(
556: token);
557: }
558:
559: public TypeBinding resolveType(BlockScope scope) {
560: // Answer the signature type of the field.
561: // constants are propaged when the field is final
562: // and initialized with a (compile time) constant
563:
564: //always ignore receiver cast, since may affect constant pool reference
565: boolean receiverCast = false;
566: if (this .receiver instanceof CastExpression) {
567: this .receiver.bits |= DisableUnnecessaryCastCheck; // will check later on
568: receiverCast = true;
569: }
570: this .receiverType = receiver.resolveType(scope);
571: if (this .receiverType == null) {
572: constant = Constant.NotAConstant;
573: return null;
574: }
575: if (receiverCast) {
576: // due to change of declaring class with receiver type, only identity cast should be notified
577: if (((CastExpression) this .receiver).expression.resolvedType == this .receiverType) {
578: scope.problemReporter().unnecessaryCast(
579: (CastExpression) this .receiver);
580: }
581: }
582: // the case receiverType.isArrayType and token = 'length' is handled by the scope API
583: FieldBinding fieldBinding = this .codegenBinding = this .binding = scope
584: .getField(this .receiverType, token, this );
585: if (!fieldBinding.isValidBinding()) {
586: constant = Constant.NotAConstant;
587: scope.problemReporter().invalidField(this ,
588: this .receiverType);
589: return null;
590: }
591: TypeBinding receiverErasure = this .receiverType.erasure();
592: if (receiverErasure instanceof ReferenceBinding) {
593: if (receiverErasure
594: .findSuperTypeWithSameErasure(fieldBinding.declaringClass) == null) {
595: this .receiverType = fieldBinding.declaringClass; // handle indirect inheritance thru variable secondary bound
596: }
597: }
598: this .receiver.computeConversion(scope, this .receiverType,
599: this .receiverType);
600: if (isFieldUseDeprecated(fieldBinding, scope,
601: (this .bits & IsStrictlyAssigned) != 0)) {
602: scope.problemReporter().deprecatedField(fieldBinding, this );
603: }
604: boolean isImplicitThisRcv = receiver.isImplicitThis();
605: constant = isImplicitThisRcv ? fieldBinding.constant()
606: : Constant.NotAConstant;
607: if (fieldBinding.isStatic()) {
608: // static field accessed through receiver? legal but unoptimal (optional warning)
609: if (!(isImplicitThisRcv || (receiver instanceof NameReference && (((NameReference) receiver).bits & Binding.TYPE) != 0))) {
610: scope.problemReporter().nonStaticAccessToStaticField(
611: this , fieldBinding);
612: }
613: if (!isImplicitThisRcv
614: && fieldBinding.declaringClass != receiverType
615: && fieldBinding.declaringClass.canBeSeenBy(scope)) {
616: scope.problemReporter().indirectAccessToStaticField(
617: this , fieldBinding);
618: }
619: }
620: // perform capture conversion if read access
621: return this .resolvedType = (((this .bits & IsStrictlyAssigned) == 0) ? fieldBinding.type
622: .capture(scope, this .sourceEnd)
623: : fieldBinding.type);
624: }
625:
626: public void setActualReceiverType(ReferenceBinding receiverType) {
627: // ignored
628: }
629:
630: public void setDepth(int depth) {
631: bits &= ~DepthMASK; // flush previous depth if any
632: if (depth > 0) {
633: bits |= (depth & 0xFF) << DepthSHIFT; // encoded on 8 bits
634: }
635: }
636:
637: public void setFieldIndex(int index) {
638: // ignored
639: }
640:
641: public void traverse(ASTVisitor visitor, BlockScope scope) {
642: if (visitor.visit(this, scope)) {
643: receiver.traverse(visitor, scope);
644: }
645: visitor.endVisit(this, scope);
646: }
647: }
|