]>
Raphaƫl G. Git Repositories - youtubedl/blob - youtube_dl/jsinterp.py
a7440c58242079ea1c6874e1bed0abe756fdc814
   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): 
  35         self
._objects 
= objects
 
  37     def interpret_statement(self
, stmt
, local_vars
, allow_recursion
=100): 
  38         if allow_recursion 
< 0: 
  39             raise ExtractorError('Recursion limit reached') 
  43         stmt_m 
= re
.match(r
'var\s', stmt
) 
  45             expr 
= stmt
[len(stmt_m
.group(0)):] 
  47             return_m 
= re
.match(r
'return(?:\s+|$)', stmt
) 
  49                 expr 
= stmt
[len(return_m
.group(0)):] 
  52                 # Try interpreting it as an expression 
  55         v 
= self
.interpret_expression(expr
, local_vars
, allow_recursion
) 
  56         return v
, should_abort
 
  58     def interpret_expression(self
, expr
, local_vars
, allow_recursion
): 
  61         if expr 
== '':  # Empty expression 
  64         if expr
.startswith('('): 
  66             for m 
in re
.finditer(r
'[()]', expr
): 
  72                         sub_expr 
= expr
[1:m
.start()] 
  73                         sub_result 
= self
.interpret_expression( 
  74                             sub_expr
, local_vars
, allow_recursion
) 
  75                         remaining_expr 
= expr
[m
.end():].strip() 
  76                         if not remaining_expr
: 
  79                             expr 
= json
.dumps(sub_result
) + remaining_expr
 
  82                 raise ExtractorError('Premature end of parens in %r' % expr
) 
  84         for op
, opfunc 
in _ASSIGN_OPERATORS
: 
  86                 (?P<out>%s)(?:\[(?P<index>[^\]]+?)\])? 
  88                 (?P<expr>.*)$''' % (_NAME_RE
, re
.escape(op
)), expr
) 
  91             right_val 
= self
.interpret_expression( 
  92                 m
.group('expr'), local_vars
, allow_recursion 
- 1) 
  94             if m
.groupdict().get('index'): 
  95                 lvar 
= local_vars
[m
.group('out')] 
  96                 idx 
= self
.interpret_expression( 
  97                     m
.group('index'), local_vars
, allow_recursion
) 
  98                 assert isinstance(idx
, int) 
 100                 val 
= opfunc(cur
, right_val
) 
 104                 cur 
= local_vars
.get(m
.group('out')) 
 105                 val 
= opfunc(cur
, right_val
) 
 106                 local_vars
[m
.group('out')] = val
 
 113             r
'(?!if|return|true|false)(?P<name>%s)$' % _NAME_RE
, 
 116             return local_vars
[var_m
.group('name')] 
 119             return json
.loads(expr
) 
 124             r
'(?P<var>%s)\.(?P<member>[^(]+)(?:\(+(?P<args>[^()]*)\))?$' % _NAME_RE
, 
 127             variable 
= m
.group('var') 
 128             member 
= m
.group('member') 
 129             arg_str 
= m
.group('args') 
 131             if variable 
in local_vars
: 
 132                 obj 
= local_vars
[variable
] 
 134                 if variable 
not in self
._objects
: 
 135                     self
._objects
[variable
] = self
.extract_object(variable
) 
 136                 obj 
= self
._objects
[variable
] 
 140                 if member 
== 'length': 
 144             assert expr
.endswith(')') 
 150                     self
.interpret_expression(v
, local_vars
, allow_recursion
) 
 151                     for v 
in arg_str
.split(',')]) 
 153             if member 
== 'split': 
 154                 assert argvals 
== ('',) 
 157                 assert len(argvals
) == 1 
 158                 return argvals
[0].join(obj
) 
 159             if member 
== 'reverse': 
 160                 assert len(argvals
) == 0 
 163             if member 
== 'slice': 
 164                 assert len(argvals
) == 1 
 165                 return obj
[argvals
[0]:] 
 166             if member 
== 'splice': 
 167                 assert isinstance(obj
, list) 
 168                 index
, howMany 
= argvals
 
 170                 for i 
in range(index
, min(index 
+ howMany
, len(obj
))): 
 171                     res
.append(obj
.pop(index
)) 
 174             return obj
[member
](argvals
) 
 177             r
'(?P<in>%s)\[(?P<idx>.+)\]$' % _NAME_RE
, expr
) 
 179             val 
= local_vars
[m
.group('in')] 
 180             idx 
= self
.interpret_expression( 
 181                 m
.group('idx'), local_vars
, allow_recursion 
- 1) 
 184         for op
, opfunc 
in _OPERATORS
: 
 185             m 
= re
.match(r
'(?P<x>.+?)%s(?P<y>.+)' % re
.escape(op
), expr
) 
 188             x
, abort 
= self
.interpret_statement( 
 189                 m
.group('x'), local_vars
, allow_recursion 
- 1) 
 191                 raise ExtractorError( 
 192                     'Premature left-side return of %s in %r' % (op
, expr
)) 
 193             y
, abort 
= self
.interpret_statement( 
 194                 m
.group('y'), local_vars
, allow_recursion 
- 1) 
 196                 raise ExtractorError( 
 197                     'Premature right-side return of %s in %r' % (op
, expr
)) 
 201             r
'^(?P<func>%s)\((?P<args>[a-zA-Z0-9_$,]+)\)$' % _NAME_RE
, expr
) 
 203             fname 
= m
.group('func') 
 205                 int(v
) if v
.isdigit() else local_vars
[v
] 
 206                 for v 
in m
.group('args').split(',')]) 
 207             if fname 
not in self
._functions
: 
 208                 self
._functions
[fname
] = self
.extract_function(fname
) 
 209             return self
._functions
[fname
](argvals
) 
 211         raise ExtractorError('Unsupported JS expression %r' % expr
) 
 213     def extract_object(self
, objname
): 
 216             (r
'(?:var\s+)?%s\s*=\s*\{' % re
.escape(objname
)) + 
 217             r
'\s*(?P<fields>([a-zA-Z$0-9]+\s*:\s*function\(.*?\)\s*\{.*?\}(?:,\s*)?)*)' + 
 220         fields 
= obj_m
.group('fields') 
 221         # Currently, it only supports function definitions 
 222         fields_m 
= re
.finditer( 
 223             r
'(?P<key>[a-zA-Z$0-9]+)\s*:\s*function' 
 224             r
'\((?P<args>[a-z,]+)\){(?P<code>[^}]+)}', 
 227             argnames 
= f
.group('args').split(',') 
 228             obj
[f
.group('key')] = self
.build_function(argnames
, f
.group('code')) 
 232     def extract_function(self
, funcname
): 
 235                 (?:function\s+%s|[{;,]%s\s*=\s*function|var\s+%s\s*=\s*function)\s* 
 236                 \((?P<args>[^)]*)\)\s* 
 237                 \{(?P<code>[^}]+)\}''' % ( 
 238                 re
.escape(funcname
), re
.escape(funcname
), re
.escape(funcname
)), 
 241             raise ExtractorError('Could not find JS function %r' % funcname
) 
 242         argnames 
= func_m
.group('args').split(',') 
 244         return self
.build_function(argnames
, func_m
.group('code')) 
 246     def call_function(self
, funcname
, *args
): 
 247         f 
= self
.extract_function(funcname
) 
 250     def build_function(self
, argnames
, code
): 
 252             local_vars 
= dict(zip(argnames
, args
)) 
 253             for stmt 
in code
.split(';'): 
 254                 res
, abort 
= self
.interpret_statement(stmt
, local_vars
)