1// Copyright 2016 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "src/inspector/search-util.h" 6 7#include "src/inspector/protocol/Protocol.h" 8#include "src/inspector/v8-inspector-impl.h" 9#include "src/inspector/v8-inspector-session-impl.h" 10#include "src/inspector/v8-regex.h" 11 12namespace v8_inspector { 13 14namespace { 15 16String16 findMagicComment(const String16& content, const String16& name, 17 bool multiline) { 18 DCHECK_EQ(String16::kNotFound, name.find("=")); 19 size_t length = content.length(); 20 size_t nameLength = name.length(); 21 22 size_t pos = length; 23 size_t equalSignPos = 0; 24 size_t closingCommentPos = 0; 25 while (true) { 26 pos = content.reverseFind(name, pos); 27 if (pos == String16::kNotFound) return String16(); 28 29 // Check for a /\/[\/*][@#][ \t]/ regexp (length of 4) before found name. 30 if (pos < 4) return String16(); 31 pos -= 4; 32 if (content[pos] != '/') continue; 33 if ((content[pos + 1] != '/' || multiline) && 34 (content[pos + 1] != '*' || !multiline)) 35 continue; 36 if (content[pos + 2] != '#' && content[pos + 2] != '@') continue; 37 if (content[pos + 3] != ' ' && content[pos + 3] != '\t') continue; 38 equalSignPos = pos + 4 + nameLength; 39 if (equalSignPos >= length) continue; 40 if (content[equalSignPos] != '=') continue; 41 if (multiline) { 42 closingCommentPos = content.find("*/", equalSignPos + 1); 43 if (closingCommentPos == String16::kNotFound) return String16(); 44 } 45 46 break; 47 } 48 49 DCHECK(equalSignPos); 50 DCHECK_LT(equalSignPos, length); 51 DCHECK(!multiline || closingCommentPos); 52 size_t urlPos = equalSignPos + 1; 53 String16 match = multiline 54 ? content.substring(urlPos, closingCommentPos - urlPos) 55 : content.substring(urlPos); 56 57 size_t newLine = match.find("\n"); 58 if (newLine != String16::kNotFound) match = match.substring(0, newLine); 59 match = match.stripWhiteSpace(); 60 61 for (size_t i = 0; i < match.length(); ++i) { 62 UChar c = match[i]; 63 if (c == '"' || c == '\'' || c == ' ' || c == '\t') return ""; 64 } 65 66 return match; 67} 68 69String16 createSearchRegexSource(const String16& text) { 70 String16Builder result; 71 72 for (size_t i = 0; i < text.length(); i++) { 73 UChar c = text[i]; 74 if (c == '[' || c == ']' || c == '(' || c == ')' || c == '{' || c == '}' || 75 c == '+' || c == '-' || c == '*' || c == '.' || c == ',' || c == '?' || 76 c == '\\' || c == '^' || c == '$' || c == '|') { 77 result.append('\\'); 78 } 79 result.append(c); 80 } 81 82 return result.toString(); 83} 84 85std::unique_ptr<std::vector<size_t>> lineEndings(const String16& text) { 86 std::unique_ptr<std::vector<size_t>> result(new std::vector<size_t>()); 87 88 const String16 lineEndString = "\n"; 89 size_t start = 0; 90 while (start < text.length()) { 91 size_t lineEnd = text.find(lineEndString, start); 92 if (lineEnd == String16::kNotFound) break; 93 94 result->push_back(lineEnd); 95 start = lineEnd + 1; 96 } 97 result->push_back(text.length()); 98 99 return result; 100} 101 102std::vector<std::pair<int, String16>> scriptRegexpMatchesByLines( 103 const V8Regex& regex, const String16& text) { 104 std::vector<std::pair<int, String16>> result; 105 if (text.isEmpty()) return result; 106 107 std::unique_ptr<std::vector<size_t>> endings(lineEndings(text)); 108 size_t size = endings->size(); 109 size_t start = 0; 110 for (size_t lineNumber = 0; lineNumber < size; ++lineNumber) { 111 size_t lineEnd = endings->at(lineNumber); 112 String16 line = text.substring(start, lineEnd - start); 113 if (line.length() && line[line.length() - 1] == '\r') 114 line = line.substring(0, line.length() - 1); 115 116 int matchLength; 117 if (regex.match(line, 0, &matchLength) != -1) 118 result.push_back(std::pair<int, String16>(lineNumber, line)); 119 120 start = lineEnd + 1; 121 } 122 return result; 123} 124 125std::unique_ptr<protocol::Debugger::SearchMatch> buildObjectForSearchMatch( 126 int lineNumber, const String16& lineContent) { 127 return protocol::Debugger::SearchMatch::create() 128 .setLineNumber(lineNumber) 129 .setLineContent(lineContent) 130 .build(); 131} 132 133std::unique_ptr<V8Regex> createSearchRegex(V8InspectorImpl* inspector, 134 const String16& query, 135 bool caseSensitive, bool isRegex) { 136 String16 regexSource = isRegex ? query : createSearchRegexSource(query); 137 return std::unique_ptr<V8Regex>( 138 new V8Regex(inspector, regexSource, caseSensitive)); 139} 140 141} // namespace 142 143std::vector<std::unique_ptr<protocol::Debugger::SearchMatch>> 144searchInTextByLinesImpl(V8InspectorSession* session, const String16& text, 145 const String16& query, const bool caseSensitive, 146 const bool isRegex) { 147 std::unique_ptr<V8Regex> regex = createSearchRegex( 148 static_cast<V8InspectorSessionImpl*>(session)->inspector(), query, 149 caseSensitive, isRegex); 150 std::vector<std::pair<int, String16>> matches = 151 scriptRegexpMatchesByLines(*regex.get(), text); 152 153 std::vector<std::unique_ptr<protocol::Debugger::SearchMatch>> result; 154 result.reserve(matches.size()); 155 for (const auto& match : matches) 156 result.push_back(buildObjectForSearchMatch(match.first, match.second)); 157 return result; 158} 159 160String16 findSourceURL(const String16& content, bool multiline) { 161 return findMagicComment(content, "sourceURL", multiline); 162} 163 164String16 findSourceMapURL(const String16& content, bool multiline) { 165 return findMagicComment(content, "sourceMappingURL", multiline); 166} 167 168} // namespace v8_inspector 169