/******************************************************************************* * Copyright (c) 2006, 2016 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) * Sergey Prigogin (Google) *******************************************************************************/ package org.eclipse.cdt.internal.core.pdom.dom; import org.eclipse.cdt.core.dom.ast.IASTFileLocation; import org.eclipse.cdt.core.dom.ast.IASTName; import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement; import org.eclipse.cdt.core.index.IIndexFile; import org.eclipse.cdt.core.index.IIndexFileLocation; import org.eclipse.cdt.internal.core.index.IIndexFragment; import org.eclipse.cdt.internal.core.index.IIndexFragmentFile; import org.eclipse.cdt.internal.core.index.IIndexFragmentInclude; import org.eclipse.cdt.internal.core.pdom.db.Database; import org.eclipse.core.runtime.CoreException; /** * @author Doug Schaefer */ public class PDOMInclude implements IIndexFragmentInclude { private static final int INCLUDED_FILE = 0; private static final int INCLUDED_BY = 4; private static final int INCLUDES_NEXT = 8; private static final int INCLUDED_BY_NEXT = 12; private static final int INCLUDED_BY_PREV = 16; // If the include name is the same as the end part of the path of the included file, // we store the length of the name instead of the name itself, and indicate that // by turning on FLAG_DEDUCIBLE_NAME flag. Notice that the length of include name // can be different from the node length, if the name is defined by a macro. private static final int INCLUDE_NAME_OR_LENGTH = 20; // TODO: assumes that int and stored pointers are the same size private static final int NODE_OFFSET = 24; // 3-byte unsigned int (sufficient for files <= 16mb) private static final int NODE_LENGTH = 27; // short (sufficient for names <= 32k) private static final int FLAGS = 29; private static final int RECORD_SIZE = 30; private static final int FLAG_SYSTEM_INCLUDE = 0x01; private static final int FLAG_INACTIVE_INCLUDE = 0x02; private static final int FLAG_RESOLVED_BY_HEURISTICS= 0x04; private static final int FLAG_DEDUCIBLE_NAME = 0x08; private static final int FLAG_EXPORTED_FILE = 0x10; private final PDOMLinkage linkage; private final long record; // Cached fields private String fName; public PDOMInclude(PDOMLinkage linkage, long record) { this.linkage = linkage; this.record = record; } public PDOMInclude(PDOMLinkage linkage, IASTPreprocessorIncludeStatement include, PDOMFile containerFile, PDOMFile targetFile) throws CoreException { this.linkage = linkage; this.record = linkage.getDB().malloc(RECORD_SIZE); IASTName name = include.getName(); char[] nameChars = name.getSimpleID(); IASTFileLocation loc = name.getFileLocation(); // Includes generated by -include or -macro don't have a location if (loc != null) { linkage.getDB().put3ByteUnsignedInt(record + NODE_OFFSET, loc.getNodeOffset()); linkage.getDB().putShort(record + NODE_LENGTH, (short) loc.getNodeLength()); } final Database db = linkage.getDB(); if (targetFile != null) { db.putRecPtr(record + INCLUDED_FILE, targetFile.getRecord()); } boolean deducible_name = isDeducibleName(targetFile, nameChars); // If the name is the same as an end part of the path of the included file, // store the length of the name instead of the name itself. if (deducible_name) { db.putInt(record + INCLUDE_NAME_OR_LENGTH, nameChars.length); } else { db.putRecPtr(record + INCLUDE_NAME_OR_LENGTH, db.newString(nameChars).getRecord()); } setFlag(encodeFlags(include, deducible_name)); setIncludedBy(containerFile); } private byte encodeFlags(IASTPreprocessorIncludeStatement include, boolean deducible_name) { byte flags= 0; if (include.isSystemInclude()) { flags |= FLAG_SYSTEM_INCLUDE; } if (!include.isActive()) { flags |= FLAG_INACTIVE_INCLUDE; } else if (include.isResolvedByHeuristics()) { flags |= FLAG_RESOLVED_BY_HEURISTICS; } if (include.isIncludedFileExported()) { flags |= FLAG_EXPORTED_FILE; } if (deducible_name) { flags |= FLAG_DEDUCIBLE_NAME; } return flags; } public long getRecord() { return record; } public void delete() throws CoreException { if (isResolved()) { // Remove us from the includedBy chain removeThisFromIncludedByChain(); } final Database db = linkage.getDB(); if ((getFlag() & FLAG_DEDUCIBLE_NAME) == 0) { long rec = db.getRecPtr(record + INCLUDE_NAME_OR_LENGTH); db.getString(rec).delete(); } // Delete our record db.free(record); } private void removeThisFromIncludedByChain() throws CoreException { PDOMInclude prevInclude = getPrevInIncludedBy(); PDOMInclude nextInclude = getNextInIncludedBy(); if (prevInclude != null) { prevInclude.setNextInIncludedBy(nextInclude); } else { ((PDOMFile) getIncludes()).setFirstIncludedBy(nextInclude); } if (nextInclude != null) nextInclude.setPrevInIncludedBy(prevInclude); } @Override public IIndexFragmentFile getIncludes() throws CoreException { long rec = linkage.getDB().getRecPtr(record + INCLUDED_FILE); return rec != 0 ? new PDOMFile(linkage, rec) : null; } void setIncludes(PDOMFile includedFile) throws CoreException { long rec = includedFile != null ? includedFile.getRecord() : 0; linkage.getDB().putRecPtr(record + INCLUDED_FILE, rec); } /** * Checks if the name is the same as the end part of the path of the included file. */ private static boolean isDeducibleName(PDOMFile includedFile, char[] name) throws CoreException { if (includedFile == null) { return false; } String s = includedFile.getLocation().getURI().getPath(); int pos = s.length() - name.length; if (pos < 0) { return false; } for (int i = 0; i < name.length; i++, pos++) { if (s.charAt(pos) != name[i]) { return false; } } return true; } @Override public IIndexFile getIncludedBy() throws CoreException { long rec = linkage.getDB().getRecPtr(record + INCLUDED_BY); return rec != 0 ? new PDOMFile(linkage, rec) : null; } void setIncludedBy(PDOMFile includedBy) throws CoreException { long rec = includedBy != null ? includedBy.getRecord() : 0; linkage.getDB().putRecPtr(record + INCLUDED_BY, rec); } public PDOMInclude getNextInIncludes() throws CoreException { long rec = linkage.getDB().getRecPtr(record + INCLUDES_NEXT); return rec != 0 ? new PDOMInclude(linkage, rec) : null; } public void setNextInIncludes(PDOMInclude include) throws CoreException { long rec = include != null ? include.getRecord() : 0; linkage.getDB().putRecPtr(record + INCLUDES_NEXT, rec); } public PDOMInclude getNextInIncludedBy() throws CoreException { long rec = linkage.getDB().getRecPtr(record + INCLUDED_BY_NEXT); return rec != 0 ? new PDOMInclude(linkage, rec) : null; } public void setNextInIncludedBy(PDOMInclude include) throws CoreException { long rec = include != null ? include.getRecord() : 0; linkage.getDB().putRecPtr(record + INCLUDED_BY_NEXT, rec); } public PDOMInclude getPrevInIncludedBy() throws CoreException { long rec = getPrevInIncludedByRecord(); return rec != 0 ? new PDOMInclude(linkage, rec) : null; } long getPrevInIncludedByRecord() throws CoreException { return linkage.getDB().getRecPtr(record + INCLUDED_BY_PREV); } public void setPrevInIncludedBy(PDOMInclude include) throws CoreException { long rec = include != null ? include.getRecord() : 0; linkage.getDB().putRecPtr(record + INCLUDED_BY_PREV, rec); } @Override public IIndexFileLocation getIncludedByLocation() throws CoreException { return getIncludedBy().getLocation(); } @Override public IIndexFileLocation getIncludesLocation() throws CoreException { IIndexFragmentFile includes = getIncludes(); return includes != null ? includes.getLocation() : null; } @Override public IIndexFragment getFragment() { return linkage.getPDOM(); } private void setFlag(byte flag) throws CoreException { linkage.getDB().putByte(record + FLAGS, flag); } private int getFlag() throws CoreException { return linkage.getDB().getByte(record + FLAGS); } @Override public boolean isSystemInclude() throws CoreException { return (getFlag() & FLAG_SYSTEM_INCLUDE) != 0; } @Override public boolean isActive() throws CoreException { return (getFlag() & FLAG_INACTIVE_INCLUDE) == 0; } @Override public boolean isResolved() throws CoreException { return linkage.getDB().getRecPtr(record + INCLUDED_FILE) != 0; } @Override public boolean isResolvedByHeuristics() throws CoreException { return (getFlag() & FLAG_RESOLVED_BY_HEURISTICS) != 0; } @Override public boolean isIncludedFileExported() throws CoreException { return (getFlag() & FLAG_EXPORTED_FILE) != 0; } @Override public int getNameOffset() throws CoreException { return linkage.getDB().get3ByteUnsignedInt(record + NODE_OFFSET); } @Override public int getNameLength() throws CoreException { return linkage.getDB().getShort(record + NODE_LENGTH) & 0xffff; } @Override public String getFullName() throws CoreException { if (fName == null) { final Database db = linkage.getDB(); // The include name is either stored explicitly, or can be deduced from the path // of the included file. if ((getFlag() & FLAG_DEDUCIBLE_NAME) == 0) { long rec = db.getRecPtr(record + INCLUDE_NAME_OR_LENGTH); fName = db.getString(rec).getString(); } else { String path = getIncludes().getLocation().getURI().getPath(); int nameLength = db.getInt(record + INCLUDE_NAME_OR_LENGTH); fName = path.substring(Math.max(path.length() - nameLength, 0)); } } return fName; } @Override public String getName() throws CoreException { final String fullName= getFullName(); final int idx= Math.max(fullName.lastIndexOf('/'), fullName.lastIndexOf('\\')); return fullName.substring(idx + 1); } public void convertToUnresolved() throws CoreException { if (isResolved()) { final Database db = linkage.getDB(); int flag = getFlag(); // Since included file is going away, the name is no longer deducible. if ((flag & FLAG_DEDUCIBLE_NAME) != 0) { long rec= db.newString(getFullName()).getRecord(); db.putRecPtr(record + INCLUDE_NAME_OR_LENGTH, rec); setFlag((byte) (flag & ~FLAG_DEDUCIBLE_NAME)); } db.putRecPtr(record + INCLUDED_FILE, 0); } } @Override public String toString() { StringBuilder buf = new StringBuilder(); try { boolean isSystem = isSystemInclude(); buf.append(isSystem ? '<' : '"'); buf.append(getFullName()); buf.append(isSystem ? '>' : '"'); IIndexFile includedBy = getIncludedBy(); if (includedBy != null) buf.append(" in ").append(includedBy); //$NON-NLS-1$ IIndexFragmentFile includes = getIncludes(); if (includes != null) { buf.append(" resolved to ").append(includes); //$NON-NLS-1$ } else { buf.append(" unresolved"); //$NON-NLS-1$ } } catch (CoreException e) { buf.append(" (incomplete due to ").append(e.getClass().getName()).append(')'); //$NON-NLS-1$ e.printStackTrace(); } return buf.toString(); } }