5
5
import math
6
6
import operator
7
7
import re
8
- from collections import Counter
9
8
10
9
from .utils import (
11
10
error_to_compat_str ,
15
14
unified_timestamp ,
16
15
)
17
16
from .compat import (
17
+ compat_basestring ,
18
18
compat_collections_chain_map as ChainMap ,
19
19
compat_itertools_zip_longest as zip_longest ,
20
20
compat_str ,
@@ -76,6 +76,10 @@ def _js_comp_op(op):
76
76
def wrapped (a , b ):
77
77
if JS_Undefined in (a , b ):
78
78
return False
79
+ if isinstance (a , compat_basestring ):
80
+ b = compat_str (b or 0 )
81
+ elif isinstance (b , compat_basestring ):
82
+ a = compat_str (a or 0 )
79
83
return op (a or 0 , b or 0 )
80
84
81
85
return wrapped
@@ -195,7 +199,6 @@ class JSInterpreter(object):
195
199
'y' : 4096 , # Perform a "sticky" search that matches starting at the current position in the target string
196
200
}
197
201
198
- _EXC_NAME = '__youtube_dl_exception__'
199
202
_OBJ_NAME = '__youtube_dl_jsinterp_obj'
200
203
201
204
OP_CHARS = None
@@ -242,9 +245,8 @@ def _regex_flags(cls, expr):
242
245
def _separate (cls , expr , delim = ',' , max_split = None , skip_delims = None ):
243
246
if not expr :
244
247
return
245
- # collections.Counter() is ~10% slower
248
+ # collections.Counter() is ~10% slower in both 2.7 and 3.9
246
249
counters = {k : 0 for k in _MATCHING_PARENS .values ()}
247
- # counters = Counter()
248
250
start , splits , pos , delim_len = 0 , 0 , 0 , len (delim ) - 1
249
251
in_quote , escaping , skipping = None , False , 0
250
252
after_op , in_regex_char_group , skip_re = True , False , 0
@@ -291,7 +293,9 @@ def _separate(cls, expr, delim=',', max_split=None, skip_delims=None):
291
293
yield expr [start :]
292
294
293
295
@classmethod
294
- def _separate_at_paren (cls , expr , delim ):
296
+ def _separate_at_paren (cls , expr , delim = None ):
297
+ if delim is None :
298
+ delim = expr and _MATCHING_PARENS [expr [0 ]]
295
299
separated = list (cls ._separate (expr , delim , 1 ))
296
300
297
301
if len (separated ) < 2 :
@@ -376,7 +380,7 @@ def interpret_statement(self, stmt, local_vars, allow_recursion=100):
376
380
if expr .startswith ('new ' ):
377
381
obj = expr [4 :]
378
382
if obj .startswith ('Date(' ):
379
- left , right = self ._separate_at_paren (obj [4 :], ')' )
383
+ left , right = self ._separate_at_paren (obj [4 :])
380
384
expr = unified_timestamp (
381
385
self .interpret_expression (left , local_vars , allow_recursion ), False )
382
386
if not expr :
@@ -390,7 +394,7 @@ def interpret_statement(self, stmt, local_vars, allow_recursion=100):
390
394
return None , should_return
391
395
392
396
if expr .startswith ('{' ):
393
- inner , outer = self ._separate_at_paren (expr , '}' )
397
+ inner , outer = self ._separate_at_paren (expr )
394
398
# try for object expression (Map)
395
399
sub_expressions = [list (self ._separate (sub_expr .strip (), ':' , 1 )) for sub_expr in self ._separate (inner )]
396
400
if all (len (sub_expr ) == 2 for sub_expr in sub_expressions ):
@@ -406,65 +410,71 @@ def interpret_statement(self, stmt, local_vars, allow_recursion=100):
406
410
expr = self ._dump (inner , local_vars ) + outer
407
411
408
412
if expr .startswith ('(' ):
409
- inner , outer = self ._separate_at_paren (expr , ')' )
413
+ inner , outer = self ._separate_at_paren (expr )
410
414
inner , should_abort = self .interpret_statement (inner , local_vars , allow_recursion )
411
415
if not outer or should_abort :
412
416
return inner , should_abort or should_return
413
417
else :
414
418
expr = self ._dump (inner , local_vars ) + outer
415
419
416
420
if expr .startswith ('[' ):
417
- inner , outer = self ._separate_at_paren (expr , ']' )
421
+ inner , outer = self ._separate_at_paren (expr )
418
422
name = self ._named_object (local_vars , [
419
423
self .interpret_expression (item , local_vars , allow_recursion )
420
424
for item in self ._separate (inner )])
421
425
expr = name + outer
422
426
423
427
m = re .match (r'''(?x)
424
- (?P<try>try|finally)\s*|
425
- (?P<catch>catch\s*(?P<err>\(\s*{_NAME_RE}\s*\)))|
426
- (?P<switch>switch)\s*\(|
427
- (?P<for>for)\s*\(|
428
- ''' .format (** globals ()), expr )
428
+ (?P<try>try)\s*\{|
429
+ (?P<switch>switch)\s*\(|
430
+ (?P<for>for)\s*\(
431
+ ''' , expr )
429
432
md = m .groupdict () if m else {}
430
433
if md .get ('try' ):
431
- if expr [m .end ()] == '{' :
432
- try_expr , expr = self ._separate_at_paren (expr [m .end ():], '}' )
433
- else :
434
- try_expr , expr = expr [m .end () - 1 :], ''
434
+ try_expr , expr = self ._separate_at_paren (expr [m .end () - 1 :])
435
+ err = None
435
436
try :
436
437
ret , should_abort = self .interpret_statement (try_expr , local_vars , allow_recursion )
437
438
if should_abort :
438
439
return ret , True
439
- except JS_Throw as e :
440
- local_vars [self ._EXC_NAME ] = e .error
441
440
except Exception as e :
442
441
# XXX: This works for now, but makes debugging future issues very hard
443
- local_vars [self ._EXC_NAME ] = e
444
- ret , should_abort = self .interpret_statement (expr , local_vars , allow_recursion )
445
- return ret , should_abort or should_return
446
-
447
- elif md .get ('catch' ):
448
-
449
- catch_expr , expr = self ._separate_at_paren (expr [m .end ():], '}' )
450
- if self ._EXC_NAME in local_vars :
451
- catch_vars = local_vars .new_child ({m .group ('err' ): local_vars .pop (self ._EXC_NAME )})
452
- ret , should_abort = self .interpret_statement (catch_expr , catch_vars , allow_recursion )
442
+ err = e
443
+
444
+ pending = (None , False )
445
+ m = re .match (r'catch\s*(?P<err>\(\s*{_NAME_RE}\s*\))?\{{' .format (** globals ()), expr )
446
+ if m :
447
+ sub_expr , expr = self ._separate_at_paren (expr [m .end () - 1 :])
448
+ if err :
449
+ catch_vars = {}
450
+ if m .group ('err' ):
451
+ catch_vars [m .group ('err' )] = err .error if isinstance (err , JS_Throw ) else err
452
+ catch_vars = local_vars .new_child (m = catch_vars )
453
+ err = None
454
+ pending = self .interpret_statement (sub_expr , catch_vars , allow_recursion )
455
+
456
+ m = re .match (r'finally\s*\{' , expr )
457
+ if m :
458
+ sub_expr , expr = self ._separate_at_paren (expr [m .end () - 1 :])
459
+ ret , should_abort = self .interpret_statement (sub_expr , local_vars , allow_recursion )
453
460
if should_abort :
454
461
return ret , True
455
462
456
- ret , should_abort = self .interpret_statement (expr , local_vars , allow_recursion )
463
+ ret , should_abort = pending
464
+ if should_abort :
465
+ return ret , True
457
466
458
- return ret , should_abort or should_return
467
+ if err :
468
+ raise err
459
469
460
470
elif md .get ('for' ):
461
- constructor , remaining = self ._separate_at_paren (expr [m .end () - 1 :], ')' )
471
+ constructor , remaining = self ._separate_at_paren (expr [m .end () - 1 :])
462
472
if remaining .startswith ('{' ):
463
- body , expr = self ._separate_at_paren (remaining , '}' )
473
+ body , expr = self ._separate_at_paren (remaining )
464
474
else :
465
475
switch_m = re .match (r'switch\s*\(' , remaining ) # FIXME
466
476
if switch_m :
467
- switch_val , remaining = self ._separate_at_paren (remaining [switch_m .end () - 1 :], ')' )
477
+ switch_val , remaining = self ._separate_at_paren (remaining [switch_m .end () - 1 :])
468
478
body , expr = self ._separate_at_paren (remaining , '}' )
469
479
body = 'switch(%s){%s}' % (switch_val , body )
470
480
else :
@@ -483,11 +493,9 @@ def interpret_statement(self, stmt, local_vars, allow_recursion=100):
483
493
except JS_Continue :
484
494
pass
485
495
self .interpret_expression (increment , local_vars , allow_recursion )
486
- ret , should_abort = self .interpret_statement (expr , local_vars , allow_recursion )
487
- return ret , should_abort or should_return
488
496
489
497
elif md .get ('switch' ):
490
- switch_val , remaining = self ._separate_at_paren (expr [m .end () - 1 :], ')' )
498
+ switch_val , remaining = self ._separate_at_paren (expr [m .end () - 1 :])
491
499
switch_val = self .interpret_expression (switch_val , local_vars , allow_recursion )
492
500
body , expr = self ._separate_at_paren (remaining , '}' )
493
501
items = body .replace ('default:' , 'case default:' ).split ('case ' )[1 :]
@@ -510,6 +518,8 @@ def interpret_statement(self, stmt, local_vars, allow_recursion=100):
510
518
break
511
519
if matched :
512
520
break
521
+
522
+ if md :
513
523
ret , should_abort = self .interpret_statement (expr , local_vars , allow_recursion )
514
524
return ret , should_abort or should_return
515
525
@@ -618,7 +628,7 @@ def interpret_statement(self, stmt, local_vars, allow_recursion=100):
618
628
member = self .interpret_expression (m .group ('member2' ), local_vars , allow_recursion )
619
629
arg_str = expr [m .end ():]
620
630
if arg_str .startswith ('(' ):
621
- arg_str , remaining = self ._separate_at_paren (arg_str , ')' )
631
+ arg_str , remaining = self ._separate_at_paren (arg_str )
622
632
else :
623
633
arg_str , remaining = None , arg_str
624
634
@@ -795,7 +805,7 @@ def extract_function_code(self, funcname):
795
805
\((?P<args>[^)]*)\)\s*
796
806
(?P<code>{.+})''' % {'name' : re .escape (funcname )},
797
807
self .code )
798
- code , _ = self ._separate_at_paren (func_m .group ('code' ), '}' ) # refine the match
808
+ code , _ = self ._separate_at_paren (func_m .group ('code' )) # refine the match
799
809
if func_m is None :
800
810
raise self .Exception ('Could not find JS function "{funcname}"' .format (** locals ()))
801
811
return self .build_arglist (func_m .group ('args' )), code
@@ -810,7 +820,7 @@ def extract_function_from_code(self, argnames, code, *global_stack):
810
820
if mobj is None :
811
821
break
812
822
start , body_start = mobj .span ()
813
- body , remaining = self ._separate_at_paren (code [body_start - 1 :], '}' )
823
+ body , remaining = self ._separate_at_paren (code [body_start - 1 :])
814
824
name = self ._named_object (
815
825
local_vars ,
816
826
self .extract_function_from_code (
0 commit comments