1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-29 19:45:01 +02:00

Engine for exploring macro expansions step by step, bug 23540.

This commit is contained in:
Markus Schorn 2008-01-16 16:43:52 +00:00
parent 3c2dd52cf7
commit 1c83f17a74
27 changed files with 1517 additions and 214 deletions

View file

@ -0,0 +1,213 @@
/*******************************************************************************
* Copyright (c) 2008 Wind River Systems, Inc. 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:
* Markus Schorn - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.core.parser.tests.scanner;
import junit.framework.TestSuite;
import org.eclipse.cdt.core.dom.parser.c.GCCScannerExtensionConfiguration;
import org.eclipse.cdt.core.parser.CodeReader;
import org.eclipse.cdt.core.parser.IToken;
import org.eclipse.cdt.core.parser.NullLogService;
import org.eclipse.cdt.core.parser.OffsetLimitReachedException;
import org.eclipse.cdt.core.parser.ParserLanguage;
import org.eclipse.cdt.core.parser.ScannerInfo;
import org.eclipse.cdt.core.testplugin.CTestPlugin;
import org.eclipse.cdt.core.testplugin.util.BaseTestCase;
import org.eclipse.cdt.core.testplugin.util.TestSourceReader;
import org.eclipse.cdt.internal.core.dom.NullCodeReaderFactory;
import org.eclipse.cdt.internal.core.parser.scanner.CPreprocessor;
import org.eclipse.cdt.internal.core.parser.scanner.MacroExpander;
import org.eclipse.cdt.internal.core.parser.scanner.MacroExpansionTracker;
import org.eclipse.text.edits.ReplaceEdit;
public class ExpansionExplorerTests extends BaseTestCase {
public static TestSuite suite() {
return suite(ExpansionExplorerTests.class);
}
private void performTest(int steps) throws Exception {
StringBuffer[] bufs= TestSourceReader.getContentsForTest(
CTestPlugin.getDefault().getBundle(), "parser", getClass(), getName(), steps+2);
String[] input= new String[steps+2];
int i= -1;
for (StringBuffer buf : bufs) {
input[++i]= buf.toString().trim();
}
final MacroExpander expander= createExpander(input[0]);
final String original= input[1];
verifyStep(expander, original, Integer.MAX_VALUE, original, input[steps+1]);
for (i= 0; i < steps; i++) {
verifyStep(expander, original, i, input[i+1], input[i+2]);
}
}
private void verifyStep(MacroExpander expander, String original, int step, String expectedPre,
String expectedPost) {
MacroExpansionTracker tracker= new MacroExpansionTracker(step);
expander.expand(original, tracker);
String pre = tracker.getCodeBeforeStep();
ReplaceEdit replacement = tracker.getReplacement();
assertNotNull(pre);
assertNotNull(replacement);
String post= apply(pre, replacement);
assertEquals(expectedPre, pre);
assertEquals(expectedPost, post);
}
private String apply(String pre, ReplaceEdit replacement) {
StringBuilder buf= new StringBuilder();
buf.append(pre, 0, replacement.getOffset());
buf.append(replacement.getText());
buf.append(pre, replacement.getExclusiveEnd(), pre.length());
return buf.toString();
}
private MacroExpander createExpander(final String macrodefs) throws OffsetLimitReachedException {
CPreprocessor cpp= new CPreprocessor(new CodeReader(macrodefs.toCharArray()),
new ScannerInfo(), ParserLanguage.C, new NullLogService(),
new GCCScannerExtensionConfiguration(), NullCodeReaderFactory.getInstance());
int type;
do {
type= cpp.nextTokenRaw().getType();
} while (type != IToken.tEND_OF_INPUT);
return (MacroExpander) cpp.getAdapter(MacroExpander.class);
}
// #define A
// B
// B
public void testNoOp() throws Exception {
performTest(1);
}
// #define A B
// A
// B
public void testObject() throws Exception {
performTest(1);
}
// #define A A1
// #define A1 A2
// #define A2 A
// A
// A1
// A2
// A
public void testObjectChain() throws Exception {
performTest(3);
}
// #define A(x) B+x
// A(c)
// B+c
public void testFunction() throws Exception {
performTest(1);
}
// #define A(x) x+x
// #define _t t
// A(_t)
// A(t)
// t+t
public void testFunctionParam() throws Exception {
performTest(2);
}
// #define A(x,y) x+y
// #define _t t
// A(_t, _t)
// A(t, _t)
// A(t, t)
// t+t
public void test2Params() throws Exception {
performTest(3);
}
// #define A(x,y,z) x + y + z
// #define _t t
// A ( _t , , _t )
// A ( t , , _t )
// A ( t , , t )
// t + + t
public void test3Params() throws Exception {
performTest(3);
}
// #define m !(m)+n
// #define n(n) n(m)
// m(m)
// !(m)+n(m)
// !(m)+n(!(m)+n)
// !(m)+!(m)+n(m)
// !(m)+!(m)+n(!(m)+n)
public void testRecursiveExpansion() throws Exception {
performTest(4);
}
// #define f(x,y) (x + y)
// #define g(x,y) (x*y)
// #define _a a
// #define _b b
// f( g(_a,_b), g(_b,_a) )
// f( g(a,_b), g(_b,_a) )
// f( g(a,b), g(_b,_a) )
// f( (a*b), g(_b,_a) )
// f( (a*b), g(b,_a) )
// f( (a*b), g(b,a) )
// f( (a*b), (b*a) )
// ((a*b) + (b*a))
public void testNestedFunctions() throws Exception {
performTest(7);
}
}

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2007 Wind River Systems, Inc. and others.
* Copyright (c) 2007, 2008 Wind River Systems, Inc. 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
@ -464,17 +464,18 @@ public class LocationMapTests extends BaseTestCase {
fLocationMap.encounteredComment(12, 23, false);
checkComment(fLocationMap.getComments()[0], new String(LONGDIGITS, 110, 15), false, FN, 110, 15, 2, 2);
IASTName[] refs= fLocationMap.getReferences(macro1);
IASTName[] refs= fLocationMap.getReferences(macro3);
assertEquals(1, refs.length);
checkName(refs[0], macro1, "n1", fTu, IASTTranslationUnit.EXPANSION_NAME, ROLE_REFERENCE, FN, 110, 15, 2, 2, new String(LONGDIGITS, 110, 15));
IASTName macro3ref= refs[0];
checkName(refs[0], macro3, "n3", fTu, IASTTranslationUnit.EXPANSION_NAME, ROLE_REFERENCE, FN, 110, 5, 2, 2, new String(LONGDIGITS, 110, 5));
refs= fLocationMap.getReferences(macro1);
assertEquals(1, refs.length);
checkName(refs[0], macro1, "n1", macro3ref, IASTTranslationUnit.EXPANSION_NAME, ROLE_REFERENCE, FN, 110, 15, 2, 2, new String(LONGDIGITS, 110, 15));
refs= fLocationMap.getReferences(macro2);
assertEquals(1, refs.length);
checkName(refs[0], macro2, "n2", fTu, IASTTranslationUnit.EXPANSION_NAME, ROLE_REFERENCE, FN, 110, 15, 2, 2, new String(LONGDIGITS, 110, 15));
refs= fLocationMap.getReferences(macro3);
assertEquals(1, refs.length);
checkName(refs[0], macro3, "n3", fTu, IASTTranslationUnit.EXPANSION_NAME, ROLE_REFERENCE, FN, 110, 5, 2, 2, new String(LONGDIGITS, 110, 5));
checkName(refs[0], macro2, "n2", macro3ref, IASTTranslationUnit.EXPANSION_NAME, ROLE_REFERENCE, FN, 110, 15, 2, 2, new String(LONGDIGITS, 110, 15));
}
public void testContexts() {

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2004, 2007 IBM Corporation and others.
* Copyright (c) 2004, 2008 IBM 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
@ -11,9 +11,6 @@
*******************************************************************************/
package org.eclipse.cdt.core.parser.tests.scanner;
import java.util.Iterator;
import java.util.List;
import junit.framework.TestSuite;
import org.eclipse.cdt.core.parser.IProblem;

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2004, 2007 IBM Corporation and others.
* Copyright (c) 2004, 2008 IBM 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
@ -286,6 +286,69 @@ public class PreprocessorTests extends PreprocessorTestsBase {
validateProblemCount(0);
}
// #define variadic(x...) (a, ##x)
// #define _c c
// variadic();
// variadic(_c);
// variadic(_c,_c);
public void testGccVariadicMacroExtensions2() throws Exception {
initializeScanner();
validateToken(IToken.tLPAREN);
validateIdentifier("a");
validateToken(IToken.tRPAREN);
validateToken(IToken.tSEMI);
validateToken(IToken.tLPAREN);
validateIdentifier("a");
validateToken(IToken.tCOMMA);
validateIdentifier("c");
validateToken(IToken.tRPAREN);
validateToken(IToken.tSEMI);
validateToken(IToken.tLPAREN);
validateIdentifier("a");
validateToken(IToken.tCOMMA);
validateIdentifier("c");
validateToken(IToken.tCOMMA);
validateIdentifier("c");
validateToken(IToken.tRPAREN);
validateToken(IToken.tSEMI);
validateEOF();
validateProblemCount(0);
}
// #define variadic(y, x...) (a, ##x)
// #define _c c
// variadic(1);
// variadic(,_c);
// variadic(,_c,_c);
public void testGccVariadicMacroExtensions3() throws Exception {
initializeScanner();
validateToken(IToken.tLPAREN);
validateIdentifier("a");
validateToken(IToken.tRPAREN);
validateToken(IToken.tSEMI);
validateToken(IToken.tLPAREN);
validateIdentifier("a");
validateToken(IToken.tCOMMA);
validateIdentifier("c");
validateToken(IToken.tRPAREN);
validateToken(IToken.tSEMI);
validateToken(IToken.tLPAREN);
validateIdentifier("a");
validateToken(IToken.tCOMMA);
validateIdentifier("c");
validateToken(IToken.tCOMMA);
validateIdentifier("c");
validateToken(IToken.tRPAREN);
validateToken(IToken.tSEMI);
validateEOF();
validateProblemCount(0);
}
// #define str(x) #x
// str();
public void testEmptyStringify() throws Exception {
@ -810,14 +873,14 @@ public class PreprocessorTests extends PreprocessorTestsBase {
}
public void _testSpecExample3_2() throws Exception {
public void testSpecExample3_2() throws Exception {
StringBuffer sb = getExample3Defines();
sb.append("g(x+(3,4)-w) | h 5) & m (f)^m(m); \n");
// f(2 * (2+(3,4)-0,1)) | f(2 * (~ 5)) & f(2 * (0,1))^m(0,1); //47
initializeScanner(sb.toString());
validateIdentifier("g");
validateIdentifier("f");
validateToken(IToken.tLPAREN);
validateInteger("2");
validateToken(IToken.tSTAR);
@ -849,7 +912,7 @@ public class PreprocessorTests extends PreprocessorTestsBase {
validateIdentifier("f");
validateToken(IToken.tLPAREN);
validateInteger("2");
validateToken(IToken.tLPAREN);
validateToken(IToken.tSTAR);
validateToken(IToken.tLPAREN);
validateInteger("0");
validateToken(IToken.tCOMMA);

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2007 Wind River Systems, Inc. and others.
* Copyright (c) 2007, 2008 Wind River Systems, Inc. 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
@ -23,6 +23,7 @@ public class ScannerTestSuite extends TestSuite {
suite.addTest(PreprocessorTests.suite());
suite.addTest(InclusionTests.suite());
suite.addTest(PreprocessorBugsTests.suite());
suite.addTest(ExpansionExplorerTests.suite());
return suite;
}
}

View file

@ -1,19 +0,0 @@
/*******************************************************************************
* Copyright (c) 2005, 2006 QNX Software Systems 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:
* QNX - Initial API and implementation
* Markus Schorn (Wind River Systems)
*******************************************************************************/
package org.eclipse.cdt.core.dom;
import org.eclipse.core.runtime.IAdaptable;
/**
* @deprecated use IIndex instead.
*/
public interface IPDOM extends IAdaptable {}

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2004, 2007 IBM Corporation and others.
* Copyright (c) 2004, 2008 IBM 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
@ -27,6 +27,12 @@ public interface IASTMacroExpansion extends IASTNodeLocation {
*/
public IASTPreprocessorMacroDefinition getMacroDefinition();
/**
* The macro reference for the explicit macro expansion containing this expansion.
* @since 5.0
*/
public IASTName getMacroReference();
/**
* Returns an offset within the macro-expansion. The offset can be used to compare
* nodes within the same macro-expansion. However, it does not serve as an offset

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2004, 2007 IBM Corporation and others.
* Copyright (c) 2004, 2008 IBM 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
@ -115,18 +115,9 @@ public interface IASTTranslationUnit extends IASTNode, IAdaptable {
* @return List of IASTName nodes representing uses of the binding
*/
public IASTName[] getReferences(IBinding binding);
/**
* Returns an array of locations. This is a sequence of file locations and macro-expansion locations.
* @param offset sequence number as stored in the ast nodes.
* @param length
* @return and array of locations.
* @deprecated the offsets needed for this method are not accessible via public API.
*/
public IASTNodeLocation[] getLocationInfo(int offset, int length);
/**
* Select the node in the treet that best fits the offset/length/file path.
* Select the node in the treat that best fits the offset/length/file path.
*
* @param path - file name specified through path
* @param offset - location in the file as an offset

View file

@ -0,0 +1,84 @@
/*******************************************************************************
* Copyright (c) 2008 Wind River Systems, Inc. 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:
* Markus Schorn - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.core.dom.rewrite;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.IMacroBinding;
import org.eclipse.cdt.internal.core.parser.scanner.MultiMacroExpansionExplorer;
import org.eclipse.jface.text.IRegion;
import org.eclipse.text.edits.ReplaceEdit;
/**
* Allows to understand macro expansions step by step.
* @since 5.0
*/
public abstract class MacroExpansionExplorer {
/**
* Representation of a single expansion step or a complete expansion.
*/
public interface IMacroExpansionStep {
/**
* Returns the code before this step.
*/
String getCodeBeforeStep();
/**
* Returns the code after this step.
*/
String getCodeAfterStep();
/**
* Returns an array of replacements representing the change from the code before
* this step to the code after this step.
*/
ReplaceEdit[] getReplacements();
/**
* Returns the macro that gets expanded in this step, or <code>null</code> for
* a step representing a full expansion.
*/
IMacroBinding getExpandedMacro();
}
/**
* Creates a macro expansion explorer for a given file location in a translation unit.
*/
public static MacroExpansionExplorer create(IASTTranslationUnit tu, IASTFileLocation loc) {
return new MultiMacroExpansionExplorer(tu, loc);
}
/**
* Creates a macro expansion explorer for a given region in the outermost file of a
* translation unit.
*/
public static MacroExpansionExplorer create(IASTTranslationUnit tu, IRegion loc) {
return new MultiMacroExpansionExplorer(tu, loc);
}
/**
* Returns the full expansion for the region of this expansion explorer.
*/
public abstract IMacroExpansionStep getFullExpansion();
/**
* Returns the total number of available steps for expanding the region of this expansion
* explorer.
*/
public abstract int getExpansionStepCount();
/**
* Returns a description for the requested step within the expansion of the region of this
* expansion explorer.
* @throws IndexOutOfBoundsException if step < 0 or step >= getExpansionStepCount().
*/
public abstract IMacroExpansionStep getExpansionStep(int step) throws IndexOutOfBoundsException;
}

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2007 IBM Corporation and others.
* Copyright (c) 2007, 2008 IBM 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
@ -10,9 +10,11 @@
*******************************************************************************/
package org.eclipse.cdt.core.parser.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
@ -174,6 +176,15 @@ public final class CharArrayMap<V> implements ICharArrayMap<V> {
return map.values();
}
public Collection<char[]> keys() {
Set<Key> keys= map.keySet();
ArrayList<char[]> r= new ArrayList<char[]>(keys.size());
for (Key key : keys) {
r.add(CharArrayUtils.extract(key.buffer, key.start, key.length));
}
return r;
}
public void clear() {
map.clear();

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2002, 2007 IBM Corporation and others.
* Copyright (c) 2002, 2008 IBM 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
@ -57,7 +57,6 @@ import org.eclipse.core.runtime.CoreException;
public class CASTTranslationUnit extends CASTNode implements IASTTranslationUnit {
private static final IASTPreprocessorStatement[] EMPTY_PREPROCESSOR_STATEMENT_ARRAY = new IASTPreprocessorStatement[0];
private static final IASTNodeLocation[] EMPTY_PREPROCESSOR_LOCATION_ARRAY = new IASTNodeLocation[0];
private static final IASTPreprocessorMacroDefinition[] EMPTY_PREPROCESSOR_MACRODEF_ARRAY = new IASTPreprocessorMacroDefinition[0];
private static final IASTPreprocessorIncludeStatement[] EMPTY_PREPROCESSOR_INCLUSION_ARRAY = new IASTPreprocessorIncludeStatement[0];
private static final IASTProblem[] EMPTY_PROBLEM_ARRAY = new IASTProblem[0];
@ -185,18 +184,6 @@ public class CASTTranslationUnit extends CASTNode implements IASTTranslationUnit
return CVisitor.getReferences(this, binding);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.cdt.core.dom.ast.IASTTranslationUnit#getLocationInfo(int,
* int)
*/
public IASTNodeLocation[] getLocationInfo(int offset, int length) {
if (resolver == null)
return EMPTY_PREPROCESSOR_LOCATION_ARRAY;
return resolver.getLocations(offset, length);
}
private class CFindNodeForOffsetAction extends CASTVisitor {
{
shouldVisitNames = true;
@ -546,6 +533,7 @@ public class CASTTranslationUnit extends CASTNode implements IASTTranslationUnit
return new IASTComment[0];
}
@SuppressWarnings("unchecked")
public Object getAdapter(Class adapter) {
if (adapter.isAssignableFrom(resolver.getClass())) {
return resolver;

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2004, 2007 IBM Corporation and others.
* Copyright (c) 2004, 2008 IBM 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
@ -71,7 +71,6 @@ import org.eclipse.core.runtime.CoreException;
public class CPPASTTranslationUnit extends CPPASTNode implements ICPPASTTranslationUnit, IASTAmbiguityParent {
private static final IASTPreprocessorStatement[] EMPTY_PREPROCESSOR_STATEMENT_ARRAY = new IASTPreprocessorStatement[0];
private static final IASTNodeLocation[] EMPTY_PREPROCESSOR_LOCATION_ARRAY = new IASTNodeLocation[0];
private static final IASTPreprocessorMacroDefinition[] EMPTY_PREPROCESSOR_MACRODEF_ARRAY = new IASTPreprocessorMacroDefinition[0];
private static final IASTPreprocessorIncludeStatement[] EMPTY_PREPROCESSOR_INCLUSION_ARRAY = new IASTPreprocessorIncludeStatement[0];
private static final String EMPTY_STRING = ""; //$NON-NLS-1$
@ -225,13 +224,6 @@ public class CPPASTTranslationUnit extends CPPASTNode implements ICPPASTTranslat
return CPPVisitor.getReferences(this, b);
}
public IASTNodeLocation[] getLocationInfo(int offset, int length) {
if (resolver == null)
return EMPTY_PREPROCESSOR_LOCATION_ARRAY;
return resolver.getLocations(offset, length);
}
private class CPPFindNodeForOffsetAction extends CPPASTVisitor {
{
shouldVisitNames = true;
@ -512,6 +504,7 @@ public class CPPASTTranslationUnit extends CPPASTNode implements ICPPASTTranslat
return new IASTComment[0];
}
@SuppressWarnings("unchecked")
public Object getAdapter(Class adapter) {
if (adapter.isAssignableFrom(resolver.getClass())) {
return resolver;

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2007 Wind River Systems, Inc. and others.
* Copyright (c) 2007, 2008 Wind River Systems, Inc. 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
@ -124,10 +124,12 @@ class ASTMacroReferenceName extends ASTPreprocessorName {
fImageLocationInfo= imgLocationInfo;
}
@Override
public boolean isReference() {
return true;
}
@Override
public IASTImageLocation getImageLocation() {
if (fImageLocationInfo != null) {
IASTTranslationUnit tu= getTranslationUnit();
@ -140,5 +142,5 @@ class ASTMacroReferenceName extends ASTPreprocessorName {
return null;
}
return super.getImageLocation();
}
}
}

View file

@ -467,6 +467,10 @@ class ASTFileLocation implements IASTFileLocation {
public int getSequenceEndNumber() {
return fLocationCtx.getSequenceNumberForOffset(fOffset+fLength, true);
}
public LocationCtxFile getLocationContext() {
return fLocationCtx;
}
}
class ASTMacroExpansionLocation implements IASTMacroExpansion {
@ -489,6 +493,10 @@ class ASTMacroExpansionLocation implements IASTMacroExpansion {
public IASTPreprocessorMacroDefinition getMacroDefinition() {
return fContext.getMacroDefinition();
}
public IASTName getMacroReference() {
return fContext.getMacroReference();
}
public IASTFileLocation asFileLocation() {
return ((LocationCtxContainer) fContext.getParent()).createFileLocation(fContext.fOffsetInParent, fContext.fEndOffsetInParent-fContext.fOffsetInParent);

View file

@ -14,7 +14,9 @@ package org.eclipse.cdt.internal.core.parser.scanner;
import java.io.File;
import java.io.IOException;
import java.text.DateFormatSymbols;
import java.util.Calendar;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -42,18 +44,19 @@ import org.eclipse.cdt.core.parser.OffsetLimitReachedException;
import org.eclipse.cdt.core.parser.ParseError;
import org.eclipse.cdt.core.parser.ParserLanguage;
import org.eclipse.cdt.core.parser.util.CharArrayIntMap;
import org.eclipse.cdt.core.parser.util.CharArrayObjectMap;
import org.eclipse.cdt.core.parser.util.CharArrayMap;
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
import org.eclipse.cdt.internal.core.parser.scanner.ExpressionEvaluator.EvalException;
import org.eclipse.cdt.internal.core.parser.scanner.Lexer.LexerOptions;
import org.eclipse.cdt.internal.core.parser.scanner.MacroDefinitionParser.InvalidMacroDefinitionException;
import org.eclipse.core.runtime.IAdaptable;
/**
* C-Preprocessor providing tokens for the parsers. The class should not be used directly, rather than that
* you should be using the {@link IScanner} interface.
* @since 5.0
*/
public class CPreprocessor implements ILexerLog, IScanner {
public class CPreprocessor implements ILexerLog, IScanner, IAdaptable {
public static final String PROP_VALUE = "CPreprocessor"; //$NON-NLS-1$
public static final int tDEFINED= IToken.FIRST_RESERVED_PREPROCESSOR;
@ -62,7 +65,6 @@ public class CPreprocessor implements ILexerLog, IScanner {
public static final int tSPACE= IToken.FIRST_RESERVED_PREPROCESSOR+3;
public static final int tNOSPACE= IToken.FIRST_RESERVED_PREPROCESSOR+4;
public static final int tMACRO_PARAMETER= IToken.FIRST_RESERVED_PREPROCESSOR+5;
public static final int tEMPTY_TOKEN = IToken.FIRST_RESERVED_PREPROCESSOR+6;
@ -81,6 +83,7 @@ public class CPreprocessor implements ILexerLog, IScanner {
private static final ObjectStyleMacro __STDC_HOSTED__ = new ObjectStyleMacro("__STDC_HOSTED_".toCharArray(), ONE); //$NON-NLS-1$
private static final ObjectStyleMacro __STDC_VERSION__ = new ObjectStyleMacro("__STDC_VERSION_".toCharArray(), "199901L".toCharArray()); //$NON-NLS-1$ //$NON-NLS-2$
private interface IIncludeFileTester {
Object checkFile(String path, String fileName);
}
@ -120,7 +123,8 @@ public class CPreprocessor implements ILexerLog, IScanner {
public Token execute() {
StringBuffer buffer = new StringBuffer("\""); //$NON-NLS-1$
Calendar cal = Calendar.getInstance();
buffer.append(cal.get(Calendar.MONTH));
DateFormatSymbols dfs= new DateFormatSymbols();
buffer.append(dfs.getShortMonths()[cal.get(Calendar.MONTH)]);
buffer.append(" "); //$NON-NLS-1$
append(buffer, cal.get(Calendar.DAY_OF_MONTH));
buffer.append(" "); //$NON-NLS-1$
@ -140,7 +144,7 @@ public class CPreprocessor implements ILexerLog, IScanner {
public Token execute() {
StringBuffer buffer = new StringBuffer("\""); //$NON-NLS-1$
Calendar cal = Calendar.getInstance();
append(buffer, cal.get(Calendar.HOUR));
append(buffer, cal.get(Calendar.HOUR_OF_DAY));
buffer.append(":"); //$NON-NLS-1$
append(buffer, cal.get(Calendar.MINUTE));
buffer.append(":"); //$NON-NLS-1$
@ -176,7 +180,7 @@ public class CPreprocessor implements ILexerLog, IScanner {
private boolean fHandledCompletion= false;
// state information
private final CharArrayObjectMap fMacroDictionary = new CharArrayObjectMap(512);
private final CharArrayMap<PreprocessorMacro> fMacroDictionary = new CharArrayMap<PreprocessorMacro>(512);
private final LocationMap fLocationMap = new LocationMap();
/** Set of already included files */
@ -345,9 +349,9 @@ public class CPreprocessor implements ILexerLog, IScanner {
}
}
Object[] predefined= fMacroDictionary.valueArray();
for (int i = 0; i < predefined.length; i++) {
fLocationMap.registerPredefinedMacro((PreprocessorMacro) predefined[i]);
Collection<PreprocessorMacro> predefined= fMacroDictionary.values();
for (PreprocessorMacro macro : predefined) {
fLocationMap.registerPredefinedMacro(macro);
}
}
@ -413,13 +417,10 @@ public class CPreprocessor implements ILexerLog, IScanner {
}
public Map<String, IMacroBinding> getMacroDefinitions() {
final CharArrayObjectMap objMap= fMacroDictionary;
int size = objMap.size();
Map<String, IMacroBinding> hashMap = new HashMap<String, IMacroBinding>(size);
for (int i = 0; i < size; i++) {
hashMap.put(String.valueOf(objMap.keyAt(i)), (IMacroBinding) objMap.getAt(i));
}
Map<String, IMacroBinding> hashMap = new HashMap<String, IMacroBinding>(fMacroDictionary.size());
for (char[] key : fMacroDictionary.keys()) {
hashMap.put(String.valueOf(key), fMacroDictionary.get(key));
}
return hashMap;
}
@ -1165,7 +1166,7 @@ public class CPreprocessor implements ILexerLog, IScanner {
lexer.consumeLine(ORIGIN_PREPROCESSOR_DIRECTIVE);
final int endOffset= lexer.currentToken().getEndOffset();
final char[] namechars= name.getCharImage();
PreprocessorMacro definition= (PreprocessorMacro) fMacroDictionary.remove(namechars, 0, namechars.length);
PreprocessorMacro definition= fMacroDictionary.remove(namechars, 0, namechars.length);
fLocationMap.encounterPoundUndef(definition, startOffset, name.getOffset(), name.getEndOffset(), endOffset, namechars);
}
@ -1392,7 +1393,7 @@ public class CPreprocessor implements ILexerLog, IScanner {
*/
private boolean expandMacro(final Token identifier, Lexer lexer, boolean stopAtNewline) throws OffsetLimitReachedException {
final char[] name= identifier.getCharImage();
PreprocessorMacro macro= (PreprocessorMacro) fMacroDictionary.get(name);
PreprocessorMacro macro= fMacroDictionary.get(name);
if (macro == null) {
return false;
}
@ -1419,4 +1420,12 @@ public class CPreprocessor implements ILexerLog, IScanner {
fCurrentContext= new ScannerContext(ctx, fCurrentContext, replacement);
return true;
}
@SuppressWarnings("unchecked")
public Object getAdapter(Class adapter) {
if (adapter.isAssignableFrom(fMacroExpander.getClass())) {
return fMacroExpander;
}
return null;
}
}

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2004, 2007 IBM Corporation and others.
* Copyright (c) 2004, 2008 IBM 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
@ -15,7 +15,7 @@ package org.eclipse.cdt.internal.core.parser.scanner;
import org.eclipse.cdt.core.parser.IProblem;
import org.eclipse.cdt.core.parser.IToken;
import org.eclipse.cdt.core.parser.util.CharArrayObjectMap;
import org.eclipse.cdt.core.parser.util.CharArrayMap;
/**
* Used to evaluate expressions in preprocessor directives.
@ -40,11 +40,11 @@ class ExpressionEvaluator {
}
private Token fTokens;
private CharArrayObjectMap fDictionary;
private CharArrayMap<PreprocessorMacro> fDictionary;
public boolean evaluate(TokenList condition, CharArrayObjectMap dictionary) throws EvalException {
public boolean evaluate(TokenList condition, CharArrayMap<PreprocessorMacro> macroDictionary) throws EvalException {
fTokens= condition.first();
fDictionary= dictionary;
fDictionary= macroDictionary;
return expression() != 0;
}

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2004, 2007 IBM Corporation and others.
* Copyright (c) 2004, 2008 IBM 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
@ -14,6 +14,7 @@ package org.eclipse.cdt.internal.core.parser.scanner;
import org.eclipse.cdt.core.dom.ast.IASTComment;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTImageLocation;
import org.eclipse.cdt.core.dom.ast.IASTMacroExpansion;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTNodeLocation;
@ -103,9 +104,12 @@ public interface ILocationResolver {
*/
IASTFileLocation getMappedFileLocation(int offset, int length);
/**
* @see IASTTranslationUnit#getLocationInfo(int, int).
*/
/**
* Returns an array of locations. This is a sequence of file locations and macro-expansion locations.
* @param offset sequence number as stored in the ast nodes.
* @param length
* @return and array of locations.
*/
IASTNodeLocation[] getLocations(int sequenceNumber, int length);
/**
@ -122,7 +126,7 @@ public interface ILocationResolver {
int getSequenceNumberForFileOffset(String filePath, int fileOffset);
/**
* @see IASTTranslationUnit#getUnpreprocessedSignature(IASTFileLocation).
* @see IASTNode#getRawSignature().
*/
char[] getUnpreprocessedSignature(IASTFileLocation loc);
@ -144,4 +148,21 @@ public interface ILocationResolver {
* Same as {@link #getMappedFileLocation(int, int)} for the given array of consecutive node locations.
*/
IASTFileLocation flattenLocations(IASTNodeLocation[] nodeLocations);
/**
* Returns all explicit macro expansions that intersect with the given file location.
* Include files that may be included within the given location are not examined.
* @param loc the file-location to search for macro references
* @return an array of macro expansions.
* @since 5.0
*/
IASTMacroExpansion[] getMacroExpansions(IASTFileLocation loc);
/**
* Returns all implicit macro references related to an explicit one.
* @param ref an explicit macro expansion.
* @return an array of names representing implicit macro expansions.
* @since 5.0
*/
IASTName[] getImplicitMacroReferences(IASTName ref);
}

View file

@ -14,7 +14,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTNodeLocation;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit.IDependencyTree.IASTInclusionNode;
@ -103,14 +102,14 @@ abstract class LocationCtx implements ILocationCtx {
* Returns the minimal file location containing the specified sequence number range, assuming
* that it is contained in this context.
*/
public IASTFileLocation findMappedFileLocation(int sequenceNumber, int length) {
public ASTFileLocation findMappedFileLocation(int sequenceNumber, int length) {
return fParent.createMappedFileLocation(fOffsetInParent, fEndOffsetInParent-fOffsetInParent);
}
/**
* Returns the file location containing the specified offset range in this context.
*/
public IASTFileLocation createMappedFileLocation(int offset, int length) {
public ASTFileLocation createMappedFileLocation(int offset, int length) {
return fParent.createMappedFileLocation(fOffsetInParent, fEndOffsetInParent-fOffsetInParent);
}

View file

@ -40,6 +40,7 @@ class LocationCtxContainer extends LocationCtx {
fSource= source;
}
@Override
public Collection<LocationCtx> getChildren() {
if (fChildren == null) {
return Collections.emptyList();
@ -84,10 +85,12 @@ class LocationCtxContainer extends LocationCtx {
return result;
}
@Override
public void addChildSequenceLength(int childLength) {
fChildSequenceLength+= childLength;
}
@Override
public final LocationCtx findSurroundingContext(int sequenceNumber, int length) {
int testEnd= length > 1 ? sequenceNumber+length-1 : sequenceNumber;
final LocationCtx child= findChildLessOrEqualThan(sequenceNumber, false);
@ -97,6 +100,7 @@ class LocationCtxContainer extends LocationCtx {
return this;
}
@Override
public final LocationCtxMacroExpansion findSurroundingMacroExpansion(int sequenceNumber, int length) {
int testEnd= length > 1 ? sequenceNumber+length-1 : sequenceNumber;
final LocationCtx child= findChildLessOrEqualThan(sequenceNumber, true);
@ -106,7 +110,8 @@ class LocationCtxContainer extends LocationCtx {
return null;
}
public IASTFileLocation findMappedFileLocation(int sequenceNumber, int length) {
@Override
public ASTFileLocation findMappedFileLocation(int sequenceNumber, int length) {
// try to delegate to a child.
int testEnd= length > 1 ? sequenceNumber+length-1 : sequenceNumber;
final LocationCtx child= findChildLessOrEqualThan(sequenceNumber, false);
@ -198,6 +203,7 @@ class LocationCtxContainer extends LocationCtx {
return idx >= 0 ? fChildren.get(idx) : null;
}
@Override
public void getInclusions(ArrayList<IASTInclusionNode> result) {
if (fChildren != null) {
for (Iterator<LocationCtx> iterator = fChildren.iterator(); iterator.hasNext();) {
@ -212,6 +218,7 @@ class LocationCtxContainer extends LocationCtx {
}
}
@Override
public int getLineNumber(int offset) {
if (fLineOffsets == null) {
fLineOffsets= computeLineOffsets();

View file

@ -10,7 +10,10 @@
*******************************************************************************/
package org.eclipse.cdt.internal.core.parser.scanner;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import java.util.ArrayList;
import java.util.Collection;
import org.eclipse.cdt.core.dom.ast.IASTMacroExpansion;
/**
* A location context representing a file.
@ -26,15 +29,18 @@ class LocationCtxFile extends LocationCtxContainer {
fASTInclude= inclusionStatement;
}
@Override
public final void addChildSequenceLength(int childLength) {
super.addChildSequenceLength(childLength);
}
@Override
public final String getFilePath() {
return fFilename;
}
public IASTFileLocation findMappedFileLocation(int sequenceNumber, int length) {
@Override
public ASTFileLocation findMappedFileLocation(int sequenceNumber, int length) {
// try to delegate to a child.
final int testEnd= length > 1 ? sequenceNumber+length-1 : sequenceNumber;
final int sequenceEnd= sequenceNumber+length;
@ -76,14 +82,17 @@ class LocationCtxFile extends LocationCtxContainer {
return new ASTFileLocation(this, startOffset, endOffset-startOffset);
}
public IASTFileLocation createMappedFileLocation(int offset, int length) {
@Override
public ASTFileLocation createMappedFileLocation(int offset, int length) {
return new ASTFileLocation(this, offset, length);
}
@Override
public ASTInclusionStatement getInclusionStatement() {
return fASTInclude;
}
@Override
ASTFileLocation createFileLocation(int start, int length) {
return new ASTFileLocation(this, start, length);
}
@ -95,4 +104,20 @@ class LocationCtxFile extends LocationCtxContainer {
}
return sequenceNumber >= child.fSequenceNumber + child.getSequenceLength();
}
public void collectExplicitMacroExpansions(int offset, int length, ArrayList<IASTMacroExpansion> result) {
Collection<LocationCtx> children= getChildren();
for (LocationCtx ctx : children) {
// context must start before the end of the search range
if (ctx.fOffsetInParent >= offset+length) {
break;
}
if (ctx instanceof LocationCtxMacroExpansion) {
// expansion must end after the search start
if (ctx.fEndOffsetInParent > offset) {
result.add(new ASTMacroExpansionLocation(((LocationCtxMacroExpansion) ctx), 0, ctx.getSequenceLength()));
}
}
}
}
}

View file

@ -19,6 +19,7 @@ import java.util.List;
import org.eclipse.cdt.core.dom.ast.IASTComment;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTImageLocation;
import org.eclipse.cdt.core.dom.ast.IASTMacroExpansion;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTNodeLocation;
@ -48,7 +49,7 @@ public class LocationMap implements ILocationResolver {
private ArrayList<ASTProblem> fProblems= new ArrayList<ASTProblem>();
private ArrayList<ASTComment> fComments= new ArrayList<ASTComment>();
private ArrayList<ASTObjectStyleMacroDefinition> fBuiltinMacros= new ArrayList<ASTObjectStyleMacroDefinition>();
private IdentityHashMap<IMacroBinding, List<IASTName>> fMacroReferences= new IdentityHashMap<IMacroBinding, List<IASTName>>();
private ArrayList<IASTName> fMacroReferences= new ArrayList<IASTName>();
private LocationCtxFile fRootContext= null;
private LocationCtx fCurrentContext= null;
@ -157,27 +158,22 @@ public class LocationMap implements ILocationResolver {
int endNumber= getSequenceNumberForOffset(endOffset);
final int length= endNumber-nameNumber;
ASTMacroReferenceName expansion= new ASTMacroReferenceName(fTranslationUnit, nameNumber, nameEndNumber, macro, null);
for (int i = 0; i < implicitMacroReferences.length; i++) {
ASTMacroReferenceName name = (ASTMacroReferenceName) implicitMacroReferences[i];
name.setOffsetAndLength(nameNumber, length);
addMacroReference((IMacroBinding) name.getBinding(), name);
name.setParent(expansion);
addMacroReference(name);
}
ASTMacroReferenceName expansion= new ASTMacroReferenceName(fTranslationUnit, nameNumber, nameEndNumber, macro, null);
addMacroReference(macro, expansion);
addMacroReference(expansion);
fCurrentContext= new LocationCtxMacroExpansion(this, (LocationCtxContainer) fCurrentContext, nameOffset, endOffset, endNumber, contextLength, imageLocations, expansion);
fLastChildInsertionOffset= 0;
return fCurrentContext;
}
private void addMacroReference(IMacroBinding macro, IASTName name) {
List<IASTName> list= fMacroReferences.get(macro);
if (list == null) {
list= new ArrayList<IASTName>();
fMacroReferences.put(macro, list);
}
list.add(name);
private void addMacroReference(IASTName name) {
fMacroReferences.add(name);
}
/**
@ -310,7 +306,7 @@ public class LocationMap implements ILocationResolver {
// not using endOffset, compatible with 4.0: endOffset= getSequenceNumberForOffset(endOffset);
final ASTUndef undef = new ASTUndef(fTranslationUnit, name, startOffset, nameOffset, nameEndOffset, definition);
fDirectives.add(undef);
addMacroReference(definition, undef.getMacroName());
addMacroReference(undef.getMacroName());
}
public void setRootNode(IASTTranslationUnit root) {
@ -351,15 +347,54 @@ public class LocationMap implements ILocationResolver {
return new String(ctx.getFilePath());
}
public IASTFileLocation getMappedFileLocation(int sequenceNumber, int length) {
public ASTFileLocation getMappedFileLocation(int sequenceNumber, int length) {
return fRootContext.findMappedFileLocation(sequenceNumber, length);
}
public char[] getUnpreprocessedSignature(IASTFileLocation loc) {
if (loc instanceof ASTFileLocation) {
return ((ASTFileLocation) loc).getSource();
}
return CharArrayUtils.EMPTY;
ASTFileLocation floc= convertFileLocation(loc);
if (floc == null) {
return CharArrayUtils.EMPTY;
}
return floc.getSource();
}
public IASTMacroExpansion[] getMacroExpansions(IASTFileLocation loc) {
ASTFileLocation floc= convertFileLocation(loc);
if (floc == null) {
return new IASTMacroExpansion[0];
}
LocationCtxFile ctx= floc.getLocationContext();
ArrayList<IASTMacroExpansion> list= new ArrayList<IASTMacroExpansion>();
ctx.collectExplicitMacroExpansions(floc.getNodeOffset(), floc.getNodeLength(), list);
return list.toArray(new IASTMacroExpansion[list.size()]);
}
private ASTFileLocation convertFileLocation(IASTFileLocation loc) {
if (loc == null) {
return null;
}
if (loc instanceof ASTFileLocation) {
return (ASTFileLocation) loc;
}
final String fileName = loc.getFileName();
final int nodeOffset = loc.getNodeOffset();
final int nodeLength = loc.getNodeLength();
int sequenceNumber= getSequenceNumberForFileOffset(fileName, nodeOffset);
if (sequenceNumber < 0) {
return null;
}
int length= 0;
if (nodeLength > 0) {
length= getSequenceNumberForFileOffset(fileName, nodeOffset + nodeLength-1)+1 - sequenceNumber;
if (length < 0) {
return null;
}
}
return getMappedFileLocation(sequenceNumber, length);
}
public IASTNodeLocation[] getLocations(int sequenceNumber, int length) {
@ -538,13 +573,25 @@ public class LocationMap implements ILocationResolver {
}
public IASTName[] getReferences(IMacroBinding binding) {
List<IASTName> list= fMacroReferences.get(binding);
if (list == null) {
return EMPTY_NAMES;
List<IASTName> result= new ArrayList<IASTName>();
for (IASTName name : fMacroReferences) {
if (name.getBinding() == binding) {
result.add(name);
}
}
return list.toArray(new IASTName[list.size()]);
return result.toArray(new IASTName[result.size()]);
}
public IASTName[] getImplicitMacroReferences(IASTName ref) {
List<IASTName> result= new ArrayList<IASTName>();
for (IASTName name : fMacroReferences) {
if (name.getParent() == ref) {
result.add(name);
}
}
return result.toArray(new IASTName[result.size()]);
}
public IDependencyTree getDependencyTree() {
return new DependencyTree(fRootContext);
}

View file

@ -11,13 +11,14 @@
package org.eclipse.cdt.internal.core.parser.scanner;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.IdentityHashMap;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.parser.IProblem;
import org.eclipse.cdt.core.parser.IToken;
import org.eclipse.cdt.core.parser.OffsetLimitReachedException;
import org.eclipse.cdt.core.parser.util.CharArrayObjectMap;
import org.eclipse.cdt.core.parser.util.CharArrayMap;
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
import org.eclipse.cdt.internal.core.parser.scanner.ImageLocationInfo.MacroImageLocationInfo;
import org.eclipse.cdt.internal.core.parser.scanner.ImageLocationInfo.ParameterImageLocationInfo;
@ -31,7 +32,8 @@ import org.eclipse.cdt.internal.core.parser.scanner.MacroDefinitionParser.TokenP
public class MacroExpander {
private static final int ORIGIN = OffsetLimitReachedException.ORIGIN_MACRO_EXPANSION;
private static final Token END_TOKEN = new Token(IToken.tEND_OF_INPUT, null, 0, 0);
private static final TokenList EMPTY_TOKEN_LIST = new TokenList();
/**
* Marks the beginning and the end of the scope of a macro expansion. Necessary to properly
* handle recursive expansions and to figure out whether spaces are required during a stringify
@ -125,7 +127,7 @@ public class MacroExpander {
private final ILexerLog fLog;
private final MacroDefinitionParser fDefinitionParser;
private final CharArrayObjectMap fDictionary;
private final CharArrayMap<PreprocessorMacro> fDictionary;
private final LocationMap fLocationMap;
private final LexerOptions fLexOptions;
private ArrayList<IASTName> fImplicitMacroExpansions= new ArrayList<IASTName>();
@ -133,9 +135,9 @@ public class MacroExpander {
private boolean fCompletionMode;
private int fStartOffset;
private int fEndOffset;
public MacroExpander(ILexerLog log, CharArrayObjectMap dict, LocationMap locationMap, LexerOptions lexOptions) {
fDictionary= dict;
public MacroExpander(ILexerLog log, CharArrayMap<PreprocessorMacro> macroDictionary, LocationMap locationMap, LexerOptions lexOptions) {
fDictionary= macroDictionary;
fLocationMap= locationMap;
fDefinitionParser= new MacroDefinitionParser();
fLexOptions= lexOptions;
@ -158,45 +160,144 @@ public class MacroExpander {
// setup input sequence
TokenSource input= new TokenSource(lexer, stopAtNewline);
TokenList firstExpansion= new TokenList();
expandOne(identifier, macro, forbidden, input, firstExpansion);
expandOne(identifier, macro, forbidden, input, firstExpansion, null);
input.prepend(firstExpansion);
TokenList result= expandAll(input, forbidden);
TokenList result= expandAll(input, forbidden, null);
postProcessTokens(result);
return result;
}
/**
* Method for tracking macro expansions.
* @since 5.0
*/
public void expand(String beforeExpansion, MacroExpansionTracker tracker) {
Lexer lexer= new Lexer(beforeExpansion.toCharArray(), fLexOptions, fLog, this);
fImplicitMacroExpansions.clear();
fImageLocationInfos.clear();
try {
tracker.start(lexer);
Token identifier= lexer.nextToken();
if (identifier.getType() != IToken.tIDENTIFIER) {
tracker.fail();
return;
}
PreprocessorMacro macro= fDictionary.get(identifier.getCharImage());
if (macro == null) {
tracker.fail();
return;
}
lexer.nextToken();
fStartOffset= identifier.getOffset();
fEndOffset= identifier.getEndOffset();
fCompletionMode= false;
IdentityHashMap<PreprocessorMacro, PreprocessorMacro> forbidden= new IdentityHashMap<PreprocessorMacro, PreprocessorMacro>();
// setup input sequence
TokenSource input= new TokenSource(lexer, false);
TokenList firstExpansion= new TokenList();
expandOne(identifier, macro, forbidden, input, firstExpansion, tracker);
input.prepend(firstExpansion);
TokenList result= expandAll(input, forbidden, tracker);
tracker.finish(result, fEndOffset);
} catch (OffsetLimitReachedException e) {
}
}
/**
* Expects that the identifier of the macro expansion has been consumed. Expands the macro consuming
* tokens from the input (to read the parameters) and stores the resulting tokens together
* with boundary markers in the result token list.
* Returns the last token of the expansion.
*/
private Token expandOne(Token lastConsumed, PreprocessorMacro macro, IdentityHashMap<PreprocessorMacro, PreprocessorMacro> forbidden, TokenSource input, TokenList result)
private Token expandOne(Token lastConsumed, PreprocessorMacro macro,
IdentityHashMap<PreprocessorMacro, PreprocessorMacro> forbidden, TokenSource input, TokenList result,
MacroExpansionTracker tracker)
throws OffsetLimitReachedException {
result.append(new ExpansionBoundary(macro, true));
if (macro.isFunctionStyle()) {
final TokenSource[] argInputs= new TokenSource[macro.getParameterPlaceholderList().length];
lastConsumed= parseArguments(input, (FunctionStyleMacro) macro, forbidden, argInputs);
TokenList[] clonedArgs= new TokenList[argInputs.length];
TokenList[] expandedArgs= new TokenList[argInputs.length];
for (int i = 0; i < argInputs.length; i++) {
final TokenSource argInput = argInputs[i];
clonedArgs[i]= argInput.cloneTokens();
final TokenList expandedArg= expandAll(argInput, forbidden);
expandedArgs[i]= expandedArg;
final int paramCount = macro.getParameterPlaceholderList().length;
final TokenSource[] argInputs= new TokenSource[paramCount];
final BitSet paramUsage= getParamUsage(macro);
if (tracker != null) {
tracker.startFunctionStyleMacro((Token) lastConsumed.clone());
}
lastConsumed= parseArguments(input, (FunctionStyleMacro) macro, forbidden, argInputs, tracker);
TokenList[] clonedArgs= new TokenList[paramCount];
TokenList[] expandedArgs= new TokenList[paramCount];
for (int i = 0; i < paramCount; i++) {
final TokenSource argInput = argInputs[i];
final boolean needCopy= paramUsage.get(2*i);
final boolean needExpansion = paramUsage.get(2*i+1);
clonedArgs[i]= needCopy ? argInput.cloneTokens() : EMPTY_TOKEN_LIST;
expandedArgs[i]= needExpansion ? expandAll(argInput, forbidden, tracker) : EMPTY_TOKEN_LIST;
if (!needExpansion) {
executeScopeMarkers(argInput, forbidden);
}
if (tracker != null) {
tracker.setExpandedMacroArgument(needExpansion ? expandedArgs[i] : null);
// make sure that the trailing arguments do not get expanded.
if (tracker.isDone()) {
paramUsage.clear();
}
}
}
if (tracker == null) {
replaceArgs(macro, clonedArgs, expandedArgs, result);
}
else {
if (tracker.isRequestedStep()) {
TokenList replacement= new TokenList();
replaceArgs(macro, clonedArgs, expandedArgs, replacement);
tracker.storeFunctionStyleMacroReplacement(macro, replacement, result);
}
else if (tracker.isDone()) {
tracker.appendFunctionStyleMacro(result);
}
else {
replaceArgs(macro, clonedArgs, expandedArgs, result);
}
tracker.endFunctionStyleMacro();
}
replaceArgs(macro, clonedArgs, expandedArgs, result);
}
else {
objStyleTokenPaste(macro, result);
if (tracker == null) {
objStyleTokenPaste(macro, result);
}
else {
if (tracker.isRequestedStep()) {
TokenList replacement= new TokenList();
objStyleTokenPaste(macro, replacement);
tracker.storeObjectStyleMacroReplacement(macro, lastConsumed, replacement, result);
}
else {
objStyleTokenPaste(macro, result);
}
tracker.endObjectStyleMacro();
}
}
result.append(new ExpansionBoundary(macro, false));
return lastConsumed;
}
private TokenList expandAll(TokenSource input, IdentityHashMap<PreprocessorMacro, PreprocessorMacro> forbidden) throws OffsetLimitReachedException {
private void executeScopeMarkers(TokenSource input, IdentityHashMap<PreprocessorMacro, PreprocessorMacro> forbidden) {
Token t= input.removeFirst();
while(t != null) {
if (t.getType() == CPreprocessor.tSCOPE_MARKER) {
((ExpansionBoundary) t).execute(forbidden);
}
t= input.removeFirst();
}
}
private TokenList expandAll(TokenSource input, IdentityHashMap<PreprocessorMacro, PreprocessorMacro> forbidden,
MacroExpansionTracker tracker) throws OffsetLimitReachedException {
final TokenList result= new TokenList();
Token l= null;
Token t= input.removeFirst();
@ -207,9 +308,12 @@ public class MacroExpander {
t= input.removeFirst(); // don't change l
continue;
case IToken.tIDENTIFIER:
PreprocessorMacro macro= (PreprocessorMacro) fDictionary.get(t.getCharImage());
PreprocessorMacro macro= fDictionary.get(t.getCharImage());
if (tracker != null && tracker.isDone()) {
result.append(t);
}
// tricky: don't mark function-style macros if you don't find the left parenthesis
if (macro == null || (macro.isFunctionStyle() && !input.findLParenthesis())) {
else if (macro == null || (macro.isFunctionStyle() && !input.findLParenthesis())) {
result.append(t);
}
else if (forbidden.containsKey(macro)) {
@ -227,7 +331,7 @@ public class MacroExpander {
TokenList replacement= new TokenList();
addSpacemarker(l, t, replacement); // start expansion
Token last= expandOne(t, macro, forbidden, input, replacement);
Token last= expandOne(t, macro, forbidden, input, replacement, tracker);
addSpacemarker(last, input.first(), replacement); // end expansion
input.prepend(replacement);
@ -262,12 +366,12 @@ public class MacroExpander {
if (l != null && t != null) {
final Object s1= l.fSource;
final Object s2= t.fSource;
if (s1 == s2 && s1 != null) {
if (s1 == s2 && s1 != null && l.getType() != CPreprocessor.tSPACE) {
if (l.getEndOffset() == t.getOffset()) {
target.append(new Token(CPreprocessor.tNOSPACE, null, 0, 0));
}
else {
target.append(new Token(CPreprocessor.tSPACE, null, 0, 0));
target.append(new Token(CPreprocessor.tSPACE, s1, l.getEndOffset(), t.getOffset()));
}
}
}
@ -276,9 +380,11 @@ public class MacroExpander {
/**
* Expects that the identifier has been consumed.
* @param forbidden
* @param tracker
* @throws OffsetLimitReachedException
*/
private Token parseArguments(TokenSource input, FunctionStyleMacro macro, IdentityHashMap<PreprocessorMacro, PreprocessorMacro> forbidden, TokenSource[] result) throws OffsetLimitReachedException {
private Token parseArguments(TokenSource input, FunctionStyleMacro macro, IdentityHashMap<PreprocessorMacro, PreprocessorMacro> forbidden, TokenSource[] result,
MacroExpansionTracker tracker) throws OffsetLimitReachedException {
final int argCount= macro.getParameterPlaceholderList().length;
final boolean hasVarargs= macro.hasVarArgs() != FunctionStyleMacro.NO_VAARGS;
final int requiredArgs= hasVarargs ? argCount-1 : argCount;
@ -296,6 +402,17 @@ public class MacroExpander {
Token t= input.fetchFirst();
if (t == null) {
break loop;
}
if (tracker != null) {
switch(t.getType()) {
case IToken.tEND_OF_INPUT:
case IToken.tCOMPLETION:
case CPreprocessor.tSCOPE_MARKER:
break;
default:
tracker.addFunctionStyleMacroExpansionToken((Token) t.clone());
break;
}
}
lastToken= t;
switch(t.getType()) {
@ -414,7 +531,7 @@ public class MacroExpander {
case IToken.tPOUND:
addSpacemarker(l, t, result); // start stringify
StringBuffer buf= new StringBuffer();
StringBuilder buf= new StringBuilder();
buf.append('"');
if (n != null && n.getType() == CPreprocessor.tMACRO_PARAMETER) {
idx= ((TokenParameterReference) n).getIndex();
@ -441,65 +558,65 @@ public class MacroExpander {
break;
case IToken.tPOUNDPOUND:
if (pasteArg1 != null) {
Token pasteArg2= null;
TokenList rest= null;
if (n != null) {
if (n.getType() == CPreprocessor.tMACRO_PARAMETER) {
idx= ((TokenParameterReference) n).getIndex();
if (idx < args.length) { // be defensive
TokenList arg= clone(args[idx]);
pasteArg2= arg.first();
// gcc-extension
if (idx == args.length-1 && macro.hasVarArgs() != FunctionStyleMacro.NO_VAARGS) {
if (pasteArg1.getType() == IToken.tCOMMA) { // no paste operation
if (arg.first() != null) {
result.append(pasteArg1);
rest= arg;
}
pasteArg1= pasteArg2= null;
}
Token pasteArg2= null;
TokenList rest= null;
if (n != null) {
if (n.getType() == CPreprocessor.tMACRO_PARAMETER) {
TokenList arg;
idx= ((TokenParameterReference) n).getIndex();
if (idx < args.length) { // be defensive
// gcc-extension
if (idx == args.length-1 && macro.hasVarArgs() != FunctionStyleMacro.NO_VAARGS &&
pasteArg1 != null && pasteArg1.getType() == IToken.tCOMMA) { // no paste operation
arg= clone(expandedArgs[idx]);
if (arg.first() != null) {
result.append(pasteArg1);
rest= arg;
}
pasteArg1= pasteArg2= null;
}
else {
arg= clone(args[idx]);
pasteArg2= arg.first();
if (pasteArg2 != null && arg.first() != arg.last()) {
rest= arg;
rest.removeFirst();
}
}
}
else {
idx= -1;
pasteArg2= n;
}
t= n;
n= (Token) n.getNext();
pasteNext= n != null && n.getType() == IToken.tPOUNDPOUND;
generated= tokenpaste(pasteArg1, pasteArg2, macro);
pasteArg1= null;
}
else {
idx= -1;
pasteArg2= n;
}
t= n;
n= (Token) n.getNext();
pasteNext= n != null && n.getType() == IToken.tPOUNDPOUND;
if (generated != null) {
if (pasteNext && rest == null) {
pasteArg1= generated; // no need to mark spaces, done ahead
}
else {
result.append(generated);
addSpacemarker(pasteArg2, rest == null ? n : rest.first(), result); // end token paste
generated= tokenpaste(pasteArg1, pasteArg2, macro);
pasteArg1= null;
if (generated != null) {
if (pasteNext && rest == null) {
pasteArg1= generated; // no need to mark spaces, done ahead
}
else {
result.append(generated);
addSpacemarker(pasteArg2, rest == null ? n : rest.first(), result); // end token paste
}
}
if (rest != null) {
if (pasteNext) {
pasteArg1= rest.last();
if (pasteArg1 != null) {
result.appendAllButLast(rest);
addSpacemarker(result.last(), pasteArg1, result); // start token paste
}
}
if (rest != null) {
if (pasteNext) {
pasteArg1= rest.last();
if (pasteArg1 != null) {
result.appendAllButLast(rest);
addSpacemarker(result.last(), pasteArg1, result); // start token paste
}
}
else {
result.appendAll(rest);
if (idx >= 0) {
addSpacemarker(t, n, result); // end argument replacement
}
else {
result.appendAll(rest);
if (idx >= 0) {
addSpacemarker(t, n, result); // end argument replacement
}
}
}
@ -519,6 +636,53 @@ public class MacroExpander {
}
}
private BitSet getParamUsage(PreprocessorMacro macro) {
final BitSet result= new BitSet();
final TokenList replacement= macro.getTokens(fDefinitionParser, fLexOptions);
Token l= null;
Token n;
for (Token t= replacement.first(); t != null; l=t, t=n) {
n= (Token) t.getNext();
switch(t.getType()) {
case CPreprocessor.tMACRO_PARAMETER:
int idx= 2*((TokenParameterReference) t).getIndex();
if (n == null || n.getType() != IToken.tPOUNDPOUND) {
idx++;
}
result.set(idx);
break;
case IToken.tPOUND:
if (n != null && n.getType() == CPreprocessor.tMACRO_PARAMETER) {
idx= ((TokenParameterReference) n).getIndex();
result.set(2*idx);
t= n; n= (Token) n.getNext();
}
break;
case IToken.tPOUNDPOUND:
if (n != null) {
if (n.getType() == CPreprocessor.tMACRO_PARAMETER) {
idx= ((TokenParameterReference) n).getIndex();
// gcc-extension
if (l != null && l.getType() == IToken.tCOMMA && macro.hasVarArgs() != FunctionStyleMacro.NO_VAARGS
&& idx == macro.getParameterPlaceholderList().length-1) {
result.set(2*idx+1);
}
else {
result.set(2*idx);
}
}
t= n; n= (Token) n.getNext();
}
break;
}
}
return result;
}
private void objStyleTokenPaste(PreprocessorMacro macro, TokenList result) {
TokenList replacement= clone(macro.getTokens(fDefinitionParser, fLexOptions));
@ -574,6 +738,9 @@ public class MacroExpander {
}
private Token tokenpaste(Token arg1, Token arg2, PreprocessorMacro macro) {
if (arg1 == null) {
return arg2;
}
if (arg2 == null) {
return arg1;
}
@ -598,7 +765,7 @@ public class MacroExpander {
return null;
}
private void stringify(TokenList tokenList, StringBuffer buf) {
private void stringify(TokenList tokenList, StringBuilder buf) {
Token t= tokenList.first();
if (t == null) {
return;
@ -608,8 +775,7 @@ public class MacroExpander {
boolean space= false;
for (; t != null; l=t, t=n) {
n= (Token) t.getNext();
if (!space && l != null && l.fSource != null && l.fSource == t.fSource &&
l.getEndOffset() != t.getOffset()) {
if (!space && hasImplicitSpace(l, t)) {
buf.append(' ');
space= true;
}
@ -695,4 +861,10 @@ public class MacroExpander {
l= t;
}
}
static boolean hasImplicitSpace(Token l, Token t) {
return l != null &&
l.fSource != null && l.fSource == t.fSource &&
l.getEndOffset() != t.getOffset() && t.getType() != CPreprocessor.tSPACE;
}
}

View file

@ -0,0 +1,54 @@
/*******************************************************************************
* Copyright (c) 2008 Wind River Systems, Inc. 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:
* Markus Schorn - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.core.parser.scanner;
import org.eclipse.cdt.core.dom.ast.IMacroBinding;
import org.eclipse.cdt.core.dom.rewrite.MacroExpansionExplorer.IMacroExpansionStep;
import org.eclipse.text.edits.ReplaceEdit;
/**
* Implementation for {@link IMacroExpansionStep}.
*/
public class MacroExpansionStep implements IMacroExpansionStep {
private final String fBefore;
private final IMacroBinding fMacroDefinition;
private final ReplaceEdit[] fReplacements;
public MacroExpansionStep(String before, IMacroBinding def, ReplaceEdit[] replacements) {
fBefore= before;
fReplacements= replacements;
fMacroDefinition= def;
}
public String getCodeBeforeStep() {
return fBefore;
}
public String getCodeAfterStep() {
StringBuilder result= new StringBuilder();
int offset= 0;
for (int i = 0; i < fReplacements.length; i++) {
ReplaceEdit r= fReplacements[i];
result.append(fBefore, offset, r.getOffset());
result.append(r.getText());
offset= r.getExclusiveEnd();
}
result.append(fBefore, offset, fBefore.length());
return result.toString();
}
public IMacroBinding getExpandedMacro() {
return fMacroDefinition;
}
public ReplaceEdit[] getReplacements() {
return fReplacements;
}
}

View file

@ -0,0 +1,348 @@
/*******************************************************************************
* Copyright (c) 2007 Wind River Systems, Inc. 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:
* Markus Schorn - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.core.parser.scanner;
import java.util.ArrayList;
import java.util.LinkedList;
import org.eclipse.cdt.core.dom.ast.IMacroBinding;
import org.eclipse.cdt.core.parser.IToken;
import org.eclipse.text.edits.ReplaceEdit;
/**
* Collects information while macro expansion is performed.
*/
public class MacroExpansionTracker {
public class MacroInfo {
private TokenList fMacroCall= new TokenList();
private ArrayList<TokenList> fArguments= new ArrayList<TokenList>();
public MacroInfo(Token identifier) {
fMacroCall.append(identifier);
}
public void setArgument(TokenList tokenList) {
fArguments.add(tokenList);
}
}
private final int fStepToTrack;
private int fStepCount;
private String fPreStep;
private ReplaceEdit fReplacement;
private IMacroBinding fMacroDefinition;
private Lexer fLexer;
private String fReplacementText= ""; //$NON-NLS-1$
private LinkedList<MacroInfo> fMacroStack= new LinkedList<MacroInfo>();
private IToken fReplaceFrom;
private IToken fReplaceTo;
public MacroExpansionTracker(int stepToTrack) {
fStepToTrack= stepToTrack;
}
/**
* Returns whether the requested step has already been encountered.
*/
public boolean isDone() {
return fStepCount > fStepToTrack;
}
/**
* Returns whether we are currently looking at the requested step.
*/
public boolean isRequestedStep() {
return fStepCount == fStepToTrack;
}
/**
* Returns the total amount of steps encountered so far.
*/
public int getStepCount() {
return fStepCount;
}
/**
* Returns the code as it looks like just before performing the step that was tracked.
*/
public String getCodeBeforeStep() {
return fPreStep;
}
/**
* Returns the replacement that represents the change by the step that was tracked.
*/
public ReplaceEdit getReplacement() {
return fReplacement;
}
/**
* Returns the macro that is expanded in the step that was tracked.
*/
public IMacroBinding getExpandedMacro() {
return fMacroDefinition;
}
/**
* Informs the tracker that macro expansion is started.
*/
void start(Lexer lexer) {
fLexer= lexer;
}
/**
* Informs the tracker that the expansion is done.
* @param result the list of tokens after performing partial expansion up to the step that was
* tracked.
* @param endOffset the end offset of the input that was read from the lexer.
*/
void finish(TokenList result, int endOffset) {
final char[] lexInput = fLexer.getInput();
if (!isDone()) {
// special case we compute the entire expansion as one step, the result contains the
// expanded text
StringBuilder replacementText= new StringBuilder();
toString(result, lexInput, replacementText, replacementText, replacementText);
fPreStep= new String(lexInput);
fReplacement= new ReplaceEdit(0, endOffset, replacementText.toString());
}
else {
// the regular case the result contains the text before the step
StringBuilder before= new StringBuilder();
StringBuilder replace= new StringBuilder();
StringBuilder after= new StringBuilder();
toString(result, lexInput, before, replace, after);
int offset= before.length();
before.append(replace).append(after);
before.append(lexInput, endOffset, lexInput.length-endOffset);
fPreStep= before.toString();
fReplacement= new ReplaceEdit(offset, replace.length(), fReplacementText);
}
}
/**
* There was no macro at the beginning of the input.
*/
void fail() {
fPreStep= new String(fLexer.getInput());
fReplacement= new ReplaceEdit(0, 0, ""); //$NON-NLS-1$
}
private void toString(TokenList tokenList, char[] rootInput, StringBuilder before, StringBuilder replace, StringBuilder after) {
StringBuilder buf= before;
Token t= tokenList.first();
if (t == null) {
return ;
}
Token l= null;
Token n;
for (; t != null; l=t, t=n) {
n= (Token) t.getNext();
if (MacroExpander.hasImplicitSpace(l, t)) {
char[] input= getInputForSource(l.fSource, rootInput);
if (input == null) {
buf.append(' ');
}
else {
final int from = l.getEndOffset();
final int to = t.getOffset();
buf.append(input, from, to-from);
}
}
if (t == fReplaceFrom) {
buf= replace;
}
char[] input= getInputForSource(t.fSource, rootInput);
if (input == null) {
buf.append(t.getCharImage());
}
else {
buf.append(input, t.getOffset(), t.getLength());
}
if (t == fReplaceTo) {
buf= after;
}
}
}
private char[] getInputForSource(Object source, char[] rootInput) {
if (source instanceof MacroExpander) {
return rootInput;
}
if (source instanceof PreprocessorMacro) {
return ((PreprocessorMacro) source).getExpansionImage();
}
return null;
}
/**
* Informs the tracker that a function-style expansion is started.
* @param identifier the identifier token for the macro expansion.
*/
public void startFunctionStyleMacro(Token identifier) {
fMacroStack.add(new MacroInfo(identifier));
}
/**
* All tokens defining a function-style macro expansion are reported.
*/
public void addFunctionStyleMacroExpansionToken(Token t) {
fMacroStack.getLast().fMacroCall.append(t);
}
/**
* The expanded arguments for the function-style macro expansion are reported.
* @param tokenList the expanded argument, or <code>null</code> if it should not
* be expanded.
*/
public void setExpandedMacroArgument(TokenList tokenList) {
fMacroStack.getLast().setArgument(tokenList);
}
/**
* Called for the requested step.
* @param macro the macro expanded in the requested step.
* @param replacement the replacement for the expansion.
* @param result a list to store the macro call with the arguments substituted in.
*/
public void storeFunctionStyleMacroReplacement(PreprocessorMacro macro, TokenList replacement, TokenList result) {
MacroInfo minfo= fMacroStack.getLast();
fMacroDefinition= macro;
fReplaceFrom= minfo.fMacroCall.first();
appendFunctionStyleMacro(result);
fReplaceTo= result.last();
StringBuilder buf= new StringBuilder();
toString(replacement, fLexer.getInput(), buf, buf, buf);
fReplacementText= buf.toString();
}
/**
* Append the current function-style macro with the arguments substituted.
*/
public void appendFunctionStyleMacro(TokenList result) {
MacroInfo minfo= fMacroStack.getLast();
boolean active= true;
int nesting= -1;
int pcount= 0;
Token n;
Token l= null;
for (Token t = minfo.fMacroCall.first(); t != null; l=t, t=n) {
n = (Token) t.getNext();
switch (t.getType()) {
case IToken.tLPAREN:
if (active) {
result.append(t);
}
// the first one sets nesting to zero.
++nesting;
if (nesting == 0) {
if (pcount < minfo.fArguments.size()) {
TokenList p = minfo.fArguments.get(pcount);
if (p != null) {
active = false;
addSpacemarker(t, n, result);
result.appendAll(p);
}
}
}
break;
case IToken.tRPAREN:
if (!active && nesting == 0) {
addSpacemarker(l, t, result);
active= true;
}
if (active) {
result.append(t);
}
if (nesting > 0) {
nesting--;
}
break;
case IToken.tCOMMA:
if (nesting == 0) {
if (++pcount < minfo.fArguments.size()) {
if (!active) {
addSpacemarker(l, t, result);
}
result.append(t);
TokenList p = minfo.fArguments.get(pcount);
active = p == null;
if (!active) {
addSpacemarker(t, n, result);
result.appendAll(p);
}
}
} else if (active) {
result.append(t);
}
break;
default:
if (active) {
result.append(t);
}
}
}
}
private void addSpacemarker(Token l, Token t, TokenList target) {
Token tl= target.last();
if (tl != null && tl.getType() == CPreprocessor.tSPACE) {
return;
}
if (l != null && t != null) {
final Object s1= l.fSource;
final Object s2= t.fSource;
if (s1 == s2 && s1 != null && l.getType() != CPreprocessor.tSPACE) {
if (l.getEndOffset() != t.getOffset()) {
target.append(new Token(CPreprocessor.tSPACE, s1, l.getEndOffset(), t.getOffset()));
}
}
}
}
/**
* Informs the tracker that the function style macro has been expanded.
*/
public void endFunctionStyleMacro() {
fStepCount++;
fMacroStack.removeLast();
}
/**
* Called for the requested step
* @param macro the macro expanded in the requested step.
* @param identifier the token that gets replaced.
* @param replacement the replacement
* @param result a list to store the macro in.
*/
public void storeObjectStyleMacroReplacement(PreprocessorMacro macro, Token identifier, TokenList replacement, TokenList result) {
fMacroDefinition= macro;
fReplaceFrom= fReplaceTo= identifier;
result.append(identifier);
StringBuilder buf= new StringBuilder();
toString(replacement, fLexer.getInput(), buf, buf, buf);
fReplacementText= buf.toString();
}
/**
* Informs the tracker that an object style macro has been expanded.
*/
public void endObjectStyleMacro() {
fStepCount++;
}
}

View file

@ -0,0 +1,184 @@
/*******************************************************************************
* Copyright (c) 2008 Wind River Systems, Inc. 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:
* Markus Schorn - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.core.parser.scanner;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTMacroExpansion;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.rewrite.MacroExpansionExplorer;
import org.eclipse.jface.text.IRegion;
import org.eclipse.text.edits.ReplaceEdit;
/**
* Delegates the task of exploring macro expansions to simpler explorers dealing with
* a single macro, only.
* @since 5.0
*/
public class MultiMacroExpansionExplorer extends MacroExpansionExplorer {
private static final class ASTFileLocation implements IASTFileLocation {
private final String fFilePath;
private final int fOffset;
private final int fLength;
private ASTFileLocation(String filePath, int offset, int length) {
fFilePath= filePath;
fOffset= offset;
fLength= length;
}
public int getNodeOffset() {return fOffset;}
public int getNodeLength() {return fLength;}
public String getFileName() {return fFilePath;}
public int getStartingLineNumber() {return 0;}
public int getEndingLineNumber() {return 0;}
public IASTFileLocation asFileLocation() {return this;}
}
private final char[] fSource;
private final int[] fBoundaries;
private final SingleMacroExpansionExplorer[] fDelegates;
public MultiMacroExpansionExplorer(IASTTranslationUnit tu, IASTFileLocation loc) {
if (tu == null || loc == null || loc.getNodeLength() == 0) {
throw new IllegalArgumentException();
}
final ILocationResolver resolver = (ILocationResolver) tu.getAdapter(ILocationResolver.class);
if (resolver == null) {
throw new IllegalArgumentException();
}
final IASTMacroExpansion[] expansions = resolver.getMacroExpansions(loc);
final int count= expansions.length;
if (count > 0) {
int from= loc.getNodeOffset();
int to= from+loc.getNodeLength();
final int lfrom = expansions[0].asFileLocation().getNodeOffset();
final IASTFileLocation l= expansions[count-1].asFileLocation();
final int lto= l.getNodeOffset() + l.getNodeLength();
if (lfrom < from || lto > to) {
from= Math.min(from, lfrom);
to= Math.max(to, lto);
loc= new ASTFileLocation(loc.getFileName(), from, to-from);
}
}
fSource= resolver.getUnpreprocessedSignature(loc);
fBoundaries= new int[count*2+1];
fDelegates= new SingleMacroExpansionExplorer[count];
final int firstOffset= loc.getNodeOffset();
int bidx= -1;
int didx= -1;
for (IASTMacroExpansion expansion : expansions) {
IASTName ref= expansion.getMacroReference();
if (ref != null) {
IASTFileLocation refLoc= expansion.asFileLocation();
int from= refLoc.getNodeOffset()-firstOffset;
int to= from+refLoc.getNodeLength();
fBoundaries[++bidx]= from;
fBoundaries[++bidx]= to;
fDelegates[++didx]= new SingleMacroExpansionExplorer(new String(fSource, from, to-from), ref,
resolver.getImplicitMacroReferences(ref));
}
}
fBoundaries[++bidx]= fSource.length;
}
public MultiMacroExpansionExplorer(final IASTTranslationUnit tu, final IRegion loc) {
this(tu, new ASTFileLocation(tu.getFilePath(), loc.getOffset(), loc.getLength()));
}
@Override
public IMacroExpansionStep getFullExpansion() {
List<ReplaceEdit> edits = combineReplaceEdits(fDelegates.length);
return new MacroExpansionStep(new String(fSource), null, edits.toArray(new ReplaceEdit[edits.size()]));
}
/**
* Combines the replace edits of the leading delegates.
*/
private List<ReplaceEdit> combineReplaceEdits(int count) {
ArrayList<ReplaceEdit> edits= new ArrayList<ReplaceEdit>();
for (int i=0; i < count; i++) {
IMacroExpansionStep step= fDelegates[i].getFullExpansion();
shiftAndAddEdits(fBoundaries[2*i], step.getReplacements(), edits);
}
return edits;
}
/**
* Shifts and adds the replace edits to the target list.
*/
private void shiftAndAddEdits(final int shift, ReplaceEdit[] stepEdits, List<ReplaceEdit> target) {
for (int j = 0; j < stepEdits.length; j++) {
final ReplaceEdit r = stepEdits[j];
final String rtext = r.getText();
target.add(new ReplaceEdit(shift+r.getOffset(), r.getLength(), rtext));
}
}
@Override
public int getExpansionStepCount() {
int result= 0;
for (int i=0; i < fDelegates.length; i++) {
result+= fDelegates[i].getExpansionStepCount();
}
return result;
}
@Override
public IMacroExpansionStep getExpansionStep(int step) throws IndexOutOfBoundsException {
if (step < 0) {
throw new IndexOutOfBoundsException();
}
int i;
MacroExpansionStep dresult= null;
StringBuilder before= new StringBuilder();
before.append(fSource, 0, fBoundaries[0]);
for (i=0; i < fDelegates.length; i++) {
final SingleMacroExpansionExplorer delegate = fDelegates[i];
int dsteps= delegate.getExpansionStepCount();
if (step < dsteps) {
dresult= delegate.getExpansionStep(step);
break;
}
before.append(delegate.getFullExpansion().getCodeAfterStep());
appendGap(before, i);
step-= dsteps;
}
if (dresult == null) {
throw new IndexOutOfBoundsException();
}
final int shift= before.length();
final int end= fBoundaries[2*i+1];
before.append(dresult.getCodeBeforeStep());
before.append(fSource, end, fSource.length-end);
List<ReplaceEdit> replacements= new ArrayList<ReplaceEdit>();
shiftAndAddEdits(shift, dresult.getReplacements(), replacements);
return new MacroExpansionStep(before.toString(), dresult.getExpandedMacro(), replacements.toArray(new ReplaceEdit[replacements.size()]));
}
private void appendGap(StringBuilder result, int i) {
int idx= 2*i+1;
int gapFrom= fBoundaries[idx];
int gapTo= fBoundaries[++idx];
result.append(fSource, gapFrom, gapTo-gapFrom);
}
}

View file

@ -0,0 +1,97 @@
/*******************************************************************************
* Copyright (c) 2008 Wind River Systems, Inc. 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:
* Markus Schorn - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.core.parser.scanner;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.rewrite.MacroExpansionExplorer;
import org.eclipse.cdt.core.parser.util.CharArrayMap;
import org.eclipse.cdt.internal.core.parser.scanner.Lexer.LexerOptions;
import org.eclipse.text.edits.ReplaceEdit;
/**
* Performs step by step macro expansion for an exact macro expansion location.
* @since 5.0
*/
public class SingleMacroExpansionExplorer extends MacroExpansionExplorer {
private static final LexerOptions LEX_OPTIONS= new LexerOptions();
static {
LEX_OPTIONS.fCreateImageLocations= false;
}
private final String fInput;
private final CharArrayMap<PreprocessorMacro> fDictionary;
private MacroExpansionStep fFullExpansion;
private int fExpansionCount;
public SingleMacroExpansionExplorer(String input, IASTName ref, IASTName[] implicitRefs) {
fInput= input;
fDictionary= createDictionary(ref, implicitRefs);
}
private CharArrayMap<PreprocessorMacro> createDictionary(IASTName ref, IASTName[] implicitRefs) {
// mstodo handle dynamic style macros
// mstodo clone index-macros
CharArrayMap<PreprocessorMacro> map= new CharArrayMap<PreprocessorMacro>(implicitRefs.length+1);
addMacroDefinition(map, ref);
for (IASTName name : implicitRefs) {
addMacroDefinition(map, name);
}
return map;
}
private void addMacroDefinition(CharArrayMap<PreprocessorMacro> map, IASTName name) {
IBinding binding= name.getBinding();
if (binding instanceof PreprocessorMacro) {
map.put(name.toCharArray(), (PreprocessorMacro) binding);
}
}
@Override
public IMacroExpansionStep getFullExpansion() {
computeExpansion();
return fFullExpansion;
}
@Override
public int getExpansionStepCount() {
computeExpansion();
return fExpansionCount;
}
private void computeExpansion() {
MacroExpander expander= new MacroExpander(ILexerLog.NULL, fDictionary, null, LEX_OPTIONS);
MacroExpansionTracker tracker= new MacroExpansionTracker(Integer.MAX_VALUE);
expander.expand(fInput, tracker);
fExpansionCount= tracker.getStepCount();
ReplaceEdit r= tracker.getReplacement();
ReplaceEdit[] replacements= r==null ? new ReplaceEdit[0] : new ReplaceEdit[]{r};
fFullExpansion= new MacroExpansionStep(fInput, null, replacements);
}
@Override
public MacroExpansionStep getExpansionStep(int step) throws IndexOutOfBoundsException {
computeExpansion();
if (step < 0 || step >= fExpansionCount) {
throw new IndexOutOfBoundsException();
}
MacroExpander expander= new MacroExpander(ILexerLog.NULL, fDictionary, null, LEX_OPTIONS);
MacroExpansionTracker tracker= new MacroExpansionTracker(step);
expander.expand(fInput, tracker);
fExpansionCount= tracker.getStepCount();
ReplaceEdit r= tracker.getReplacement();
ReplaceEdit[] replacements= r==null ? new ReplaceEdit[0] : new ReplaceEdit[]{r};
return new MacroExpansionStep(tracker.getCodeBeforeStep(), tracker.getExpandedMacro(), replacements);
}
}

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2006, 2007 QNX Software Systems and others.
* Copyright (c) 2006, 2008 QNX Software Systems 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
@ -152,13 +152,13 @@ public class PDOMMacro implements IIndexMacro, IASTFileLocation {
try {
byte style= pdom.getDB().getByte(record + MACRO_STYLE);
if (style == MACROSTYLE_FUNCTION) {
List paramList = new ArrayList();
List<char[]> paramList = new ArrayList<char[]>();
PDOMMacroParameter param= getFirstParameter();
while (param != null) {
paramList.add(param.getName().getChars());
param = param.getNextParameter();
}
fParameterList= (char[][])paramList.toArray(new char[paramList.size()][]);
fParameterList= paramList.toArray(new char[paramList.size()][]);
}
} catch (CoreException e) {
CCorePlugin.log(e);
@ -285,6 +285,7 @@ public class PDOMMacro implements IIndexMacro, IASTFileLocation {
return null;
}
@SuppressWarnings("unchecked")
public Object getAdapter(Class adapter) {
return null;
}