]>
Raphaƫl G. Git Repositories - youtubedl/blob - youtube_dl/jsinterp.py
453e2732cc4faa453a98b153356c2188feef1d35
   1 from __future__ 
import unicode_literals
 
  15     ('>>', operator
.rshift
), 
  16     ('<<', operator
.lshift
), 
  20     ('/', operator
.truediv
), 
  23 _ASSIGN_OPERATORS 
= [(op 
+ '=', opfunc
) for op
, opfunc 
in _OPERATORS
] 
  24 _ASSIGN_OPERATORS
.append(('=', lambda cur
, right
: right
)) 
  26 _NAME_RE 
= r
'[a-zA-Z_$][a-zA-Z_$0-9]*' 
  29 class JSInterpreter(object): 
  30     def __init__(self
, code
, objects
=None): 
  33         self
.code 
= self
._remove
_comments
(code
) 
  35         self
._objects 
= objects
 
  37     def _remove_comments(self
, code
): 
  38         return re
.sub(r
'(?s)/\*.*?\*/', '', code
) 
  40     def interpret_statement(self
, stmt
, local_vars
, allow_recursion
=100): 
  41         if allow_recursion 
< 0: 
  42             raise ExtractorError('Recursion limit reached') 
  46         stmt_m 
= re
.match(r
'var\s', stmt
) 
  48             expr 
= stmt
[len(stmt_m
.group(0)):] 
  50             return_m 
= re
.match(r
'return(?:\s+|$)', stmt
) 
  52                 expr 
= stmt
[len(return_m
.group(0)):] 
  55                 # Try interpreting it as an expression 
  58         v 
= self
.interpret_expression(expr
, local_vars
, allow_recursion
) 
  59         return v
, should_abort
 
  61     def interpret_expression(self
, expr
, local_vars
, allow_recursion
): 
  64         if expr 
== '':  # Empty expression 
  67         if expr
.startswith('('): 
  69             for m 
in re
.finditer(r
'[()]', expr
): 
  75                         sub_expr 
= expr
[1:m
.start()] 
  76                         sub_result 
= self
.interpret_expression( 
  77                             sub_expr
, local_vars
, allow_recursion
) 
  78                         remaining_expr 
= expr
[m
.end():].strip() 
  79                         if not remaining_expr
: 
  82                             expr 
= json
.dumps(sub_result
) + remaining_expr
 
  85                 raise ExtractorError('Premature end of parens in %r' % expr
) 
  87         for op
