| /* |
| Copyright 2019 Alain Dargelas |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| /* |
| * File: AnalyzeFile.cpp |
| * Author: alain |
| * |
| * Created on July 23, 2017, 11:05 PM |
| */ |
| #include "SourceCompile/SymbolTable.h" |
| #include "Design/TimeInfo.h" |
| #include "Design/DesignElement.h" |
| #include "CommandLine/CommandLineParser.h" |
| #include "ErrorReporting/Error.h" |
| #include "ErrorReporting/Location.h" |
| #include "ErrorReporting/ErrorDefinition.h" |
| #include "ErrorReporting/ErrorContainer.h" |
| #include "SourceCompile/IncludeFileInfo.h" |
| #include "Utils/StringUtils.h" |
| #include "SourceCompile/CompilationUnit.h" |
| #include "SourceCompile/PreprocessFile.h" |
| #include "SourceCompile/CompileSourceFile.h" |
| #include "SourceCompile/Compiler.h" |
| #include "Design/Design.h" |
| #include "SourceCompile/AnalyzeFile.h" |
| #include <fstream> |
| #include <stdio.h> |
| #include <ctype.h> |
| #include <string.h> |
| #include <iostream> |
| #include <sstream> |
| #include <stack> |
| #include <regex> |
| using namespace SURELOG; |
| |
| void saveContent(std::string fileName, std::string& content) { |
| std::ifstream ifs; |
| ifs.open(fileName); |
| bool save = true; |
| if (ifs.good()) { |
| std::string str((std::istreambuf_iterator<char>(ifs)), |
| std::istreambuf_iterator<char>()); |
| ifs.close(); |
| if (str == content) save = false; |
| } |
| if (save) { |
| std::ofstream ofs(fileName); |
| ofs << content; |
| ofs.close(); |
| } |
| } |
| |
| void AnalyzeFile::checkSLlineDirective_(std::string line, unsigned int lineNb) { |
| std::stringstream ss; |
| std::string keyword; |
| /* Storing the whole string into string stream */ |
| ss << line; |
| std::string tmp; |
| ss >> tmp; |
| std::stringstream(tmp) >> keyword; |
| unsigned int type = 0; |
| if (keyword == "SLline") { |
| IncludeFileInfo info(0, 0, 0, 0); |
| ss >> tmp; |
| std::stringstream(tmp) >> info.m_sectionStartLine; |
| ss >> tmp; |
| std::string file; |
| std::stringstream(tmp) >> file; |
| StringUtils::ltrim(file, '\"'); |
| StringUtils::rtrim(file, '\"'); |
| info.m_sectionFile = m_clp->getSymbolTable()->registerSymbol(file); |
| ss >> tmp; |
| std::stringstream(tmp) >> type; |
| |
| if (type == 1) { |
| // Push |
| info.m_originalLine = lineNb; |
| info.m_type = type; |
| m_includeFileInfo.push(info); |
| } else if (type == 2) { |
| // Pop |
| if (m_includeFileInfo.size()) m_includeFileInfo.pop(); |
| if (m_includeFileInfo.size()) { |
| m_includeFileInfo.top().m_sectionFile = info.m_sectionFile; |
| m_includeFileInfo.top().m_originalLine = lineNb; |
| m_includeFileInfo.top().m_sectionStartLine = |
| info.m_sectionStartLine - 1; |
| m_includeFileInfo.top().m_type = type; |
| } |
| } |
| } |
| } |
| |
| std::string AnalyzeFile::setSLlineDirective_(unsigned int lineNb, |
| unsigned int& origFromLine, |
| std::string& origFile) { |
| std::string result; |
| if (m_includeFileInfo.size()) { |
| result = "SLline "; |
| origFile = m_clp->getSymbolTable()->getSymbol( |
| m_includeFileInfo.top().m_sectionFile); |
| unsigned int origLine = m_includeFileInfo.top().m_originalLine; |
| unsigned int sectionStartLine = m_includeFileInfo.top().m_sectionStartLine; |
| origFromLine = lineNb - origLine + sectionStartLine; |
| result += std::to_string(origFromLine); |
| result += " \""; |
| result += origFile; |
| result += "\" "; |
| result += std::to_string(1); |
| result += "\n"; |
| } |
| result = ""; |
| return result; |
| } |
| |
| void AnalyzeFile::analyze() { |
| std::ifstream ifs; |
| ifs.open(m_ppFileName); |
| if (!ifs.good()) { |
| return; |
| } |
| unsigned int minNbLineForPartitioning = m_clp->getNbLinesForFileSpliting(); |
| std::vector<FileChunk> fileChunks; |
| std::string line; |
| bool inPackage = false; |
| int inClass = 0; |
| int inModule = 0; |
| bool inProgram = false; |
| int inInterface = 0; |
| bool inConfig = false; |
| bool inChecker = false; |
| bool inPrimitive = false; |
| // int inFunction = false; |
| // int inTask = false; |
| bool inComment = false; |
| bool inString = false; |
| unsigned int lineNb = 0; |
| unsigned int charNb = 0; |
| unsigned int startLine = 0; |
| unsigned int startChar = 0; |
| unsigned int indexPackage = 0; |
| unsigned int indexModule = 0; |
| int nbPackage = 0, nbClass = 0, nbModule = 0, nbProgram = 0, nbInterface = 0, |
| nbConfig = 0, nbChecker = 0, |
| nbPrimitive = 0 /*./re , nbFunction = 0, nbTask = 0*/; |
| std::vector<std::string> allLines; |
| std::string prev_keyword; |
| std::string prev_prev_keyword; |
| allLines.push_back("FILLER LINE"); |
| const std::regex import_regex("import[ ]+[a-zA-Z_0-9:\\*]+[ ]*;"); |
| std::smatch pieces_match; |
| std::string fileLevelImportSection; |
| // Parse the file |
| while (std::getline(ifs, line)) { |
| bool inLineComment = false; |
| allLines.push_back(line); |
| lineNb++; |
| char c = 0; |
| char cp = 0; |
| std::string keyword; |
| for (unsigned int i = 0; i < line.size(); i++) { |
| charNb++; |
| c = line[i]; |
| if (cp == '/' && c == '*') { |
| if (!inLineComment) inComment = true; |
| } else if (cp == '/' && c == '/') { |
| if ((!inComment) && (!inString)) inLineComment = true; |
| } else if (cp == '*' && c == '/') { |
| inComment = false; |
| } else if (cp != '\\' && c == '\"') { |
| if ((!inLineComment) && (!inComment)) inString = !inString; |
| } |
| if ((!inComment) && (!inLineComment) && (!inString)) { |
| if ((islower(c) || isupper(c) || c == '_') && |
| (i != (line.size() - 1))) { |
| keyword += c; |
| } else { |
| if ((islower(c) || isupper(c) || c == '_') && |
| (i == (line.size() - 1))) { |
| keyword += c; |
| } |
| |
| if (keyword == "package") { |
| std::string packageName; |
| for (unsigned int j = i + 1; j < line.size(); j++) { |
| if (line[j] == ';') break; |
| if (line[j] != ' ') packageName += line[j]; |
| } |
| m_design->addOrderedPackage(packageName); |
| inPackage = true; |
| startLine = lineNb; |
| startChar = charNb; |
| FileChunk chunk(DesignElement::ElemType::Package, startLine, 0, |
| startChar, 0); |
| fileChunks.push_back(chunk); |
| indexPackage = fileChunks.size() - 1; |
| } |
| if (keyword == "endpackage") { |
| if (inPackage) { |
| fileChunks[indexPackage].m_toLine = lineNb; |
| fileChunks[indexPackage].m_endChar = charNb; |
| nbPackage++; |
| } |
| inPackage = false; |
| // std::cout << "PACKAGE:" << |
| // allLines[fileChunks[indexPackage].m_fromLine] << " f:" << |
| // fileChunks[indexPackage].m_fromLine |
| // << " t:" << fileChunks[indexPackage].m_toLine << |
| // std::endl; |
| } |
| if (keyword == "module") { |
| if (inModule == 0) { |
| startLine = lineNb; |
| startChar = charNb; |
| FileChunk chunk(DesignElement::ElemType::Module, startLine, 0, |
| startChar, 0); |
| fileChunks.push_back(chunk); |
| indexModule = fileChunks.size() - 1; |
| } |
| inModule++; |
| } |
| if (keyword == "endmodule") { |
| if (inModule == 1) { |
| fileChunks[indexModule].m_toLine = lineNb; |
| fileChunks[indexModule].m_endChar = charNb; |
| nbModule++; |
| } |
| inModule--; |
| } |
| /* |
| if (keyword == "function" && (prev_keyword != "context") && |
| (prev_prev_keyword != "import")) |
| { |
| if ((inClass == 0) && (inInterface == 0) && (inModule == 0)) |
| { |
| if (inFunction == false) |
| { |
| startLine = lineNb; |
| startChar = charNb; |
| } |
| inFunction = true; |
| } |
| } |
| if (keyword == "endfunction") |
| { |
| if (inFunction == true) |
| { |
| FileChunk chunk (DesignElement::ElemType::Function, |
| startLine, lineNb, startChar, charNb); fileChunks.push_back (chunk); |
| nbFunction++; |
| } |
| inFunction = false; |
| } |
| if (keyword == "task") |
| { |
| if ((inClass == 0) && (inInterface == 0) && (inModule == 0)) |
| { |
| if (inTask == false) |
| { |
| startLine = lineNb; |
| startChar = charNb; |
| } |
| inTask = true; |
| } |
| } |
| if (keyword == "endtask") |
| { |
| if (inTask == true) |
| { |
| FileChunk chunk (DesignElement::ElemType::Task, startLine, |
| lineNb, startChar, charNb); fileChunks.push_back (chunk); nbTask++; |
| } |
| inTask = false; |
| } |
| */ |
| if (keyword == "class" && (prev_keyword != "typedef")) { |
| if (inClass == 0) { |
| startLine = lineNb; |
| startChar = charNb; |
| } |
| inClass++; |
| } |
| if (keyword == "endclass") { |
| if (inClass == 1) { |
| FileChunk chunk(DesignElement::ElemType::Class, startLine, lineNb, |
| startChar, charNb); |
| fileChunks.push_back(chunk); |
| nbClass++; |
| } |
| inClass--; |
| } |
| if (keyword == "interface") { |
| if (inInterface == 0) { |
| startLine = lineNb; |
| startChar = charNb; |
| } |
| inInterface++; |
| } |
| if (keyword == "endinterface") { |
| if (inInterface == 1) { |
| FileChunk chunk(DesignElement::ElemType::Interface, startLine, |
| lineNb, startChar, charNb); |
| fileChunks.push_back(chunk); |
| nbInterface++; |
| } |
| inInterface--; |
| } |
| if (keyword == "config") { |
| startLine = lineNb; |
| startChar = charNb; |
| inConfig = true; |
| } |
| if (keyword == "endconfig") { |
| if (inConfig) { |
| FileChunk chunk(DesignElement::ElemType::Config, startLine, |
| lineNb, startChar, charNb); |
| fileChunks.push_back(chunk); |
| nbConfig++; |
| } |
| inConfig = false; |
| } |
| if (keyword == "checker") { |
| startLine = lineNb; |
| startChar = charNb; |
| inChecker = true; |
| } |
| if (keyword == "endchecker") { |
| if (inChecker) { |
| FileChunk chunk(DesignElement::ElemType::Checker, startLine, |
| lineNb, startChar, charNb); |
| fileChunks.push_back(chunk); |
| nbChecker++; |
| } |
| inChecker = false; |
| } |
| if (keyword == "program") { |
| startLine = lineNb; |
| startChar = charNb; |
| inProgram = true; |
| } |
| if (keyword == "endprogram") { |
| if (inProgram) { |
| FileChunk chunk(DesignElement::ElemType::Program, startLine, |
| lineNb, startChar, charNb); |
| fileChunks.push_back(chunk); |
| nbProgram++; |
| } |
| inProgram = false; |
| } |
| if (keyword == "primitive") { |
| startLine = lineNb; |
| startChar = charNb; |
| inPrimitive = true; |
| } |
| if (keyword == "endprimitive") { |
| if (inPrimitive) { |
| FileChunk chunk(DesignElement::ElemType::Primitive, startLine, |
| lineNb, startChar, charNb); |
| fileChunks.push_back(chunk); |
| nbPrimitive++; |
| } |
| inPrimitive = false; |
| } |
| if (keyword != "") { |
| if (prev_keyword != "") { |
| prev_prev_keyword = prev_keyword; |
| } |
| prev_keyword = keyword; |
| } |
| keyword = ""; |
| } |
| } |
| cp = c; |
| } |
| |
| if ((!inPackage) && (!inClass) && (!inModule) && (!inProgram) && |
| (!inInterface) && (!inConfig) && (!inChecker) && (!inPrimitive) && |
| (!inComment) && (!inString)) { |
| if (std::regex_search(line, pieces_match, import_regex)) { |
| fileLevelImportSection += line; |
| } |
| } |
| } |
| ifs.close(); |
| |
| unsigned int lineSize = lineNb; |
| |
| if (lineSize < minNbLineForPartitioning) { |
| m_splitFiles.push_back(m_ppFileName); |
| m_lineOffsets.push_back(0); |
| return; |
| } |
| if (m_nbChunks < 2) { |
| m_splitFiles.push_back(m_ppFileName); |
| m_lineOffsets.push_back(0); |
| return; |
| } |
| |
| if (inComment || inString) { |
| m_splitFiles.clear(); |
| m_lineOffsets.clear(); |
| Location loc(0, 0, 0, m_clp->getSymbolTable()->registerSymbol(m_fileName)); |
| Error err(ErrorDefinition::PA_CANNOT_SPLIT_FILE, loc); |
| m_clp->getErrorContainer()->addError(err); |
| m_clp->getErrorContainer()->printMessages(); |
| return; |
| } |
| |
| // Split the file |
| |
| unsigned int chunkSize = lineSize / m_nbChunks; |
| int chunkNb = 0; |
| |
| unsigned int fromLine = 1; |
| unsigned int toIndex = 0; |
| IncludeFileInfo info(1, m_clp->getSymbolTable()->registerSymbol(m_fileName), |
| 1, 1); |
| m_includeFileInfo.push(info); |
| unsigned int linesWriten = 0; |
| for (unsigned int i = 0; i < fileChunks.size(); i++) { |
| DesignElement::ElemType chunkType = fileChunks[i].m_chunkType; |
| |
| // The case of a package or a module |
| if (chunkType == DesignElement::ElemType::Package || |
| chunkType == DesignElement::ElemType::Module) { |
| std::string packageDeclaration; |
| std::string importSection; |
| unsigned int packagelastLine = fileChunks[i].m_toLine; |
| packageDeclaration = allLines[fileChunks[i].m_fromLine]; |
| for (unsigned hi = fileChunks[i].m_fromLine; hi < fileChunks[i].m_toLine; |
| hi++) { |
| std::string header = allLines[hi]; |
| if (std::regex_search(header, pieces_match, import_regex)) { |
| importSection += header; |
| } |
| } |
| // Break up package or module |
| if ((fileChunks[i].m_toLine - fileChunks[i].m_fromLine) > chunkSize) { |
| bool splitted = false; |
| bool endPackageDetected = false; |
| std::string sllineInfo; |
| unsigned int origFromLine = 0; |
| std::string origFile; |
| // unsigned int baseFromLine = fromLine; |
| while (!endPackageDetected) { |
| std::string content; |
| bool actualContent = false; |
| bool finishPackage = false; |
| bool hitLimit = true; |
| for (unsigned int j = i + 1; j < fileChunks.size(); j++) { |
| hitLimit = false; |
| if (fileChunks[j].m_fromLine > packagelastLine) { |
| finishPackage = true; |
| toIndex = j - 1; |
| break; |
| } |
| |
| if ((fileChunks[j].m_toLine - fromLine) >= chunkSize) { |
| toIndex = j; |
| break; |
| } |
| if (j == (fileChunks.size() - 1)) { |
| toIndex = j; |
| break; |
| } |
| toIndex = j; |
| } |
| if (hitLimit) { |
| toIndex = fileChunks.size() - 1; |
| } |
| unsigned int toLine = fileChunks[toIndex].m_toLine + 1; |
| if (finishPackage) { |
| toLine = packagelastLine + 1; |
| } |
| if (toIndex == fileChunks.size() - 1) { |
| toLine = allLines.size(); |
| } |
| |
| if (splitted) { |
| content += sllineInfo; |
| content += packageDeclaration + " " + importSection; |
| // content += "SLline " + std::to_string(fromLine - baseFromLine + |
| // origFromLine + 1) + " \"" + origFile + "\" 1"; |
| } else { |
| sllineInfo = setSLlineDirective_(fromLine, origFromLine, origFile); |
| content = sllineInfo; |
| } |
| |
| bool inString = false; |
| bool inComment = false; |
| |
| m_lineOffsets.push_back(linesWriten); |
| |
| // Detect end of package or end of module |
| for (unsigned int l = fromLine; l < toLine; l++) { |
| std::string line = allLines[l]; |
| checkSLlineDirective_(line, l); |
| |
| bool inLineComment = false; |
| content += allLines[l]; |
| if (l == fileChunks[i].m_fromLine) { |
| content += " " + importSection; |
| } |
| if (l != (toLine - 1)) { |
| content += "\n"; |
| } |
| linesWriten++; |
| |
| actualContent = true; |
| char c = 0; |
| char cp = 0; |
| std::string keyword; |
| for (unsigned int ii = 0; ii < line.size(); ii++) { |
| c = line[ii]; |
| if (cp == '/' && c == '*') { |
| if (!inLineComment) inComment = true; |
| } else if (cp == '/' && c == '/') { |
| if (!inComment) inLineComment = true; |
| } else if (cp == '*' && c == '/') { |
| inComment = false; |
| } else if (cp != '\\' && c == '\"') { |
| if ((!inLineComment) && (!inComment)) inString = !inString; |
| } |
| if ((!inComment) && (!inLineComment) && (!inString)) { |
| if ((islower(c) || isupper(c) || c == '_') && |
| (ii != (line.size() - 1))) { |
| keyword += c; |
| } else { |
| if ((islower(c) || isupper(c) || c == '_') && |
| (ii == (line.size() - 1))) { |
| keyword += c; |
| } |
| |
| if ((chunkType == DesignElement::Package) && |
| (keyword == "endpackage")) |
| endPackageDetected = true; |
| if ((chunkType == DesignElement::Module) && |
| (keyword == "endmodule")) |
| endPackageDetected = true; |
| |
| keyword = ""; |
| } |
| } |
| cp = c; |
| } |
| } |
| |
| if (actualContent) { |
| splitted = true; |
| if ((chunkType == DesignElement::Package) && |
| endPackageDetected == false) |
| content += " endpackage "; |
| if ((chunkType == DesignElement::Module) && |
| endPackageDetected == false) |
| content += " endmodule "; |
| } else { |
| splitted = false; |
| } |
| |
| std::string splitFileName = |
| m_ppFileName + ".ck" + std::to_string(chunkNb); |
| if (chunkNb > 1000) { |
| m_splitFiles.clear(); |
| m_lineOffsets.clear(); |
| Location loc(0, 0, 0, |
| m_clp->getSymbolTable()->registerSymbol(m_fileName)); |
| Error err(ErrorDefinition::PA_CANNOT_SPLIT_FILE, loc); |
| m_clp->getErrorContainer()->addError(err); |
| m_clp->getErrorContainer()->printMessages(); |
| return; |
| } |
| content += " " + fileLevelImportSection; |
| saveContent(splitFileName, content); |
| |
| m_splitFiles.push_back(splitFileName); |
| |
| // m_lineOffsets.push_back(fromLine - 1); |
| |
| chunkNb++; |
| fromLine = fileChunks[toIndex].m_toLine + 1; |
| |
| i = toIndex; |
| if (finishPackage) { |
| fromLine = toLine; |
| } |
| if (i >= fileChunks.size() - 1) { |
| break; |
| } |
| } |
| } else { |
| // Split the complete package/module in a file |
| std::string content; |
| unsigned int packagelastLine = fileChunks[i].m_toLine; |
| unsigned int toLine = fileChunks[i].m_toLine + 1; |
| if (i == fileChunks.size() - 1) { |
| toLine = allLines.size() - 1; |
| } |
| const char* temp = allLines[toLine].c_str(); |
| if (strstr(temp, "/*") && (!strstr(temp, "*/"))) { |
| m_splitFiles.clear(); |
| m_lineOffsets.clear(); |
| Location loc(0, 0, 0, |
| m_clp->getSymbolTable()->registerSymbol(m_fileName)); |
| Error err(ErrorDefinition::PA_CANNOT_SPLIT_FILE, loc); |
| m_clp->getErrorContainer()->addError(err); |
| m_clp->getErrorContainer()->printMessages(); |
| return; |
| } |
| |
| if (i == fileChunks.size() - 1) { |
| toLine = allLines.size(); |
| } |
| |
| unsigned int origFromLine = 0; |
| std::string origFile; |
| |
| m_lineOffsets.push_back(linesWriten); |
| |
| content += setSLlineDirective_(fromLine, origFromLine, origFile); |
| for (unsigned int l = fromLine; l < toLine; l++) { |
| checkSLlineDirective_(allLines[l], l); |
| content += allLines[l]; |
| if (l != (toLine - 1)) { |
| content += "\n"; |
| } |
| linesWriten++; |
| } |
| |
| std::string splitFileName = |
| m_ppFileName + ".ck" + std::to_string(chunkNb); |
| if (chunkNb > 1000) { |
| m_splitFiles.clear(); |
| m_lineOffsets.clear(); |
| Location loc(0, 0, 0, |
| m_clp->getSymbolTable()->registerSymbol(m_fileName)); |
| Error err(ErrorDefinition::PA_CANNOT_SPLIT_FILE, loc); |
| m_clp->getErrorContainer()->addError(err); |
| m_clp->getErrorContainer()->printMessages(); |
| return; |
| } |
| saveContent(splitFileName, content); |
| |
| m_splitFiles.push_back(splitFileName); |
| |
| // m_lineOffsets.push_back (fromLine-1); |
| |
| chunkNb++; |
| for (unsigned int j = i; j < fileChunks.size(); j++) { |
| if ((fileChunks[j].m_toLine > packagelastLine)) { |
| break; |
| } |
| if (j == (fileChunks.size() - 1)) { |
| toIndex = j; |
| break; |
| } |
| toIndex = j; |
| } |
| |
| fromLine = packagelastLine + 1; |
| i = toIndex; |
| } |
| } |
| // The case of classes and other chunks |
| else { |
| for (unsigned int j = i; j < fileChunks.size(); j++) { |
| if (fileChunks[j].m_chunkType == DesignElement::ElemType::Package) { |
| break; |
| } |
| if ((fileChunks[j].m_toLine - fromLine) >= chunkSize) { |
| toIndex = j; |
| break; |
| } |
| if (j == (fileChunks.size() - 1)) { |
| toIndex = j; |
| break; |
| } |
| toIndex = j; |
| } |
| |
| std::string content; |
| unsigned int toLine = fileChunks[toIndex].m_toLine + 1; |
| if (toIndex == fileChunks.size() - 1) { |
| toLine = allLines.size(); |
| } |
| unsigned int origFromLine = 0; |
| std::string origFile; |
| |
| m_lineOffsets.push_back(linesWriten); |
| |
| content += setSLlineDirective_(fromLine, origFromLine, origFile); |
| content += " " + fileLevelImportSection; |
| for (unsigned int l = fromLine; l < toLine; l++) { |
| checkSLlineDirective_(allLines[l], l); |
| content += allLines[l]; |
| if (l != (toLine - 1)) { |
| content += "\n"; |
| } |
| linesWriten++; |
| } |
| |
| std::string splitFileName = |
| m_ppFileName + ".ck" + std::to_string(chunkNb); |
| if (chunkNb > 1000) { |
| m_splitFiles.clear(); |
| m_lineOffsets.clear(); |
| Location loc(0, 0, 0, |
| m_clp->getSymbolTable()->registerSymbol(m_fileName)); |
| Error err(ErrorDefinition::PA_CANNOT_SPLIT_FILE, loc); |
| m_clp->getErrorContainer()->addError(err); |
| m_clp->getErrorContainer()->printMessages(); |
| return; |
| } |
| saveContent(splitFileName, content); |
| |
| m_splitFiles.push_back(splitFileName); |
| |
| chunkNb++; |
| |
| fromLine = fileChunks[toIndex].m_toLine + 1; |
| i = toIndex; |
| } |
| } |
| } |