Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 97662db238e0702310fbfc0502fa5157b32be4c6 (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
/*******************************************************************************
 * Copyright (c) 2007, 2013 Borland Software 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:
 *     Borland Software Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.m2m.internal.qvt.oml.evaluator;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.m2m.internal.qvt.oml.ast.binding.ASTBindingHelper;
import org.eclipse.m2m.internal.qvt.oml.ast.binding.ASTSyntheticNode;
import org.eclipse.m2m.internal.qvt.oml.ast.binding.ASTSyntheticNodeAccess;
import org.eclipse.m2m.internal.qvt.oml.ast.binding.IModuleSourceInfo;
import org.eclipse.m2m.internal.qvt.oml.ast.env.InternalEvaluationEnv;
import org.eclipse.m2m.internal.qvt.oml.ast.env.QvtOperationalEvaluationEnv;
import org.eclipse.m2m.internal.qvt.oml.ast.parser.QvtOperationalParserUtil;
import org.eclipse.m2m.internal.qvt.oml.emf.util.EmfUtil;
import org.eclipse.m2m.internal.qvt.oml.expressions.ImperativeOperation;
import org.eclipse.m2m.internal.qvt.oml.expressions.Module;
import org.eclipse.ocl.ecore.FeatureCallExp;
import org.eclipse.ocl.expressions.VariableExp;
import org.eclipse.ocl.utilities.ASTNode;

/**
 * Helps to build QVT stack trace from a given state of QVT code execution.
 */
public class QvtStackTraceBuilder {
	
	private static final String UNKNOWN_NAME = "<Unknown>"; //$NON-NLS-1$
	private static final int UNKNOWN_LINE_NUM = -1;
	
	private QvtOperationalEvaluationEnv fEvalEnv;

	/**
	 * Constructs stack trace builder for the given evaluation environment.
	 * 
	 * @param evalEnv
	 *            the evaluation environment representing the top stack trace
	 *            
	 * @param astNodeIPOffset explicit the AST node offset representing the current instruction 
	 *		pointer of execution in a QVT module
	 */
	public QvtStackTraceBuilder(QvtOperationalEvaluationEnv evalEnv) {
		if(evalEnv == null) {
			throw new IllegalArgumentException();
		}
		
		fEvalEnv = evalEnv;
	}
	
	/**
	 * Builds the stack trace corresponding to evaluation environments hierarchy
	 * associated with this builder.
	 * 
	 * @return list of QVT stack elements
	 */
    public List<QVTStackTraceElement> buildStackTrace() {
    	LinkedList<QVTStackTraceElement> elements = new LinkedList<QVTStackTraceElement>();
    	
    	for(QvtOperationalEvaluationEnv nextEnv = fEvalEnv; nextEnv != null; nextEnv = nextEnv.getParent()) {
    		// skip all the root execution environments as they 
    		// are not bound to any module code locations
    		QvtOperationalEvaluationEnv parent = nextEnv.getParent();
			if(parent != null) {
        		InternalEvaluationEnv internalEnv = nextEnv.getAdapter(InternalEvaluationEnv.class);
        		// skip all stack frames not running in a module, 
        		// IOW possible non QVT transformation clients
        		if(internalEnv.getCurrentModule() != null) {		
        			elements.addLast(createStackElement(nextEnv));
        		}
    		}
    	}
    	
    	QvtOperationalEvaluationEnv rootEnv = fEvalEnv.getRoot();
		QvtOperationalEvaluationEnv aggregatingEnv = EvaluationUtil.getAggregatingContext(rootEnv);
		if(aggregatingEnv != null) {
			List<QVTStackTraceElement> aggregatedStackTrace = new QvtStackTraceBuilder(aggregatingEnv).buildStackTrace();			
			List<QVTStackTraceElement> result = new ArrayList<QVTStackTraceElement>(elements.size() + aggregatedStackTrace.size());
			result.addAll(elements);
			result.addAll(aggregatedStackTrace);
			return result;
		}
    	return Collections.unmodifiableList(elements);
    }

    private QVTStackTraceElement createStackElement(QvtOperationalEvaluationEnv env) {
    	String unitName = null;
    	String moduleName = UNKNOWN_NAME;
    	String operName = UNKNOWN_NAME;
    	int lineNumber = UNKNOWN_LINE_NUM;    	
    	
    	Module module = null;
    	ImperativeOperation operation = env.getOperation();

    	InternalEvaluationEnv internEvalEnv = env.getAdapter(InternalEvaluationEnv.class);
    	int resultOffset = getCurrentASTOffset(internEvalEnv);
		
    	ModuleInstance thisInstance = internEvalEnv.getCurrentModule();
    	if(thisInstance == null) {
    		throw new IllegalArgumentException("Currently executed model is not set in environment"); //$NON-NLS-1$
    	}
    	
    	module = thisInstance.getModule();
    	assert module != null;
    	moduleName = module.getName();
    	
		if(operation == null) {
			// we must be executing a module instance initialization - synthetic constructor
	    	operName = moduleName;
	    	
	    	if(internEvalEnv.getCurrentIP() == module || resultOffset < -1) {
	    		ASTSyntheticNode astNode = ASTSyntheticNodeAccess.getASTNode(module);
		    	if(astNode != null) {
		    		resultOffset = astNode.getStartPosition();
		    	}
	    	}
		} else {
    		operName = operation.getName();	    		
    		EClassifier contextType = QvtOperationalParserUtil.getContextualType(operation);
    		if(contextType != null) {
    			operName = contextType.getName() + EmfUtil.PATH_SEPARATOR + operName;
    		}
		}

		IModuleSourceInfo sourceInfo = ASTBindingHelper.getModuleSourceBinding(module);
		if(sourceInfo != null) {
			URI uri = sourceInfo.getSourceURI();
			unitName = uri.lastSegment();
			if(resultOffset >= 0) {
				lineNumber = sourceInfo.getLineNumberProvider().getLineNumber(resultOffset);
			}
		}
    	
    	return new QVTStackTraceElement(moduleName, operName, unitName, lineNumber);
    }
    
    private static int getCurrentASTOffset(InternalEvaluationEnv evalEnv) {
    	// TODO - for cases that AST does not fill all offset
    	// traverse up to the enclosing operation scope, taking the closest 
    	// offset which has been initialized    	
    	EObject currentIPObject = evalEnv.getCurrentIP();
    	
    	if(currentIPObject instanceof ASTNode) {
    		ASTNode astNode = (ASTNode) currentIPObject;
    		
			if(astNode.getStartPosition() < 0 && astNode instanceof VariableExp<?, ?>) {
				// Remark: special processing for implicit source variables represented as
				// synthetic variable expression in AST. These do not have any CST representation
				// but are rather synthetic nodes => point to the call AST
				if(astNode.eContainer() instanceof FeatureCallExp) {
					astNode = (FeatureCallExp) astNode.eContainer();
				}
			}    		
    		
    		return astNode.getStartPosition();
    	}
    	
		ASTSyntheticNode astNode = ASTSyntheticNodeAccess.getASTNode(currentIPObject);
		if(astNode != null) {
			return astNode.getStartPosition();
		}
    	
    	return -1;
    }
}

Back to the top