/******************************************************************************* * Copyright (c) 2007, 2013 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: * Anton Leherbauer (Wind River Systems) - initial API and implementation *******************************************************************************/ package org.eclipse.cdt.internal.core.model; import java.util.HashMap; import java.util.Map; import org.eclipse.cdt.core.model.AssemblyLanguage; import org.eclipse.cdt.core.model.CModelException; import org.eclipse.cdt.core.model.IContributedModelBuilder; import org.eclipse.cdt.core.model.ITranslationUnit; import org.eclipse.cdt.core.parser.IToken; import org.eclipse.cdt.core.parser.OffsetLimitReachedException; import org.eclipse.cdt.internal.core.parser.scanner.AbstractCharArray; import org.eclipse.cdt.internal.core.parser.scanner.ILexerLog; import org.eclipse.cdt.internal.core.parser.scanner.Lexer; import org.eclipse.cdt.internal.core.parser.scanner.Lexer.LexerOptions; import org.eclipse.cdt.internal.core.parser.scanner.Token; /** * A simple model builder for assembly translation units. * Recognizes preprocessor directives (#include and #define) * and labels. * * @see AssemblyLanguage * @since 5.0 */ public class AsmModelBuilder implements IContributedModelBuilder { private static final class AsmDirective { private static final AsmDirective GLOBAL= new AsmDirective(); private static final AsmDirective DATA= new AsmDirective(); } private static final class LexerLog implements ILexerLog { @Override public void handleComment(boolean isBlockComment, int offset, int endOffset, AbstractCharArray input) { } @Override public void handleProblem(int problemID, char[] info, int offset, int endOffset) { } } private static final class Counter { int fCount; } private static final Map<String, AsmDirective> fgDirectives; static { fgDirectives= new HashMap<String, AsmDirective>(); fgDirectives.put("globl", AsmDirective.GLOBAL); //$NON-NLS-1$ fgDirectives.put("global", AsmDirective.GLOBAL); //$NON-NLS-1$ fgDirectives.put("ascii", AsmDirective.DATA); //$NON-NLS-1$ fgDirectives.put("asciz", AsmDirective.DATA); //$NON-NLS-1$ fgDirectives.put("byte", AsmDirective.DATA); //$NON-NLS-1$ fgDirectives.put("long", AsmDirective.DATA); //$NON-NLS-1$ fgDirectives.put("word", AsmDirective.DATA); //$NON-NLS-1$ } private TranslationUnit fTranslationUnit; private char fLineSeparatorChar; private Map<String, Counter> fGlobals; private Map<String, Counter> fLabels; private int fLastLabelEndOffset; private AsmLabel fLastGlobalLabel; private SourceManipulation fLastLabel; private Lexer fLexer; /** * Creates a model builder for the given assembly translation unit. * * @param tu the translation unit */ public AsmModelBuilder(ITranslationUnit tu) { fTranslationUnit= (TranslationUnit)tu; } /** * Configure the line separator character (in addition to normal newlines). * * @param lineSeparatorChar */ public void setLineSeparatorCharacter(char lineSeparatorChar) { fLineSeparatorChar= lineSeparatorChar; } /* * @see org.eclipse.cdt.core.model.IContributedModelBuilder#parse(boolean) */ @Override public void parse(boolean quickParseMode) throws Exception { char[] source = fTranslationUnit.getContents(); if (source == null) { return; } buildModel(source); // not sure whether this is necessary or not fTranslationUnit.setIsStructureKnown(true); // cleanup fGlobals= null; fLabels= null; fLexer= null; } /** * Build the model. * * @param source * @throws CModelException */ private void buildModel(final char[] source) throws CModelException { fGlobals= new HashMap<String, Counter>(); fLabels= new HashMap<String, Counter>(); fLastLabel= null; fLastGlobalLabel= null; fLastLabelEndOffset= 0; final LexerOptions lexerOptions= new LexerOptions(); fLexer= new Lexer(source, lexerOptions, new LexerLog(), null); // if true the next token is the first on a (logical) line boolean firstTokenOnLine= true; // next token can be an instruction or a label boolean expectInstruction=true; // inside instruction boolean inInstruction= false; IToken token= nextToken(); while (token != null) { switch (token.getType()) { case IToken.tPOUND: if (fLexer.currentTokenIsFirstOnLine()) { parsePPDirective(fTranslationUnit); } break; case IToken.tDOT: // assembly directive? firstTokenOnLine= false; if (expectInstruction) { expectInstruction= false; token= nextToken(); if (token != null && token.getType() == IToken.tIDENTIFIER) { String text= token.getImage(); if (isGlobalDirective(text)) { firstTokenOnLine= parseGlobalDirective(); } else if (isDataDirective(text)) { inInstruction= true; } } } break; case IToken.tIDENTIFIER: // identifier may be a label or part of an instruction if (firstTokenOnLine) { // peek next char char nextChar= source[token.getEndOffset()]; if (nextChar == ':') { createLabel(fTranslationUnit, token); expectInstruction= true; } else { expectInstruction= false; inInstruction= true; } firstTokenOnLine= false; } else if (expectInstruction){ expectInstruction= false; inInstruction= true; } break; case Lexer.tNEWLINE: if (!firstTokenOnLine) { firstTokenOnLine= true; if (inInstruction) { fLastLabelEndOffset= fLexer.currentToken().getEndOffset(); } } break; default: expectInstruction= false; firstTokenOnLine= false; if (fLineSeparatorChar != 0) { if (token.getLength() == 1 && token.getCharImage()[0] == fLineSeparatorChar) { firstTokenOnLine= true; } } } if (firstTokenOnLine) { expectInstruction= true; inInstruction= false; } token= nextToken(); } if (!firstTokenOnLine && inInstruction) { fLastLabelEndOffset= fLexer.currentToken().getEndOffset(); } if (fLastLabel != null) { fixupLastLabel(); } if (fLastGlobalLabel != null) { fixupLastGlobalLabel(); } } /** * @return the next token from the scanner or <code>null</code> on end-of-input. */ protected IToken nextToken() { Token token; try { token= fLexer.nextToken(); if (token.getType() == IToken.tEND_OF_INPUT) { token = null; } } catch (OffsetLimitReachedException exc) { token= null; } return token; } private boolean parseGlobalDirective() { boolean eol= false; do { IToken t= nextToken(); if (t == null) { break; } switch (t.getType()) { case IToken.tIDENTIFIER: registerGlobalLabel(t.getImage()); break; case Lexer.tNEWLINE: eol= true; break; default: if (fLineSeparatorChar != 0) { if (t.getLength() == 1 && t.getCharImage()[0] == fLineSeparatorChar) { eol= true; } } } } while (!eol); return eol; } private static final boolean isDataDirective(String directive) { return fgDirectives.get(directive) == AsmDirective.DATA; } private static final boolean isGlobalDirective(String directive) { return fgDirectives.get(directive) == AsmDirective.GLOBAL; } protected void registerGlobalLabel(String labelName) { Counter counter= fGlobals.get(labelName); if (counter == null) { fGlobals.put(labelName, counter= new Counter()); } counter.fCount++; } private int getGlobalLabelIndex(String labelName) { Counter counter= fGlobals.get(labelName); if (counter == null) { return 0; } return counter.fCount; } protected int registerLabel(String labelName) { Counter counter= fLabels.get(labelName); if (counter == null) { fLabels.put(labelName, counter= new Counter()); } return counter.fCount++; } protected void createLabel(CElement parent, IToken token) throws CModelException { String labelName= token.getImage(); int index= getGlobalLabelIndex(labelName); boolean global= index > 0; if (!global) { index= registerLabel(labelName); } AsmLabel label= new AsmLabel(parent, labelName, global, index); SourceManipulationInfo labelInfo= label.getSourceManipulationInfo(); labelInfo.setIdPos(token.getOffset(), token.getLength()); labelInfo.setPos(token.getOffset(), token.getLength()); if (fLastLabel != null) { fixupLastLabel(); } if (global) { // new global label if (fLastGlobalLabel != null) { fixupLastGlobalLabel(); } fLastGlobalLabel= label; } else { // add under global label if available if (fLastGlobalLabel != null) { parent= fLastGlobalLabel; } } fLastLabel= label; parent.addChild(label); } /** * Set the body position of the last global label. * * @throws CModelException */ private void fixupLastGlobalLabel() throws CModelException { if (fLastLabel != null && fLastLabel != fLastGlobalLabel) { SourceManipulationInfo globalLabelInfo= fLastGlobalLabel.getSourceManipulationInfo(); SourceManipulationInfo labelInfo= fLastLabel.getSourceManipulationInfo(); globalLabelInfo.setPos(globalLabelInfo.getStartPos(), labelInfo.getStartPos() + labelInfo.getLength() - globalLabelInfo.getStartPos()); // TLETODO set line info } } /** * Set the body position of the last label. * * @throws CModelException */ private void fixupLastLabel() throws CModelException { if (fLastLabelEndOffset > 0) { SourceManipulationInfo labelInfo= fLastLabel.getSourceManipulationInfo(); labelInfo.setPos(labelInfo.getStartPos(), fLastLabelEndOffset - labelInfo.getStartPos()); // TLETODO set line info fLastLabelEndOffset= 0; } } private void parsePPDirective(CElement parent) throws CModelException { IToken token= nextToken(); if (token != null && token.getType() == IToken.tIDENTIFIER) { final String image= token.getImage(); if (image.equals("define")) { //$NON-NLS-1$ try { parsePPDefine(parent); } catch (OffsetLimitReachedException exc) { } return; } else if (image.equals("include")) { //$NON-NLS-1$ try { parsePPInclude(parent); } catch (OffsetLimitReachedException exc) { } return; } } try { skipToNewLine(); } catch (OffsetLimitReachedException exc) { } } protected int skipToNewLine() throws OffsetLimitReachedException { return fLexer.consumeLine(fLexer.currentToken().getEndOffset()); } private void parsePPDefine(CElement parent) throws CModelException, OffsetLimitReachedException { int startOffset= fLexer.currentToken().getOffset(); int nameStart= 0; int nameEnd= 0; String name= null; IToken t= nextToken(); if (t.getType() == IToken.tIDENTIFIER) { nameStart= fLexer.currentToken().getOffset(); nameEnd= fLexer.currentToken().getEndOffset(); name= t.getImage(); } if (name == null) { return; } int endOffset= skipToNewLine(); Macro macro= new Macro(parent, name); SourceManipulationInfo macroInfo= macro.getSourceManipulationInfo(); macroInfo.setIdPos(nameStart, nameEnd - nameStart); macroInfo.setPos(startOffset, endOffset - startOffset); parent.addChild(macro); } private void parsePPInclude(CElement parent) throws CModelException, OffsetLimitReachedException { int startOffset= fLexer.currentToken().getOffset(); int nameStart= 0; int nameEnd= 0; String name= null; boolean isStandard= false; IToken t= nextToken(); switch (t.getType()) { case IToken.tLT: nameStart= fLexer.currentToken().getOffset(); do { t= nextToken(); } while (t.getType() != IToken.tGT); nameEnd= fLexer.currentToken().getEndOffset(); name= new String(fLexer.getInputChars(nameStart + 1, nameEnd - 1)); isStandard= true; break; case IToken.tSTRING: nameStart= fLexer.currentToken().getOffset(); nameEnd= fLexer.currentToken().getEndOffset(); name= t.getImage().substring(1, t.getLength() - 1); break; case IToken.tIDENTIFIER: nameStart= fLexer.currentToken().getOffset(); nameEnd= fLexer.currentToken().getEndOffset(); name= t.getImage(); break; default: } if (name == null) { return; } int endOffset= skipToNewLine(); Include include= new Include(parent, name, isStandard); SourceManipulationInfo includeInfo= include.getSourceManipulationInfo(); includeInfo.setIdPos(nameStart, nameEnd - nameStart); includeInfo.setPos(startOffset, endOffset - startOffset); parent.addChild(include); } }