dfindexeddb 20240224__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,642 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright 2024 Google LLC
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # https://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ """Parsers for v8 javascript serialized objects."""
16
+ from dataclasses import dataclass
17
+ from datetime import datetime
18
+ import io
19
+ import os
20
+ from typing import Any, BinaryIO, Dict, Optional, Set, Tuple, Union
21
+
22
+ from dfindexeddb import errors
23
+ from dfindexeddb import utils
24
+ from dfindexeddb.indexeddb import definitions
25
+
26
+
27
+ @dataclass
28
+ class BufferArrayView:
29
+ """A parsed Javascript BufferArrayView."""
30
+ buffer: bytes
31
+ tag: definitions.V8ArrayBufferViewTag
32
+ offset: int
33
+ length: int
34
+ flags: int
35
+
36
+
37
+ class JSArray(list):
38
+ """A parsed Javascript array.
39
+
40
+ This is a wrapper around a standard Python list to allow assigning arbitrary
41
+ properties as is possible in the Javascript equivalent.
42
+ """
43
+
44
+ def __repr__(self):
45
+ array_entries = ", ".join([str(entry) for entry in list(self)])
46
+ properties = ", ".join(
47
+ f'{key}: {value}' for key, value in self.properties.items())
48
+ return f'[{array_entries}, {properties}]'
49
+
50
+ @property
51
+ def properties(self) -> Dict[str, Any]:
52
+ """Returns the object properties."""
53
+ return self.__dict__
54
+
55
+ def __contains__(self, item):
56
+ return item in self.__dict__
57
+
58
+ def __getitem__(self, name):
59
+ return self.__dict__[name]
60
+
61
+
62
+ @dataclass
63
+ class Null:
64
+ """A parsed Javascript Null."""
65
+
66
+
67
+ @dataclass
68
+ class RegExp:
69
+ """A parsed Javascript RegExp."""
70
+ pattern: str
71
+ flags: int
72
+
73
+
74
+ @dataclass(frozen=True)
75
+ class Undefined:
76
+ """A parsed Javascript undef."""
77
+
78
+
79
+ class ValueDeserializer:
80
+ """A class to deserialize v8 Javascript values/objects.
81
+
82
+ Attributes:
83
+ decoder (utils.StreamDecoder): A StreamDecoder that wraps the binary
84
+ stream containing the serialized Javascript value/object.
85
+ delegate: An instance to delegate deserializing an object
86
+ next_id (int): the next ID to use for a deserialized object.
87
+ objects (dict[int, Any]): a dictionary mapping integer IDs to a
88
+ deserialized object.
89
+ version (int): the version of the serialized Javascript value/object
90
+ """
91
+
92
+ LATEST_VERSION = 15
93
+
94
+ def __init__(self, stream: BinaryIO, delegate):
95
+ """Initializes a ValueDeserializer.
96
+
97
+ Args:
98
+ stream: a stream of bytes to be deserialized.
99
+ delegate: an object to delegate additional deserialization of additional
100
+ Javascript types/objects.
101
+ """
102
+ self.decoder = utils.StreamDecoder(stream)
103
+ self.delegate = delegate
104
+ self.next_id = 0
105
+ self.objects = {}
106
+ self.version = None
107
+
108
+ def GetWireFormatVersion(self) -> int:
109
+ """Returns the underlying wire format version.
110
+
111
+ Note: Return value is only valid after a call to ReadHeader.
112
+ """
113
+ return self.version
114
+
115
+ def ReadHeader(self) -> bool:
116
+ """Reads the header and returns True if the version is supported."""
117
+ if self._ReadTag() != definitions.V8SerializationTag.VERSION:
118
+ return False
119
+ _, self.version = self.decoder.DecodeUint32Varint()
120
+ if self.version > self.LATEST_VERSION:
121
+ return False
122
+ return True
123
+
124
+ def ReadObjectWrapper(self):
125
+ """Deserializes a V8 object from the current decoder position."""
126
+ original_position = self.decoder.stream.tell()
127
+ result = self._ReadObject()
128
+ if result is None and self.version == 13:
129
+ self.decoder.stream.seek(original_position, os.SEEK_SET)
130
+ result = self._ReadObject()
131
+ raise NotImplementedError('ValueDeserializer.ReadObjectWrapper v13')
132
+ return result
133
+
134
+ def ReadObjectUsingEntireBufferForLegacyFormat(self):
135
+ """Reads an object, consuming the entire buffer."""
136
+ raise NotImplementedError(
137
+ 'ValueDeserializer.ReadObjectUsingEntireBufferForLegacyFormat')
138
+
139
+ def ReadValue(self):
140
+ """Reads the Javascript value."""
141
+ if self.GetWireFormatVersion() > 0:
142
+ return self.ReadObjectWrapper()
143
+ return self.ReadObjectUsingEntireBufferForLegacyFormat()
144
+
145
+ def _PeekTag(self) -> Optional[definitions.V8SerializationTag]:
146
+ """Peeks the next serialization tag from the current decoder position.
147
+
148
+ Returns:
149
+ The serialization tag or None if there is no more bytes to read.
150
+ """
151
+ try:
152
+ _, tag_value = self.decoder.PeekBytes(1)
153
+ except errors.DecoderError:
154
+ return None
155
+ return definitions.V8SerializationTag(tag_value[0])
156
+
157
+ def _ReadTag(self) -> definitions.V8SerializationTag:
158
+ """Returns the next non-padding serialization tag.
159
+
160
+ Raises:
161
+ errors.ParserError: when an invalid v8 tag is read.
162
+ """
163
+ while True:
164
+ _, tag_value = self.decoder.ReadBytes(1)
165
+ try:
166
+ tag = definitions.V8SerializationTag(tag_value[0])
167
+ except ValueError as error:
168
+ raise errors.ParserError(f'Invalid v8 tag value {tag_value}') from error
169
+ if tag != definitions.V8SerializationTag.PADDING:
170
+ return tag
171
+
172
+ def _ConsumeTag(self, peeked_tag: definitions.V8SerializationTag):
173
+ """Consumes the next serialization tag.
174
+
175
+ Args:
176
+ peeked_tag: the tag that is expected to be read from the current position.
177
+
178
+ Raises:
179
+ errors.ParserError: if the peeked tag is not read.
180
+ """
181
+ tag = self._ReadTag()
182
+ if tag != peeked_tag:
183
+ raise errors.ParserError(f'Unexpected tag {tag} found.')
184
+
185
+ def _ReadObject(self):
186
+ """Reads a Javascript object from the current position."""
187
+ _, result = self._ReadObjectInternal()
188
+ tag = self._PeekTag()
189
+ if tag and tag == definitions.V8SerializationTag.ARRAY_BUFFER_VIEW:
190
+ self._ConsumeTag(tag)
191
+ result = self._ReadJSArrayBufferView(result)
192
+ return result
193
+
194
+ def _ReadObjectInternal(self) -> Tuple[definitions.V8SerializationTag, Any]:
195
+ """Reads a Javascript object from the current position.
196
+
197
+ Returns:
198
+ a tuple of the serialization tag and the parsed object.
199
+ """
200
+ tag = self._ReadTag()
201
+ if tag == definitions.V8SerializationTag.VERIFY_OBJECT_COUNT:
202
+ _ = self.decoder.DecodeUint32Varint()
203
+ parsed_object = self._ReadObject()
204
+ elif tag == definitions.V8SerializationTag.UNDEFINED:
205
+ parsed_object = Undefined()
206
+ elif tag == definitions.V8SerializationTag.NULL:
207
+ parsed_object = Null()
208
+ elif tag == definitions.V8SerializationTag.TRUE:
209
+ parsed_object = True
210
+ elif tag == definitions.V8SerializationTag.FALSE:
211
+ parsed_object = False
212
+ elif tag == definitions.V8SerializationTag.INT32:
213
+ _, parsed_object = self.decoder.DecodeInt32Varint()
214
+ elif tag == definitions.V8SerializationTag.UINT32:
215
+ _, parsed_object = self.decoder.DecodeUint32Varint()
216
+ elif tag == definitions.V8SerializationTag.DOUBLE:
217
+ _, parsed_object = self.decoder.DecodeDouble()
218
+ elif tag == definitions.V8SerializationTag.BIGINT:
219
+ parsed_object = self.ReadBigInt()
220
+ elif tag == definitions.V8SerializationTag.UTF8_STRING:
221
+ parsed_object = self.ReadUTF8String()
222
+ elif tag == definitions.V8SerializationTag.ONE_BYTE_STRING:
223
+ parsed_object = self.ReadOneByteString()
224
+ elif tag == definitions.V8SerializationTag.TWO_BYTE_STRING:
225
+ parsed_object = self.ReadTwoByteString()
226
+ elif tag == definitions.V8SerializationTag.OBJECT_REFERENCE:
227
+ _, object_id = self.decoder.DecodeUint32Varint()
228
+ parsed_object = self.objects[object_id]
229
+ elif tag == definitions.V8SerializationTag.BEGIN_JS_OBJECT:
230
+ parsed_object = self._ReadJSObject()
231
+ elif tag == definitions.V8SerializationTag.BEGIN_SPARSE_JS_ARRAY:
232
+ parsed_object = self.ReadSparseJSArray()
233
+ elif tag == definitions.V8SerializationTag.BEGIN_DENSE_JS_ARRAY:
234
+ parsed_object = self.ReadDenseJSArray()
235
+ elif tag == definitions.V8SerializationTag.DATE:
236
+ parsed_object = self._ReadJSDate()
237
+ elif tag in (
238
+ definitions.V8SerializationTag.TRUE_OBJECT,
239
+ definitions.V8SerializationTag.FALSE_OBJECT,
240
+ definitions.V8SerializationTag.NUMBER_OBJECT,
241
+ definitions.V8SerializationTag.BIGINT_OBJECT,
242
+ definitions.V8SerializationTag.STRING_OBJECT):
243
+ parsed_object = self._ReadJSPrimitiveWrapper(tag)
244
+ elif tag == definitions.V8SerializationTag.REGEXP:
245
+ parsed_object = self._ReadJSRegExp()
246
+ elif tag == definitions.V8SerializationTag.BEGIN_JS_MAP:
247
+ parsed_object = self._ReadJSMap()
248
+ elif tag == definitions.V8SerializationTag.BEGIN_JS_SET:
249
+ parsed_object = self._ReadJSSet()
250
+ elif tag == definitions.V8SerializationTag.ARRAY_BUFFER:
251
+ parsed_object = self._ReadJSArrayBuffer(
252
+ is_shared=False, is_resizable=False)
253
+ elif tag == definitions.V8SerializationTag.RESIZABLE_ARRAY_BUFFER:
254
+ parsed_object = self._ReadJSArrayBuffer(
255
+ is_shared=False, is_resizable=True)
256
+ elif tag == definitions.V8SerializationTag.SHARED_ARRAY_BUFFER:
257
+ parsed_object = self._ReadJSArrayBuffer(
258
+ is_shared=True, is_resizable=False)
259
+ elif tag == definitions.V8SerializationTag.ERROR:
260
+ parsed_object = self._ReadJSError()
261
+ elif tag == definitions.V8SerializationTag.WASM_MODULE_TRANSFER:
262
+ parsed_object = self._ReadWasmModuleTransfer()
263
+ elif tag == definitions.V8SerializationTag.WASM_MEMORY_TRANSFER:
264
+ parsed_object = self._ReadWasmMemory()
265
+ elif tag == definitions.V8SerializationTag.HOST_OBJECT:
266
+ parsed_object = self.ReadHostObject()
267
+ elif (
268
+ tag == definitions.V8SerializationTag.SHARED_OBJECT and
269
+ self.version >= 15):
270
+ parsed_object = self.ReadSharedObject()
271
+ elif self.version < 13:
272
+ self.decoder.stream.seek(-1)
273
+ parsed_object = self.ReadHostObject()
274
+ else:
275
+ parsed_object = None
276
+ return tag, parsed_object
277
+
278
+ def ReadString(self) -> str:
279
+ """Reads a string from the current position.
280
+
281
+ Raises:
282
+ errors.ParserError: if the parsed object is not a string.
283
+ """
284
+ if self.version < 12:
285
+ return self.ReadUTF8String()
286
+
287
+ str_obj = self._ReadObject()
288
+ if not isinstance(str_obj, str):
289
+ raise errors.ParserError('Not a string')
290
+ return str_obj
291
+
292
+ def ReadBigInt(self) -> int:
293
+ """Reads a Javascript Bigint from the current position."""
294
+ bit_field = self.decoder.DecodeUint32Varint()[1]
295
+ byte_count = bit_field >> 1
296
+ signed = bool(bit_field & 0x1)
297
+ _, bigint = self.decoder.DecodeInt(byte_count=byte_count)
298
+ return -bigint if signed else bigint
299
+
300
+ def ReadUTF8String(self) -> str:
301
+ """Reads a UTF-8 string from the current position."""
302
+ count = self.decoder.DecodeUint32Varint()[1]
303
+ buffer = self.decoder.ReadBytes(count=count)[1]
304
+ return buffer.decode('utf-8')
305
+
306
+ def ReadOneByteString(self) -> str:
307
+ """Reads a one-byte string from the current position.
308
+
309
+ The raw bytes are decoded using latin-1 encoding.
310
+ """
311
+ length = self.decoder.DecodeUint32Varint()[1]
312
+ buffer = self.decoder.ReadBytes(count=length)[1]
313
+ return buffer.decode('latin-1')
314
+
315
+ def ReadTwoByteString(self) -> str:
316
+ """Reads a UTF-16-LE string from the current position."""
317
+ length = self.decoder.DecodeUint32Varint()[1]
318
+ buffer = self.decoder.ReadBytes(count=length)[1]
319
+ return buffer.decode('utf-16-le')
320
+
321
+ def ReadExpectedString(self) -> Optional[str]:
322
+ """Reads a string from the current position, None if there is no tag or
323
+ invalid byte length.
324
+
325
+ A string can be UTF-8/one-byte/two-byte.
326
+
327
+ Raises:
328
+ errors.ParserError: if there is an unexpected serialization tag."""
329
+ tag = self._ReadTag()
330
+ if not tag:
331
+ return None
332
+ _, byte_length = self.decoder.DecodeUint32Varint()
333
+ if not byte_length:
334
+ return None
335
+
336
+ _, raw_bytes = self.decoder.ReadBytes(count=byte_length)
337
+
338
+ if tag == definitions.V8SerializationTag.ONE_BYTE_STRING:
339
+ return raw_bytes.decode('latin-1')
340
+ if tag == definitions.V8SerializationTag.TWO_BYTE_STRING:
341
+ return raw_bytes.decode('utf-16-le')
342
+ if tag == definitions.V8SerializationTag.UTF8_STRING:
343
+ return raw_bytes.decode('utf-8')
344
+ raise errors.ParserError(f'Unexpected serialization tag {tag}.')
345
+
346
+ def _ReadJSObject(self) -> Dict:
347
+ """Reads a JSObject from the current position.
348
+
349
+ Raises:
350
+ errors.ParserError: if an unexpected number of properties have been
351
+ read.
352
+ """
353
+ next_id = self._GetNextId()
354
+ js_object = {}
355
+
356
+ num_properties = self._ReadJSObjectProperties(
357
+ js_object, definitions.V8SerializationTag.END_JS_OBJECT)
358
+ _, expected_number_properties = self.decoder.DecodeUint32Varint()
359
+ if expected_number_properties != num_properties:
360
+ raise errors.ParserError('Unexpected number of properties')
361
+
362
+ self.objects[next_id] = js_object
363
+ return js_object
364
+
365
+ def _ReadJSObjectProperties(
366
+ self,
367
+ js_object: Union[Dict, JSArray],
368
+ end_tag: definitions.V8SerializationTag
369
+ ) -> int:
370
+ """Reads key-value properties and sets them to the given js_object.
371
+
372
+ Args:
373
+ js_object: the Javascript object to set the parsed properties to.
374
+ end_tag: the end tag that signifies there are no more properties to read.
375
+
376
+ Returns:
377
+ the number of properties that were read.
378
+ """
379
+ num_properties = 0
380
+ while self._PeekTag() != end_tag:
381
+ key = self._ReadObject()
382
+ value = self._ReadObject()
383
+ js_object[key] = value
384
+ num_properties += 1
385
+ self._ConsumeTag(end_tag)
386
+ return num_properties
387
+
388
+ def _GetNextId(self) -> int:
389
+ """Gets the next object ID."""
390
+ next_id = self.next_id
391
+ self.next_id += 1
392
+ return next_id
393
+
394
+ def ReadSparseJSArray(self) -> JSArray:
395
+ """Reads a sparse encoded JSArray from the current position.
396
+
397
+ Raises:
398
+ errors.ParserError: if there is an unexpected property or array length.
399
+ """
400
+ next_id = self._GetNextId()
401
+
402
+ js_array = JSArray()
403
+ _, length = self.decoder.DecodeUint32Varint()
404
+ for _ in range(length):
405
+ js_array.append(Undefined())
406
+
407
+ num_properties = self._ReadJSObjectProperties(
408
+ js_array.__dict__, definitions.V8SerializationTag.END_SPARSE_JS_ARRAY)
409
+ _, expected_num_properties = self.decoder.DecodeUint32Varint()
410
+ _, expected_length = self.decoder.DecodeUint32Varint()
411
+
412
+ if num_properties != expected_num_properties:
413
+ raise errors.ParserError('Unexpected property length')
414
+ if length != expected_length:
415
+ raise errors.ParserError('Unexpected array length')
416
+ self.objects[next_id] = js_array
417
+ return js_array
418
+
419
+ def ReadDenseJSArray(self) -> JSArray:
420
+ """Reads a dense encoded JSArray from the current position.
421
+
422
+ Raises:
423
+ errors.ParserError: if there is an unexpected property or array length.
424
+ """
425
+ next_id = self._GetNextId()
426
+
427
+ js_array = JSArray()
428
+ _, length = self.decoder.DecodeUint32Varint()
429
+ for _ in range(length):
430
+ tag = self._PeekTag()
431
+ if tag == definitions.V8SerializationTag.THE_HOLE:
432
+ self._ConsumeTag(tag)
433
+ continue
434
+ array_object = self._ReadObject()
435
+
436
+ if self.version < 11 and isinstance(array_object, Undefined):
437
+ continue
438
+ js_array.append(array_object)
439
+
440
+ num_properties = self._ReadJSObjectProperties(
441
+ js_array.__dict__, definitions.V8SerializationTag.END_DENSE_JS_ARRAY)
442
+ _, expected_num_properties = self.decoder.DecodeUint32Varint()
443
+ _, expected_length = self.decoder.DecodeUint32Varint()
444
+ if num_properties != expected_num_properties:
445
+ raise errors.ParserError('Unexpected property length')
446
+ if length != expected_length:
447
+ raise errors.ParserError('Unexpected array length')
448
+ self.objects[next_id] = js_array
449
+ return js_array
450
+
451
+ def _ReadJSDate(self) -> datetime:
452
+ """Reads a JSDate from the current position."""
453
+ next_id = self._GetNextId()
454
+
455
+ _, value = self.decoder.DecodeDouble()
456
+ result = datetime.utcfromtimestamp(value/1000.0)
457
+ self.objects[next_id] = result
458
+ return result
459
+
460
+ def _ReadJSPrimitiveWrapper(
461
+ self,
462
+ tag: definitions.V8SerializationTag
463
+ ) -> Union[bool, float, int, str]:
464
+ """Reads a Javascript wrapped primitive.
465
+
466
+ Args:
467
+ tag: the serialization tag.
468
+
469
+ Returns:
470
+ the parsed value.
471
+
472
+ Raises:
473
+ errors.ParserError: if the given tag is not a Javascript wrapped
474
+ primitive.
475
+ """
476
+ next_id = self._GetNextId()
477
+
478
+ if tag == definitions.V8SerializationTag.TRUE_OBJECT:
479
+ value = True
480
+ elif tag == definitions.V8SerializationTag.FALSE_OBJECT:
481
+ value = False
482
+ elif tag == definitions.V8SerializationTag.NUMBER_OBJECT:
483
+ _, value = self.decoder.DecodeDouble()
484
+ elif tag == definitions.V8SerializationTag.BIGINT_OBJECT:
485
+ value = self.ReadBigInt()
486
+ elif tag == definitions.V8SerializationTag.STRING_OBJECT:
487
+ value = self.ReadString()
488
+ else:
489
+ raise errors.ParserError(f'Invalid tag {tag}')
490
+
491
+ self.objects[next_id] = value
492
+ return value
493
+
494
+ def _ReadJSRegExp(self) -> RegExp:
495
+ """Reads a Javscript regular expression from the current position."""
496
+ next_id = self._GetNextId()
497
+ pattern = self.ReadString()
498
+ _, flags = self.decoder.DecodeUint32Varint() # TODO: verify flags
499
+ regexp = RegExp(pattern=pattern, flags=flags)
500
+ self.objects[next_id] = regexp
501
+ return regexp
502
+
503
+ def _ReadJSMap(self) -> Dict[Any, Any]:
504
+ """Reads a Javascript map (dictionary) from the current position.
505
+
506
+ Raises:
507
+ errors.ParserError: on an unexpected map length.
508
+ """
509
+ next_id = self._GetNextId()
510
+ js_map = {}
511
+
512
+ tag = self._PeekTag()
513
+ while tag != definitions.V8SerializationTag.END_JS_MAP:
514
+ key = self._ReadObject()
515
+ value = self._ReadObject()
516
+ js_map[key] = value
517
+ tag = self._PeekTag()
518
+ self._ConsumeTag(definitions.V8SerializationTag.END_JS_MAP)
519
+
520
+ _, expected_length = self.decoder.DecodeUint32Varint()
521
+ if len(js_map) * 2 != expected_length:
522
+ raise errors.ParserError('unexpected length')
523
+
524
+ self.objects[next_id] = js_map
525
+ return js_map
526
+
527
+ def _ReadJSSet(self) -> Set[Any]:
528
+ """Reads a Javascript set from the current position.
529
+
530
+ Raises:
531
+ errors.ParserError: on an unexpected map length.
532
+ """
533
+ next_id = self._GetNextId()
534
+
535
+ js_set = set()
536
+ tag = self._PeekTag()
537
+ while tag != definitions.V8SerializationTag.END_JS_SET:
538
+ element = self._ReadObject()
539
+ js_set.add(element)
540
+ tag = self._PeekTag()
541
+ self._ConsumeTag(definitions.V8SerializationTag.END_JS_SET)
542
+
543
+ _, expected_length = self.decoder.DecodeUint32Varint()
544
+ if len(js_set) != expected_length:
545
+ raise ValueError('unexpected length')
546
+
547
+ self.objects[next_id] = js_set
548
+ return js_set
549
+
550
+ def _ReadJSArrayBuffer(
551
+ self, is_shared: bool, is_resizable: bool) -> bytes:
552
+ """Reads a Javascript ArrayBuffer from the current position.
553
+
554
+ Args:
555
+ is_shared: True if the buffer is shared, False otherwise.
556
+ is_resizable: True if the buffer is resizable, False otherwise.
557
+ """
558
+ next_id = self._GetNextId()
559
+ array_buffer = b''
560
+
561
+ if is_shared:
562
+ raise NotImplementedError('Shared ArrayBuffer not supported yet')
563
+
564
+ _, byte_length = self.decoder.DecodeUint32Varint()
565
+ max_byte_length = byte_length
566
+ if is_resizable:
567
+ _, max_byte_length = self.decoder.DecodeUint32Varint()
568
+ if byte_length > max_byte_length:
569
+ return array_buffer
570
+ if byte_length:
571
+ _, array_buffer = self.decoder.ReadBytes(byte_length)
572
+
573
+ self.objects[next_id] = array_buffer
574
+ return array_buffer
575
+
576
+ def _ReadJSArrayBufferView(self, buffer):
577
+ """Reads a JSArrayBufferView from the current position."""
578
+ _, tag = self.decoder.ReadBytes(1)
579
+ _, byte_offset = self.decoder.DecodeUint32Varint()
580
+ _, byte_length = self.decoder.DecodeUint32Varint()
581
+
582
+ if self.version >= 14: # or version_13_broken_data_mode
583
+ _, flags = self.decoder.DecodeUint32Varint()
584
+ else:
585
+ flags = 0
586
+
587
+ return BufferArrayView(
588
+ buffer=buffer,
589
+ tag=definitions.V8ArrayBufferViewTag(tag[0]),
590
+ offset=byte_offset,
591
+ length=byte_length,
592
+ flags=flags)
593
+
594
+ def _ReadJSError(self):
595
+ """Reads a Javascript error from the current position."""
596
+ raise NotImplementedError('ValueDeserializer.ReadJSError')
597
+
598
+ def _ReadWasmModuleTransfer(self):
599
+ """Reads a Wasm module transfer object from the current position."""
600
+ raise NotImplementedError('ValueDeserializer.ReadWasmModuleTransfer')
601
+
602
+ def _ReadWasmMemory(self):
603
+ """Reads a Wasm memory object from the current position."""
604
+ raise NotImplementedError('ValueDeserializer.ReadWasmMemory')
605
+
606
+ def ReadSharedObject(self) -> Any:
607
+ """Reads a shared object from the current position."""
608
+ #_, shared_object_id = self.decoder.DecodeUint32Varint()
609
+ raise NotImplementedError('ValueDeserializer.ReadSharedObject')
610
+
611
+ def ReadHostObject(self):
612
+ """Reads a Host object using the delegate object.
613
+
614
+ Raises:
615
+ errors.ParserError: when the delegate object is None.
616
+ """
617
+ next_id = self._GetNextId()
618
+ if not self.delegate:
619
+ raise errors.ParserError('No delegate to read host object.')
620
+ host_object = self.delegate.ReadHostObject()
621
+ self.objects[next_id] = host_object
622
+ return host_object
623
+
624
+ @classmethod
625
+ def FromBytes(cls, data: bytes, delegate: Any) -> Any:
626
+ """Returns a deserialized javascript object from the data.
627
+
628
+ Args:
629
+ data: the data to deserialize/parse.
630
+ delegate: an object to delegate parsing additional Javascript objects.
631
+
632
+ Returns:
633
+ A python representation of the parsed javascript object.
634
+
635
+ Raises:
636
+ errors.ParserError: if there is an invalid V8 javascript header.
637
+ """
638
+ stream = io.BytesIO(data)
639
+ deserializer = cls(stream, delegate)
640
+ if not deserializer.ReadHeader():
641
+ raise errors.ParserError('Invalid V8 header')
642
+ return deserializer.ReadObjectWrapper()
@@ -0,0 +1,14 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright 2024 Google LLC
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # https://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.