OLD | NEW |
| (Empty) |
1 // Copyright 2016 The Chromium 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 "platform/v8_inspector/V8StringUtil.h" | |
6 | |
7 #include "platform/inspector_protocol/InspectorProtocol.h" | |
8 #include "platform/v8_inspector/V8InspectorImpl.h" | |
9 #include "platform/v8_inspector/V8InspectorSessionImpl.h" | |
10 #include "platform/v8_inspector/V8Regex.h" | |
11 | |
12 namespace v8_inspector { | |
13 | |
14 namespace { | |
15 | |
16 String16 findMagicComment(const String16& content, const String16& name, bool mu
ltiline) | |
17 { | |
18 DCHECK(name.find("=") == String16::kNotFound); | |
19 unsigned length = content.length(); | |
20 unsigned 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) | |
28 return String16(); | |
29 | |
30 // Check for a /\/[\/*][@#][ \t]/ regexp (length of 4) before found name
. | |
31 if (pos < 4) | |
32 return String16(); | |
33 pos -= 4; | |
34 if (content[pos] != '/') | |
35 continue; | |
36 if ((content[pos + 1] != '/' || multiline) | |
37 && (content[pos + 1] != '*' || !multiline)) | |
38 continue; | |
39 if (content[pos + 2] != '#' && content[pos + 2] != '@') | |
40 continue; | |
41 if (content[pos + 3] != ' ' && content[pos + 3] != '\t') | |
42 continue; | |
43 equalSignPos = pos + 4 + nameLength; | |
44 if (equalSignPos < length && content[equalSignPos] != '=') | |
45 continue; | |
46 if (multiline) { | |
47 closingCommentPos = content.find("*/", equalSignPos + 1); | |
48 if (closingCommentPos == String16::kNotFound) | |
49 return String16(); | |
50 } | |
51 | |
52 break; | |
53 } | |
54 | |
55 DCHECK(equalSignPos); | |
56 DCHECK(!multiline || closingCommentPos); | |
57 size_t urlPos = equalSignPos + 1; | |
58 String16 match = multiline | |
59 ? content.substring(urlPos, closingCommentPos - urlPos) | |
60 : content.substring(urlPos); | |
61 | |
62 size_t newLine = match.find("\n"); | |
63 if (newLine != String16::kNotFound) | |
64 match = match.substring(0, newLine); | |
65 match = match.stripWhiteSpace(); | |
66 | |
67 for (unsigned i = 0; i < match.length(); ++i) { | |
68 UChar c = match[i]; | |
69 if (c == '"' || c == '\'' || c == ' ' || c == '\t') | |
70 return ""; | |
71 } | |
72 | |
73 return match; | |
74 } | |
75 | |
76 String16 createSearchRegexSource(const String16& text) | |
77 { | |
78 String16Builder result; | |
79 | |
80 for (unsigned i = 0; i < text.length(); i++) { | |
81 UChar c = text[i]; | |
82 if (c == '[' || c == ']' || c == '(' || c == ')' || c == '{' || c == '}' | |
83 || c == '+' || c == '-' || c == '*' || c == '.' || c == ',' || c ==
'?' | |
84 || c == '\\' || c == '^' || c == '$' || c == '|') { | |
85 result.append('\\'); | |
86 } | |
87 result.append(text[i]); | |
88 } | |
89 | |
90 return result.toString(); | |
91 } | |
92 | |
93 std::unique_ptr<std::vector<unsigned>> lineEndings(const String16& text) | |
94 { | |
95 std::unique_ptr<std::vector<unsigned>> result(new std::vector<unsigned>()); | |
96 | |
97 const String16 lineEndString = "\n"; | |
98 unsigned start = 0; | |
99 while (start < text.length()) { | |
100 size_t lineEnd = text.find(lineEndString, start); | |
101 if (lineEnd == String16::kNotFound) | |
102 break; | |
103 | |
104 result->push_back(static_cast<unsigned>(lineEnd)); | |
105 start = lineEnd + 1; | |
106 } | |
107 result->push_back(text.length()); | |
108 | |
109 return result; | |
110 } | |
111 | |
112 std::vector<std::pair<int, String16>> scriptRegexpMatchesByLines(const V8Regex&
regex, const String16& text) | |
113 { | |
114 std::vector<std::pair<int, String16>> result; | |
115 if (text.isEmpty()) | |
116 return result; | |
117 | |
118 std::unique_ptr<std::vector<unsigned>> endings(lineEndings(text)); | |
119 unsigned size = endings->size(); | |
120 unsigned start = 0; | |
121 for (unsigned lineNumber = 0; lineNumber < size; ++lineNumber) { | |
122 unsigned lineEnd = endings->at(lineNumber); | |
123 String16 line = text.substring(start, lineEnd - start); | |
124 if (line.length() && line[line.length() - 1] == '\r') | |
125 line = line.substring(0, line.length() - 1); | |
126 | |
127 int matchLength; | |
128 if (regex.match(line, 0, &matchLength) != -1) | |
129 result.push_back(std::pair<int, String16>(lineNumber, line)); | |
130 | |
131 start = lineEnd + 1; | |
132 } | |
133 return result; | |
134 } | |
135 | |
136 std::unique_ptr<protocol::Debugger::SearchMatch> buildObjectForSearchMatch(int l
ineNumber, const String16& lineContent) | |
137 { | |
138 return protocol::Debugger::SearchMatch::create() | |
139 .setLineNumber(lineNumber) | |
140 .setLineContent(lineContent) | |
141 .build(); | |
142 } | |
143 | |
144 std::unique_ptr<V8Regex> createSearchRegex(V8InspectorImpl* inspector, const Str
ing16& query, bool caseSensitive, bool isRegex) | |
145 { | |
146 String16 regexSource = isRegex ? query : createSearchRegexSource(query); | |
147 return wrapUnique(new V8Regex(inspector, regexSource, caseSensitive)); | |
148 } | |
149 | |
150 } // namespace | |
151 | |
152 v8::Local<v8::String> toV8String(v8::Isolate* isolate, const String16& string) | |
153 { | |
154 if (string.isEmpty()) | |
155 return v8::String::Empty(isolate); | |
156 return v8::String::NewFromTwoByte(isolate, reinterpret_cast<const uint16_t*>
(string.characters16()), v8::NewStringType::kNormal, string.length()).ToLocalChe
cked(); | |
157 } | |
158 | |
159 v8::Local<v8::String> toV8StringInternalized(v8::Isolate* isolate, const String1
6& string) | |
160 { | |
161 if (string.isEmpty()) | |
162 return v8::String::Empty(isolate); | |
163 return v8::String::NewFromTwoByte(isolate, reinterpret_cast<const uint16_t*>
(string.characters16()), v8::NewStringType::kInternalized, string.length()).ToLo
calChecked(); | |
164 } | |
165 | |
166 v8::Local<v8::String> toV8StringInternalized(v8::Isolate* isolate, const char* s
tr) | |
167 { | |
168 return v8::String::NewFromUtf8(isolate, str, v8::NewStringType::kInternalize
d).ToLocalChecked(); | |
169 } | |
170 | |
171 v8::Local<v8::String> toV8String(v8::Isolate* isolate, const StringView& string) | |
172 { | |
173 if (!string.length()) | |
174 return v8::String::Empty(isolate); | |
175 if (string.is8Bit()) | |
176 return v8::String::NewFromOneByte(isolate, reinterpret_cast<const uint8_
t*>(string.characters8()), v8::NewStringType::kNormal, string.length()).ToLocalC
hecked(); | |
177 return v8::String::NewFromTwoByte(isolate, reinterpret_cast<const uint16_t*>
(string.characters16()), v8::NewStringType::kNormal, string.length()).ToLocalChe
cked(); | |
178 } | |
179 | |
180 String16 toProtocolString(v8::Local<v8::String> value) | |
181 { | |
182 if (value.IsEmpty() || value->IsNull() || value->IsUndefined()) | |
183 return String16(); | |
184 std::unique_ptr<UChar[]> buffer(new UChar[value->Length()]); | |
185 value->Write(reinterpret_cast<uint16_t*>(buffer.get()), 0, value->Length()); | |
186 return String16(buffer.get(), value->Length()); | |
187 } | |
188 | |
189 String16 toProtocolStringWithTypeCheck(v8::Local<v8::Value> value) | |
190 { | |
191 if (value.IsEmpty() || !value->IsString()) | |
192 return String16(); | |
193 return toProtocolString(value.As<v8::String>()); | |
194 } | |
195 | |
196 String16 toString16(const StringView& string) | |
197 { | |
198 if (!string.length()) | |
199 return String16(); | |
200 if (string.is8Bit()) | |
201 return String16(reinterpret_cast<const char*>(string.characters8()), str
ing.length()); | |
202 return String16(reinterpret_cast<const UChar*>(string.characters16()), strin
g.length()); | |
203 } | |
204 | |
205 StringView toStringView(const String16& string) | |
206 { | |
207 if (string.isEmpty()) | |
208 return StringView(); | |
209 return StringView(reinterpret_cast<const uint16_t*>(string.characters16()),
string.length()); | |
210 } | |
211 | |
212 bool stringViewStartsWith(const StringView& string, const char* prefix) | |
213 { | |
214 if (!string.length()) | |
215 return !(*prefix); | |
216 if (string.is8Bit()) { | |
217 for (size_t i = 0, j = 0; prefix[j] && i < string.length(); ++i, ++j) { | |
218 if (string.characters8()[i] != prefix[j]) | |
219 return false; | |
220 } | |
221 } else { | |
222 for (size_t i = 0, j = 0; prefix[j] && i < string.length(); ++i, ++j) { | |
223 if (string.characters16()[i] != prefix[j]) | |
224 return false; | |
225 } | |
226 } | |
227 return true; | |
228 } | |
229 | |
230 std::unique_ptr<protocol::Value> parseJSON(const StringView& string) | |
231 { | |
232 if (!string.length()) | |
233 return nullptr; | |
234 if (string.is8Bit()) | |
235 return blink::protocol::parseJSON(string.characters8(), string.length())
; | |
236 return blink::protocol::parseJSON(string.characters16(), string.length()); | |
237 } | |
238 | |
239 std::vector<std::unique_ptr<protocol::Debugger::SearchMatch>> searchInTextByLine
sImpl(V8InspectorSession* session, const String16& text, const String16& query,
const bool caseSensitive, const bool isRegex) | |
240 { | |
241 std::unique_ptr<V8Regex> regex = createSearchRegex(static_cast<V8InspectorSe
ssionImpl*>(session)->inspector(), query, caseSensitive, isRegex); | |
242 std::vector<std::pair<int, String16>> matches = scriptRegexpMatchesByLines(*
regex.get(), text); | |
243 | |
244 std::vector<std::unique_ptr<protocol::Debugger::SearchMatch>> result; | |
245 for (const auto& match : matches) | |
246 result.push_back(buildObjectForSearchMatch(match.first, match.second)); | |
247 return result; | |
248 } | |
249 | |
250 String16 findSourceURL(const String16& content, bool multiline) | |
251 { | |
252 return findMagicComment(content, "sourceURL", multiline); | |
253 } | |
254 | |
255 String16 findSourceMapURL(const String16& content, bool multiline) | |
256 { | |
257 return findMagicComment(content, "sourceMappingURL", multiline); | |
258 } | |
259 | |
260 std::unique_ptr<protocol::Value> toProtocolValue(v8::Local<v8::Context> context,
v8::Local<v8::Value> value, int maxDepth) | |
261 { | |
262 if (value.IsEmpty()) { | |
263 NOTREACHED(); | |
264 return nullptr; | |
265 } | |
266 | |
267 if (!maxDepth) | |
268 return nullptr; | |
269 maxDepth--; | |
270 | |
271 if (value->IsNull() || value->IsUndefined()) | |
272 return protocol::Value::null(); | |
273 if (value->IsBoolean()) | |
274 return protocol::FundamentalValue::create(value.As<v8::Boolean>()->Value
()); | |
275 if (value->IsNumber()) { | |
276 double doubleValue = value.As<v8::Number>()->Value(); | |
277 int intValue = static_cast<int>(doubleValue); | |
278 if (intValue == doubleValue) | |
279 return protocol::FundamentalValue::create(intValue); | |
280 return protocol::FundamentalValue::create(doubleValue); | |
281 } | |
282 if (value->IsString()) | |
283 return protocol::StringValue::create(toProtocolString(value.As<v8::Strin
g>())); | |
284 if (value->IsArray()) { | |
285 v8::Local<v8::Array> array = value.As<v8::Array>(); | |
286 std::unique_ptr<protocol::ListValue> inspectorArray = protocol::ListValu
e::create(); | |
287 uint32_t length = array->Length(); | |
288 for (uint32_t i = 0; i < length; i++) { | |
289 v8::Local<v8::Value> value; | |
290 if (!array->Get(context, i).ToLocal(&value)) | |
291 return nullptr; | |
292 std::unique_ptr<protocol::Value> element = toProtocolValue(context,
value, maxDepth); | |
293 if (!element) | |
294 return nullptr; | |
295 inspectorArray->pushValue(std::move(element)); | |
296 } | |
297 return std::move(inspectorArray); | |
298 } | |
299 if (value->IsObject()) { | |
300 std::unique_ptr<protocol::DictionaryValue> jsonObject = protocol::Dictio
naryValue::create(); | |
301 v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(value); | |
302 v8::Local<v8::Array> propertyNames; | |
303 if (!object->GetPropertyNames(context).ToLocal(&propertyNames)) | |
304 return nullptr; | |
305 uint32_t length = propertyNames->Length(); | |
306 for (uint32_t i = 0; i < length; i++) { | |
307 v8::Local<v8::Value> name; | |
308 if (!propertyNames->Get(context, i).ToLocal(&name)) | |
309 return nullptr; | |
310 // FIXME(yurys): v8::Object should support GetOwnPropertyNames | |
311 if (name->IsString()) { | |
312 v8::Maybe<bool> hasRealNamedProperty = object->HasRealNamedPrope
rty(context, v8::Local<v8::String>::Cast(name)); | |
313 if (!hasRealNamedProperty.IsJust() || !hasRealNamedProperty.From
Just()) | |
314 continue; | |
315 } | |
316 v8::Local<v8::String> propertyName; | |
317 if (!name->ToString(context).ToLocal(&propertyName)) | |
318 continue; | |
319 v8::Local<v8::Value> property; | |
320 if (!object->Get(context, name).ToLocal(&property)) | |
321 return nullptr; | |
322 std::unique_ptr<protocol::Value> propertyValue = toProtocolValue(con
text, property, maxDepth); | |
323 if (!propertyValue) | |
324 return nullptr; | |
325 jsonObject->setValue(toProtocolString(propertyName), std::move(prope
rtyValue)); | |
326 } | |
327 return std::move(jsonObject); | |
328 } | |
329 NOTREACHED(); | |
330 return nullptr; | |
331 } | |
332 | |
333 // static | |
334 std::unique_ptr<StringBuffer> StringBuffer::create(const StringView& string) | |
335 { | |
336 String16 owner = toString16(string); | |
337 return StringBufferImpl::adopt(owner); | |
338 } | |
339 | |
340 // static | |
341 std::unique_ptr<StringBufferImpl> StringBufferImpl::adopt(String16& string) | |
342 { | |
343 return wrapUnique(new StringBufferImpl(string)); | |
344 } | |
345 | |
346 StringBufferImpl::StringBufferImpl(String16& string) | |
347 { | |
348 m_owner.swap(string); | |
349 m_string = toStringView(m_owner); | |
350 } | |
351 | |
352 } // namespace v8_inspector | |
OLD | NEW |