, opfunc 
in _ASSIGN_OPERATORS
: 
  89                 (?P<out>%s)(?:\[(?P<index>[^\]]+?)\])? 
  91                 (?P<expr>.*)$''' % (_NAME_RE
, re
.escape(op
)), expr
) 
  94             right_val 
= self
.interpret_expression( 
  95                 m
.group('expr'), local_vars
, allow_recursion 
- 1) 
  97             if m
.groupdict().get('index'): 
  98                 lvar 
= local_vars
[m
.group('out')] 
  99                 idx 
= self
.interpret_expression( 
 100                     m
.group('index'), local_vars
, allow_recursion
) 
 101                 assert isinstance(idx
, int) 
 103                 val 
= opfunc(cur
, right_val
) 
 107                 cur 
= local_vars
.get(m
.group('out')) 
 108                 val 
= opfunc(cur
, right_val
) 
 109                 local_vars
[m
.group('out')] = val
 
 116             r
'(?!if|return|true|false)(?P<name>%s)$' % _NAME_RE
, 
 119             return local_vars
[var_m
.group('name')] 
 122             return json
.loads(expr
) 
 127             r
'(?P<var>%s)\.(?P<member>[^(]+)(?:\(+(?P<args>[^()]*)\))?$' % _NAME_RE
, 
 130             variable 
= m
.group('var') 
 131             member 
= m
.group('member') 
 132             arg_str 
= m
.group('args') 
 134             if variable 
in local_vars
: 
 135                 obj 
= local_vars
[variable
] 
 137                 if variable 
not in self
._objects
: 
 138                     self
._objects
[variable
] = self
.extract_object(variable
) 
 139                 obj 
= self
._objects
[variable
] 
 143                 if member 
== 'length': 
 147             assert expr
.endswith(')') 
 153                     self
.interpret_expression(v
, local_vars
, allow_recursion
) 
 154                     for v 
in arg_str
.split(',')]) 
 156             if member 
== 'split': 
 157                 assert argvals 
== ('',) 
 160                 assert len(argvals
) == 1 
 161                 return argvals
[0].join(obj
) 
 162             if member 
== 'reverse': 
 163                 assert len(argvals
) == 0 
 166             if member 
== 'slice': 
 167                 assert len(argvals
) == 1 
 168                 return obj
[argvals
[0]:] 
 169             if member 
== 'splice': 
 170                 assert isinstance(obj
, list) 
 171                 index
, howMany 
= argvals
 
 173                 for i 
in range(index
, min(index 
+ howMany
, len(obj
))): 
 174                     res
.append(obj
.pop(index
)) 
 177             return obj
[member
](argvals
) 
 180             r
'(?P<in>%s)\[(?P<idx>.+)\]$' % _NAME_RE
, expr
) 
 182             val 
= local_vars
[m
.group('in')] 
 183             idx 
= self
.interpret_expression( 
 184                 m
.group('idx'), local_vars
, allow_recursion 
- 1) 
 187         for op
, opfunc 
in _OPERATORS
: 
 188             m 
= re
.match(r
'(?P<x>.+?)%s(?P<y>.+)' % re
.escape(op
), expr
) 
 191             x
, abort 
= self
.interpret_statement( 
 192                 m
.group('x'), local_vars
, allow_recursion 
- 1) 
 194                 raise ExtractorError( 
 195                     'Premature left-side return of %s in %r' % (op
, expr
)) 
 196             y
, abort 
= self
.interpret_statement( 
 197                 m
.group('y'), local_vars
, allow_recursion 
- 1) 
 199                 raise ExtractorError( 
 200                     'Premature right-side return of %s in %r' % (op
, expr
)) 
 204             r
'^(?P<func>%s)\((?P<args>[a-zA-Z0-9_$,]+)\)$' % _NAME_RE
, expr
) 
 206             fname 
= m
.group('func') 
 208                 int(v
) if v
.isdigit() else local_vars
[v
] 
 209                 for v 
in m
.group('args').split(',')]) 
 210             if fname 
not in self
._functions
: 
 211                 self
._functions
[fname
] = self
.extract_function(fname
) 
 212             return self
._functions
[fname
](argvals
) 
 214         raise ExtractorError('Unsupported JS expression %r' % expr
) 
 216     def extract_object(self
, objname
): 
 219             (r
'(?:var\s+)?%s\s*=\s*\{' % re
.escape(objname
)) + 
 220             r
'\s*(?P<fields>([a-zA-Z$0-9]+\s*:\s*function\(.*?\)\s*\{.*?\})*)' + 
 223         fields 
= obj_m
.group('fields') 
 224         # Currently, it only supports function definitions 
 225         fields_m 
= re
.finditer( 
 226             r
'(?P<key>[a-zA-Z$0-9]+)\s*:\s*function' 
 227             r
'\((?P<args>[a-z,]+)\){(?P<code>[^}]+)}', 
 230             argnames 
= f
.group('args').split(',') 
 231             obj
[f
.group('key')] = self
.build_function(argnames
, f
.group('code')) 
 235     def extract_function(self
, funcname
): 
 238                 (?:function\s+%s|[{;]%s\s*=\s*function)\s* 
 239                 \((?P<args>[^)]*)\)\s* 
 240                 \{(?P<code>[^}]+)\}''' % ( 
 241                 re
.escape(funcname
), re
.escape(funcname
)), 
 244             raise ExtractorError('Could not find JS function %r' % funcname
) 
 245         argnames 
= func_m
.group('args').split(',') 
 247         return self
.build_function(argnames
, func_m
.group('code')) 
 249     def call_function(self
, funcname
, *args
): 
 250         f 
= self
.extract_function(funcname
) 
 253     def build_function(self
, argnames
, code
): 
 255             local_vars 
= dict(zip(argnames
, args
)) 
 256             for stmt 
in code
.split(';'): 
 257                 res
, abort 
= self
.interpret_statement(stmt
, local_vars
)