Skip to main content
summaryrefslogtreecommitdiffstats
blob: d8f65aac1dbcf969d3d9e245d617dffea88e0607 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
/**********************************************************************
 * This file is part of "Object Teams Development Tooling"-Software
 *
 * Copyright 2003, 2008 Fraunhofer Gesellschaft, Munich, Germany,
 * for its Fraunhofer Institute for Computer Architecture and Software
 * Technology (FIRST), Berlin, Germany and Technical University Berlin,
 * Germany.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 * $Id: $
 *
 * Please visit http://www.eclipse.org/objectteams for updates and contact.
 *
 * Contributors:
 * Fraunhofer FIRST - Initial API and implementation
 * Technical University Berlin - Initial API and implementation
 **********************************************************************/
package org.eclipse.jdt.internal.compiler.parser;

import org.eclipse.jdt.core.compiler.InvalidInputException;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.Block;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.objectteams.otdt.core.compiler.IOTConstants;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.AbstractMethodMappingDeclaration;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.CallinMappingDeclaration;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.CalloutMappingDeclaration;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.GuardPredicateDeclaration;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.MethodSpec;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.AstGenerator;

/**
 * TODO(SH): handle paramter mappings
 *
 * NEW for OTDT.
 * started from COPY&PASTE from RecoveredMethod
 *
 * Internal method mapping structure for parsing recovery
 */
public class RecoveredMethodMapping extends RecoveredElement implements TerminalTokens {

	public AbstractMethodMappingDeclaration methodMappingDeclaration;

	// yet unused structure of base method specs by their method declaration:
	public RecoveredMethod[] baseMethods;
	int baseMethodCount = 0;

	public boolean foundBase = false; // for completion on base guard of recovered callin binding

