Release Vulpes OS 2.7-preview.22
This commit is contained in:
parent
200d1fb235
commit
a138f97877
294 changed files with 515786 additions and 187 deletions
81
tundra/tools/kaios_debug.py
Normal file
81
tundra/tools/kaios_debug.py
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
# SPDX-License-Identifier: MPL-2.0
|
||||
"""Small client for the Gecko 48 debugger exposed by KaiOS over ADB."""
|
||||
import json
|
||||
import socket
|
||||
import subprocess
|
||||
|
||||
|
||||
class KaiOSDebugger:
|
||||
def __init__(self, serial):
|
||||
self.adb = ['adb', '-s', serial]
|
||||
self.port = None
|
||||
self.socket = None
|
||||
|
||||
def __enter__(self):
|
||||
self.port = subprocess.check_output(self.adb + [
|
||||
'forward', 'tcp:0', 'localfilesystem:/data/local/debugger-socket'],
|
||||
text=True, timeout=15).strip()
|
||||
try:
|
||||
self.socket = socket.create_connection(('127.0.0.1', int(self.port)), timeout=20)
|
||||
self.greeting = self.receive()
|
||||
self.root = self.request('root', 'listTabs')
|
||||
return self
|
||||
except BaseException:
|
||||
self.__exit__(None, None, None)
|
||||
raise
|
||||
|
||||
def __exit__(self, *unused):
|
||||
if self.socket:
|
||||
self.socket.close()
|
||||
if self.port:
|
||||
subprocess.run(self.adb + ['forward', '--remove', 'tcp:' + self.port],
|
||||
check=True, timeout=15)
|
||||
|
||||
def receive(self):
|
||||
prefix = bytearray()
|
||||
while True:
|
||||
byte = self.socket.recv(1)
|
||||
if not byte:
|
||||
raise EOFError('KaiOS debugger disconnected')
|
||||
if byte == b':':
|
||||
break
|
||||
prefix.extend(byte)
|
||||
if len(prefix) > 12:
|
||||
raise ValueError('Invalid debugger packet header')
|
||||
remaining = int(prefix)
|
||||
if remaining > 16 * 1024 * 1024:
|
||||
raise ValueError('Debugger packet exceeds diagnostic limit')
|
||||
data = bytearray()
|
||||
while remaining:
|
||||
part = self.socket.recv(remaining)
|
||||
if not part:
|
||||
raise EOFError('Incomplete debugger packet')
|
||||
data.extend(part)
|
||||
remaining -= len(part)
|
||||
return json.loads(data)
|
||||
|
||||
def request(self, actor, kind, **fields):
|
||||
body = json.dumps(dict(to=actor, type=kind, **fields)).encode()
|
||||
self.socket.sendall(str(len(body)).encode() + b':' + body)
|
||||
while True:
|
||||
result = self.receive()
|
||||
if result.get('from') != actor or result.get('type') in (
|
||||
'consoleAPICall', 'pageError', 'networkEvent', 'newGlobal'):
|
||||
continue
|
||||
if 'error' in result:
|
||||
raise RuntimeError(json.dumps(result))
|
||||
return result
|
||||
|
||||
def evaluate(self, console, expression):
|
||||
result = self.request(console, 'evaluateJS', text=expression)
|
||||
if result.get('exceptionMessage'):
|
||||
raise RuntimeError(str(result['exceptionMessage']))
|
||||
value = result.get('result')
|
||||
if isinstance(value, dict) and value.get('type') == 'longString':
|
||||
actor = value['actor']
|
||||
try:
|
||||
value = self.request(actor, 'substring', start=0,
|
||||
end=value['length'])['substring']
|
||||
finally:
|
||||
self.request(actor, 'release')
|
||||
return value
|
||||
Loading…
Reference in a new issue