|
29 | 29 |
|
30 | 30 | """ |
31 | 31 |
|
| 32 | +import ctypes |
32 | 33 | import re |
33 | 34 | import string |
34 | 35 | import struct |
@@ -90,6 +91,10 @@ def __lldb_init_module(debugger, dict): |
90 | 91 | debugger.HandleCommand('type summary add --expand -F lldb_webkit.WTFOptionSet_SummaryProvider -x "^WTF::OptionSet<.+>$"') |
91 | 92 | debugger.HandleCommand('type summary add --expand -F lldb_webkit.WTFCompactPointerTuple_SummaryProvider -x "^WTF::CompactPointerTuple<.+,.+>$"') |
92 | 93 |
|
| 94 | + debugger.HandleCommand('type summary add --expand -F lldb_webkit.SummarizeEncodedJSValue JSC::EncodedJSValue') |
| 95 | + debugger.HandleCommand('type summary add --expand -F lldb_webkit.SummarizeJSString JSC::JSString') |
| 96 | + debugger.HandleCommand('type summary add --expand -F lldb_webkit.SummarizeJSValue JSC::JSValue') |
| 97 | + |
93 | 98 | debugger.HandleCommand('type summary add -F lldb_webkit.WTFURL_SummaryProvider WTF::URL') |
94 | 99 | debugger.HandleCommand('type summary add -F lldb_webkit.WebCoreColor_SummaryProvider WebCore::Color') |
95 | 100 |
|
@@ -1301,3 +1306,200 @@ def isIndefinite(self): |
1301 | 1306 |
|
1302 | 1307 | def hasDoubleValue(self): |
1303 | 1308 | return self.valobj.GetChildMemberWithName('m_timeFlags').GetValueAsSigned(0) & (1 << 5) |
| 1309 | + |
| 1310 | + |
| 1311 | +def SummarizeJSValue(valobj, dict): |
| 1312 | + mirror = JSValueMirror(valobj) |
| 1313 | + return mirror.summary() |
| 1314 | + |
| 1315 | + |
| 1316 | +def SummarizeEncodedJSValue(valobj, dict): |
| 1317 | + mirror = JSValueMirror(valobj, valobj.GetValueAsUnsigned(0)) |
| 1318 | + return mirror.summary() |
| 1319 | + |
| 1320 | + |
| 1321 | +def SummarizeJSString(valobj, dict): |
| 1322 | + mirror = JSStringMirror(valobj) |
| 1323 | + return mirror.summary() |
| 1324 | + |
| 1325 | + |
| 1326 | +class JSValueMirror: |
| 1327 | + """Reflects on a JSValue or EncodedJSValue.""" |
| 1328 | + |
| 1329 | + OTHER_TAG = 0b0010 |
| 1330 | + BOOL_TAG = 0b0100 |
| 1331 | + UNDEFINED_TAG = 0b1000 |
| 1332 | + |
| 1333 | + NATIVE_CALLEE_TAG = OTHER_TAG | 1 |
| 1334 | + NATIVE_CALLEE_MASK = 0b0111 |
| 1335 | + |
| 1336 | + TOP_15_BITS_MASK = 0xFFFE << 48 |
| 1337 | + TOP_15_BITS_POINTER_TAG = 0 |
| 1338 | + TOP_15_BITS_INTEGER_TAG = 0xFFFE << 48 |
| 1339 | + |
| 1340 | + NUMBER_TAG = 0xFFFE000000000000 |
| 1341 | + MISC_TAG = OTHER_TAG | BOOL_TAG | UNDEFINED_TAG |
| 1342 | + |
| 1343 | + NOT_CELL_MASK = NUMBER_TAG | OTHER_TAG |
| 1344 | + |
| 1345 | + VALUE_NULL = OTHER_TAG |
| 1346 | + VALUE_UNDEFINED = OTHER_TAG | UNDEFINED_TAG |
| 1347 | + VALUE_TRUE = OTHER_TAG | BOOL_TAG | 1 |
| 1348 | + VALUE_FALSE = OTHER_TAG | BOOL_TAG | 0 |
| 1349 | + |
| 1350 | + VALUE_EMPTY = 0 |
| 1351 | + VALUE_DELETED = 4 |
| 1352 | + |
| 1353 | + def __init__(self, valobj, bits=None): |
| 1354 | + assert valobj.GetTypeName() == "JSC::JSValue" or valobj.GetTypeName() == "JSC::EncodedJSValue" |
| 1355 | + self.valobj = valobj |
| 1356 | + self.bits = bits if bits is not None else valobj.GetChildMemberWithName("u").GetChildMemberWithName("asInt64").GetValueAsUnsigned(0) |
| 1357 | + |
| 1358 | + def isNull(self): |
| 1359 | + return self.bits == self.VALUE_NULL |
| 1360 | + |
| 1361 | + def isUndefined(self): |
| 1362 | + return self.bits == self.VALUE_UNDEFINED |
| 1363 | + |
| 1364 | + def isEmpty(self): |
| 1365 | + return self.bits == self.VALUE_EMPTY |
| 1366 | + |
| 1367 | + def isBool(self): |
| 1368 | + return self.bits & ~1 == self.VALUE_FALSE |
| 1369 | + |
| 1370 | + def isTrue(self): |
| 1371 | + return self.bits == self.VALUE_TRUE |
| 1372 | + |
| 1373 | + def isFalse(self): |
| 1374 | + return self.bits == self.VALUE_TRUE |
| 1375 | + |
| 1376 | + def isInt32(self): |
| 1377 | + return self.bits & self.NUMBER_TAG == self.NUMBER_TAG |
| 1378 | + |
| 1379 | + def isNumber(self): |
| 1380 | + return self.bits & self.NUMBER_TAG != 0 |
| 1381 | + |
| 1382 | + def isDouble(self): |
| 1383 | + return self.isNumber() and not self.isInt32() |
| 1384 | + |
| 1385 | + def isCell(self): |
| 1386 | + return self.bits & self.NOT_CELL_MASK == 0 |
| 1387 | + |
| 1388 | + def toInt32(self): |
| 1389 | + assert self.isInt32() |
| 1390 | + masked = self.bits & 0xFFFFFFFF |
| 1391 | + if masked > 0x80000000: |
| 1392 | + return masked - 0x100000000 # => -1 |
| 1393 | + else: |
| 1394 | + return masked |
| 1395 | + |
| 1396 | + def toDouble(self): |
| 1397 | + assert self.isDouble() |
| 1398 | + corrected = self.bits - (1 << 49) |
| 1399 | + return struct.unpack('<d', corrected.to_bytes(8, 'little'))[0] |
| 1400 | + |
| 1401 | + def summary(self): |
| 1402 | + if self.isCell(): |
| 1403 | + cell = castSBValueToPointerType("JSC::JSCell", self.valobj) |
| 1404 | + mirror = JSCellMirror(cell, self.bits) |
| 1405 | + return mirror.summary() |
| 1406 | + if self.isNull(): |
| 1407 | + return "{JS null}" |
| 1408 | + if self.isUndefined(): |
| 1409 | + return "{JS undefined}" |
| 1410 | + if self.isTrue(): |
| 1411 | + return "{JS true}" |
| 1412 | + if self.isFalse(): |
| 1413 | + return "{JS false}" |
| 1414 | + if self.isEmpty(): |
| 1415 | + return "{JS empty}" |
| 1416 | + if self.isInt32(): |
| 1417 | + return f"{{JS int32={self.toInt32()}}}" |
| 1418 | + if self.isDouble(): |
| 1419 | + return f"{{JS double={self.toDouble()}}}" |
| 1420 | + return "{JSC ?}" |
| 1421 | + |
| 1422 | + |
| 1423 | +class JSCellMirror: |
| 1424 | + def __init__(self, valobj, bits): |
| 1425 | + assert valobj.GetTypeName() == "JSC::JSCell *" |
| 1426 | + self.valobj = valobj |
| 1427 | + self.bits = bits |
| 1428 | + self.type = valobj.GetChildMemberWithName("m_type").GetValueAsUnsigned(0) |
| 1429 | + typeEnum = valobj.GetTarget().FindFirstType("JSC::JSType") |
| 1430 | + self.enumMembers = typeEnum.GetEnumMembers() |
| 1431 | + |
| 1432 | + def enumMemberValue(self, enumMemberName): |
| 1433 | + return self.enumMembers[enumMemberName].unsigned |
| 1434 | + |
| 1435 | + def enumMemberName(self, enumMemberValue): |
| 1436 | + # try bisecting first, but it that fails give it another try linearly just to be sure |
| 1437 | + index = bisect_right(self.enumMembers, enumMemberValue, key=lambda mem: mem.unsigned) |
| 1438 | + guess = self.enumMembers[index - 1] |
| 1439 | + if guess.unsigned == enumMemberValue: |
| 1440 | + return guess.name |
| 1441 | + for member in self.enumMembers: |
| 1442 | + if member.unsigned == enumMemberValue: |
| 1443 | + return member.name |
| 1444 | + return "?" |
| 1445 | + |
| 1446 | + def isString(self): |
| 1447 | + return self.type == self.enumMemberValue("StringType") |
| 1448 | + |
| 1449 | + def isObject(self): |
| 1450 | + return self.type >= self.enumMemberValue("ObjectType") |
| 1451 | + |
| 1452 | + def isCustomGetterSetter(self): |
| 1453 | + return self.type == self.enumMemberValue("CustomGetterSetterType") |
| 1454 | + |
| 1455 | + def summary(self): |
| 1456 | + if self.isString(): |
| 1457 | + string = castSBValueToPointerType("JSC::JSString", self.valobj) |
| 1458 | + mirror = JSStringMirror(string) |
| 1459 | + return mirror.summary(verbose=False) |
| 1460 | + if self.isObject(): |
| 1461 | + return "{JS object %s}" % (self.enumMemberName(self.type)) |
| 1462 | + return "{{JS cell %s}}}" % (self.enumMemberName(self.type)) |
| 1463 | + |
| 1464 | + |
| 1465 | +class JSStringMirror: |
| 1466 | + def __init__(self, valobj): |
| 1467 | + assert valobj.GetTypeName() == "JSC::JSString *" |
| 1468 | + self.fiber = valobj.GetChildMemberWithName("m_fiber") |
| 1469 | + |
| 1470 | + def isRopeString(self): |
| 1471 | + return self.fiber.GetValueAsUnsigned(0) & 1 != 0 |
| 1472 | + |
| 1473 | + def summary(self, verbose=True): |
| 1474 | + provider = WTFStringImplProvider(self.fiber, {}) |
| 1475 | + if not provider.is_initialized(): |
| 1476 | + raise RuntimeError("null fiber pointer in JSString mirror") |
| 1477 | + contents = self.maybeTrim(provider.to_string()) |
| 1478 | + if verbose: |
| 1479 | + bitCount = 8 if provider.is_8bit() else 16 |
| 1480 | + if self.isRopeString(): |
| 1481 | + return '{JS ropeString bits=%d fiber1 length=%d %s ...}' % (bitCount, provider.get_length(), contents) |
| 1482 | + else: |
| 1483 | + return '{JS string bits=%d length=%d %s}' % (bitCount, provider.get_length(), contents) |
| 1484 | + else: |
| 1485 | + if self.isRopeString(): |
| 1486 | + return '{JS ropeString fiber1=%s}' % (contents) |
| 1487 | + else: |
| 1488 | + return '{JS string %s}' % (contents) |
| 1489 | + |
| 1490 | + def maybeTrim(self, string): |
| 1491 | + escaped = repr(string) |
| 1492 | + if len(escaped) > 90: |
| 1493 | + return f'{escaped[:80]} [... trimmed]' |
| 1494 | + else: |
| 1495 | + return escaped |
| 1496 | + |
| 1497 | + |
| 1498 | +def castSBValueToPointerType(typeName, value): |
| 1499 | + ptrType = value.GetTarget().FindFirstType(typeName).GetPointerType() |
| 1500 | + return value.Cast(ptrType) |
| 1501 | + |
| 1502 | + |
| 1503 | +def hexPointerString(bits): |
| 1504 | + """Convert bits, which is an unsigned integer, to a string that would be produced by %p in C.""" |
| 1505 | + return f"{bits:#0{2 + ctypes.sizeof(ctypes.c_void_p) * 2}x}" |
0 commit comments