	private Expression predicateExpression;

public RecoveredMethodMapping(AbstractMethodMappingDeclaration methodMapping, RecoveredElement parent, int bracketBalance, Parser parser){
	super(parent, bracketBalance, parser);
	this.methodMappingDeclaration = methodMapping;
	this.foundOpeningBrace = false; // allways created before '{' is seen.
}
/*
 * Record a nested block declaration
 */
@Override
public RecoveredElement add(Block nestedBlockDeclaration, int bracketBalanceValue) {

	// TODO(SH): should param mapping be treated as block?

	/* default behavior is to delegate recording to parent if any,
	do not consider elements passed the known end (if set)
	it must be belonging to an enclosing element
	*/
	if (this.methodMappingDeclaration.declarationSourceEnd > 0
		&& nestedBlockDeclaration.sourceStart
			> this.methodMappingDeclaration.declarationSourceEnd){
				if (this.parent == null){
					return this; // ignore
				} else {
					return this.parent.add(nestedBlockDeclaration, bracketBalanceValue);
				}
	}
	/* consider that if the opening brace was not found, it is there */
	if (!this.foundOpeningBrace){
		this.foundOpeningBrace = true;
		this.bracketBalance++;
	}

	return this;
}
@Override
public RecoveredElement add(Statement statement, int bracketBalanceValue)
{
	AstGenerator gen= new AstGenerator(statement.sourceStart, statement.sourceEnd);

	// adding a statement to a method mapping is interpreted as adding a guard predicate:
	GuardPredicateDeclaration predicate= null;
	if (this.methodMappingDeclaration instanceof CallinMappingDeclaration)
		predicate = ((CallinMappingDeclaration) this.methodMappingDeclaration).predicate;
	if (predicate == null) {
		predicate =	new GuardPredicateDeclaration(
						this.methodMappingDeclaration.compilationResult,
						this.foundBase ? IOTConstants.BASE_PREDICATE_PREFIX : IOTConstants.PREDICATE_METHOD_NAME,
						this.foundBase,
						statement.sourceStart-5, // guess position of "when"
						statement.sourceStart-1);// -- " --
		if (this.foundBase) {
			// generate base argument (cf. Parser.consumePredicate):
			// type of this argument will be set in SourceTypeBinding.resolveTypesFor
			// (needs baseclass to be resolved, which might be inherited from super-role).
			long poss = ((long)statement.sourceStart<<32)+statement.sourceStart+1;
			Argument targetArg = new Argument(
					IOTConstants.BASE,
					poss,
					new SingleTypeReference("_OT$unknownBaseType".toCharArray(), poss),  //$NON-NLS-1$
					ClassFileConstants.AccFinal);
			predicate.arguments = new Argument[] {targetArg};
		}
		predicate.returnType= gen.typeReference(TypeBinding.BOOLEAN);
	}

	// create a suitable return statement:
	if (statement instanceof FieldDeclaration)
		predicate.statements = new Statement[] {((FieldDeclaration)statement).initialization};
	else if (statement instanceof Expression)
		predicate.updatePredicateExpression((Expression)statement, statement.sourceEnd+1);

	// add as method to enclosing type:
	this.parent.add(predicate, bracketBalanceValue);
	return this;
}
/**
 * This method records a base method spec as part of the method mapping.
 * (Note that the inherited method must be overridden and 'this' must be returned instead of 'parent').
 */
@Override
public RecoveredElement add(AbstractMethodDeclaration methodDeclaration, int bracketBalanceValue)
{
	// check whether methodDeclaration is the start of a new member rather than an encoding for a base method spec:
	Scanner scanner = parser().scanner;
	int saveCurrent= scanner.currentPosition;
	int saveStart=   scanner.startPosition;
	try {
		int nextToken = scanner.getNextToken();
		switch (nextToken) {
		case TerminalTokens.TokenNameLBRACE:   // it's a real method
		case TerminalTokens.TokenNameBINDIN:   // it's the start of a new callin mapping
		case TerminalTokens.TokenNameBINDOUT:  // it's the start of a new callout mapping
			// add real method or mapping to the class:
			return super.add(methodDeclaration, bracketBalanceValue);
		}
	} catch (InvalidInputException e) {
		// ignore
	} finally {
		// restore
		scanner.currentPosition= saveCurrent;
		scanner.startPosition=   saveStart;
	}

	if (!(methodDeclaration instanceof MethodDeclaration)) // see https://svn.objectteams.org/trac/ot/ticket/275
		return this.parent;

	// so it indeed seems to be a method spec (encoded as a method decl):
	if (this.baseMethods == null) {
		this.baseMethods = new RecoveredMethod[5];
		this.baseMethodCount = 0;
	} else {
		if (this.baseMethodCount == this.baseMethods.length) {
			System.arraycopy(
				this.baseMethods,
				0,
				(this.baseMethods = new RecoveredMethod[2 * this.baseMethodCount]),
				0,
				this.baseMethodCount);
		}
	}
	RecoveredMethod element = new RecoveredMethod(methodDeclaration, this, bracketBalanceValue, this.recoveringParser);
	this.baseMethods[this.baseMethodCount++] = element;
	/* if methodspec not finished, then methodspec becomes current */
	if (methodDeclaration.declarationSourceEnd == 0)
		return element;
	return this;
}
public void addMethodSpec(MethodSpec methodSpec) {
	if (this.foundOpeningBrace)
		return; // TODO(SH): may try to convert this to a parameter mapping?
	if (this.methodMappingDeclaration.isCallout()) {
		((CalloutMappingDeclaration)this.methodMappingDeclaration).baseMethodSpec = methodSpec;
	} else {
		CallinMappingDeclaration mapping = (CallinMappingDeclaration) this.methodMappingDeclaration;
		if (this.baseMethodCount == 0) {
			mapping.baseMethodSpecs = new MethodSpec[] { methodSpec };
			this.baseMethodCount++;
		} else {
			System.arraycopy(mapping.baseMethodSpecs, 0,
							 mapping.baseMethodSpecs = new MethodSpec[this.baseMethodCount+1], 0,
							 this.baseMethodCount);
			mapping.baseMethodSpecs[this.baseMethodCount++] = methodSpec;
		}
	}
}
@Override
public RecoveredElement add(AbstractMethodMappingDeclaration methodMapping, int bracketBalanceValue)
{
	if (this.foundOpeningBrace)
		// after "{" nested mapping is most likely to be a misread param mapping. Discard.
		return this;
	return super.add(methodMapping, bracketBalanceValue);
}
@Override
public RecoveredElement add(FieldDeclaration fieldDeclaration, int bracketBalanceValue) {
	// check whether fieldDeclaration is a misread method spec rather than a real field
	switch (parser().currentToken) {
	case TerminalTokens.TokenNameBINDIN:   // it's the start of a new callin mapping
	case TerminalTokens.TokenNameBINDOUT:  // it's the start of a new callout mapping
		// ignore misread field:
		return this;
	}

	return super.add(fieldDeclaration, bracketBalanceValue);
}
/*
 * Answer the associated parsed structure
 */
@Override
public ASTNode parseTree(){
	return this.methodMappingDeclaration;
}
/*
 * Answer the very source end of the corresponding parse node
 */
@Override
public int sourceEnd(){
	return this.methodMappingDeclaration.declarationSourceEnd;
}
@Override
public String toString(int tab) {
	StringBuffer result = new StringBuffer(tabString(tab));
	result.append("Recovered method mapping:\n"); //$NON-NLS-1$
	this.methodMappingDeclaration.print(tab + 1, result);
	for (int i = 0; i < this.baseMethodCount; i++)
		result.append(this.baseMethods[i].toString(tab + 1));
	return result.toString();
}
/*
 * Update the bodyStart of the corresponding parse node
 */
@Override
public void updateBodyStart(int bodyStart){
	this.foundOpeningBrace = true;
	this.methodMappingDeclaration.bodyStart = bodyStart;
}
@Override
public void updateFromParserState() {
	Parser parser = parser();
	if (this.predicateExpression == null && parser.expressionPtr > -1) {
		this.predicateExpression = parser.expressionStack[parser.expressionPtr--];
	}
}
public AbstractMethodMappingDeclaration updatedMethodMappingDeclaration(int bodyEnd)
{
	// update/transfer base methods
	MethodSpec[] baseMethodSpecs = this.methodMappingDeclaration.getBaseMethodSpecs();
	int existingCount= baseMethodSpecs==null ? null : baseMethodSpecs.length;
	if (this.baseMethodCount > existingCount) {
		if (this.methodMappingDeclaration.isCallout()) {
			CalloutMappingDeclaration callout= (CalloutMappingDeclaration)this.methodMappingDeclaration;
			callout.baseMethodSpec= new MethodSpec(this.baseMethods[0].methodDeclaration);
		} else {
			CallinMappingDeclaration callinMapping= (CallinMappingDeclaration)this.methodMappingDeclaration;
			callinMapping.baseMethodSpecs= new MethodSpec[this.baseMethodCount];
			for (int i=0; i<this.baseMethodCount; i++)
				callinMapping.baseMethodSpecs[i]=
					new MethodSpec(this.baseMethods[i].methodDeclaration);
		}
	}

// TODO(SH): recovery of parameter mapping details.
	if (   this.methodMappingDeclaration.declarationSourceEnd <= this.methodMappingDeclaration.declarationSourceStart
		&& bodyEnd > this.methodMappingDeclaration.declarationSourceStart)

		this.methodMappingDeclaration.declarationSourceEnd= bodyEnd;

	// update unfinished binding-level predicate:
	if (this.methodMappingDeclaration.isCallin()) {
		CallinMappingDeclaration callinDecl = (CallinMappingDeclaration) this.methodMappingDeclaration;
		if (   callinDecl.predicate != null
			&& callinDecl.predicate.bodyStart == 0)
		{
			Parser parser = parser();
			if (this.predicateExpression != null)
				callinDecl.predicate.updatePredicateExpression(this.predicateExpression, this.predicateExpression.sourceEnd);
			else if (parser.expressionPtr > -1)
				parser.consumePredicateExpression();
			else
				callinDecl.predicate.tagAsHavingErrors();

		}
	}
	return this.methodMappingDeclaration;
}
/*
 * An opening brace got consumed, might be the expected opening one of the current element,
 * in which case the bodyStart is updated.
 */
@Override
public RecoveredElement updateOnOpeningBrace(int braceStart, int braceEnd){

	/* in case the opening brace is close enough to the signature */
	if (this.bracketBalance == 0){
		/*
			if (parser.scanner.searchLineNumber(methodDeclaration.sourceEnd)
				!= parser.scanner.searchLineNumber(braceEnd)){
		 */
		switch(parser().lastIgnoredToken){
			case -1 :
				break;
			default:
				this.foundOpeningBrace = true;
				this.bracketBalance = 1; // pretend the brace was already there
		}
	}
	return super.updateOnOpeningBrace(braceStart, braceEnd);
}
@Override
public void updateParseTree(){
	updatedMethodMappingDeclaration(-1);
}
@Override
public RecoveredElement updateOnClosingBrace(int braceStart, int braceEnd) {
	Parser parser = parser();
	if (this.baseMethodCount == 0 && parser.identifierPtr > -1) {
		// recover missing base method spec:
		MethodSpec baseSpec = null;
		if (   this.methodMappingDeclaration.isCallin()
			|| (((CalloutMappingDeclaration)this.methodMappingDeclaration).baseMethodSpec == null)) 
		{
			if (this.methodMappingDeclaration.hasSignature) {
				if (parser.identifierPtr > 0) {
					// Type selector
					baseSpec = new MethodSpec(parser.identifierStack[parser.identifierPtr], parser.identifierPositionStack[parser.identifierPtr--]);
					parser.identifierLengthPtr--;
				} else {
					// Type <missing>
					baseSpec = new MethodSpec("missing".toCharArray(), parser.identifierPositionStack[parser.identifierPtr]); //$NON-NLS-1$
				}
				baseSpec.hasSignature = true;
				boolean identifierAvailable =    (parser.identifierLengthPtr > -1 && parser.identifierLengthStack[parser.identifierLengthPtr] < 0)
										      || (parser.genericsIdentifiersLengthPtr > -1 && parser.genericsIdentifiersLengthStack[parser.genericsIdentifiersLengthPtr] > 0);
				if (identifierAvailable) {
					baseSpec.returnType = parser.getTypeReference(0);
					baseSpec.declarationSourceStart = baseSpec.returnType.sourceStart;
				}
			} else {
				// selector
				baseSpec = new MethodSpec(parser.identifierStack[parser.identifierPtr], parser.identifierPositionStack[parser.identifierPtr--]);
				parser.identifierLengthPtr--;
			}
			// don't overwrite existing base method spec:
			this.methodMappingDeclaration.checkAddBasemethodSpec(baseSpec);
		}
	}
	if (!this.foundOpeningBrace) {
		this.updateSourceEndIfNecessary(braceStart - 1, braceStart - 1);
		return this.parent.updateOnClosingBrace(braceStart, braceEnd);
	}
	return super.updateOnClosingBrace(braceStart, braceEnd);
}
/*
 * Update the declarationSourceEnd of the corresponding parse node
 */
@Override
public void updateSourceEndIfNecessary(int braceStart, int braceEnd){
	if (this.methodMappingDeclaration.declarationSourceEnd == 0) {
		if(parser().rBraceSuccessorStart >= braceEnd) {
			this.methodMappingDeclaration.declarationSourceEnd = parser().rBraceEnd;
			this.methodMappingDeclaration.bodyEnd = parser().rBraceStart;
		} else {
			this.methodMappingDeclaration.declarationSourceEnd = braceEnd;
			this.methodMappingDeclaration.bodyEnd  = braceStart - 1;
		}
	}
	MethodSpec[] baseMethodSpecs = this.methodMappingDeclaration.getBaseMethodSpecs();
	if (baseMethodSpecs != null && baseMethodSpecs.length == 1) {
		MethodSpec baseMethod = baseMethodSpecs[0];
		if (baseMethod.sourceStart >= braceStart) {
			// next declaration claims what we erroneously read as a base method spec
			int start = baseMethod.sourceStart-1;
			MethodSpec newSpec = parser().newMethodSpec(new char[0], (((long)start)<<32) +start);
			if (this.methodMappingDeclaration.isCallin())
				((CallinMappingDeclaration)this.methodMappingDeclaration).baseMethodSpecs = new MethodSpec[] {newSpec};
			else
				((CalloutMappingDeclaration)this.methodMappingDeclaration).baseMethodSpec = newSpec;
		}
	}
}
public void setCallinModifier(int tokenID, int start, int end) {
	if (this.methodMappingDeclaration instanceof CallinMappingDeclaration) {
		CallinMappingDeclaration decl = ((CallinMappingDeclaration)this.methodMappingDeclaration);
		decl.callinModifier= tokenID;
		decl.modifierStart= start;
		decl.modifierEnd= end;
	}
}
}

Back to the top