Merge branch 'master' into python-2.4

This commit is contained in:
rocky
2018-02-15 07:31:11 -05:00
12 changed files with 150 additions and 42 deletions

View File

@@ -45,6 +45,8 @@
$ source admin-tools/setup-python-2.4.sh
$ git merge master
# Add and fix merge conflicts
$ git commit
# Check against older versions

Binary file not shown.

Binary file not shown.

0
test/bytecode_3.6_run/.gitignore vendored Normal file
View File

View File

@@ -1,9 +1,16 @@
# Bug in Python 3.5 is getting the two star'd arguments right.
def sum(a,b,c,d):
# Bug in 3.5 is detecting when there is more than
# one * in a call. There is a "BUILD_UNPACK_LIST" instruction used
# to unpack star arguments
def sum(a, b, c, d):
return a + b + c + d
args=(1,2)
args, a, b, c = (1, 2), 1, 2, 3
sum(*args, *args)
sum(*args, *args, *args)
sum(a, *args, *args)
sum(a, b, *args)
sum(a, b, *args, *args)
# FIXME: this is handled incorrectly
# (*c,) = (3,4)

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# Mode: -*- python -*-
#
# Copyright (c) 2015-2016 by Rocky Bernstein <rb@dustyfeet.com>
# Copyright (c) 2015-2016, 2018 by Rocky Bernstein <rb@dustyfeet.com>
#
import sys, os, getopt
@@ -12,16 +12,23 @@ program, ext = os.path.splitext(os.path.basename(__file__))
__doc__ = """
Usage:
%s [OPTIONS]... FILE
%s [--help | -h | -V | --version]
{0} [OPTIONS]... FILE
{0} [--help | -h | -V | --version]
Disassemble FILE with the instruction mangling that is done to
assist uncompyle6 in parsing the instruction stream. For example
instructions with variable-length arguments like CALL_FUNCTION and
BUILD_LIST have arguement counts appended to the instruction name, and
COME_FROM instructions are inserted into the instruction stream.
Examples:
{0} foo.pyc
{0} foo.py # same thing as above but find the file
{0} foo.pyc bar.pyc # disassemble foo.pyc and bar.pyc
See also `pydisasm' from the `xdis' package.
Options:
-U | --uncompyle6 show instructions with uncompyle6 mangling
-V | --version show version and stop
-h | --help show this message
@@ -33,8 +40,6 @@ def main():
Usage_short = """usage: %s FILE...
Type -h for for full help.""" % program
native = True
if len(sys.argv) == 1:
sys.stderr.write("No file(s) given\n")
sys.stderr.write(Usage_short)
@@ -54,8 +59,6 @@ Type -h for for full help.""" % program
elif opt in ('-V', '--version'):
print("%s %s" % (program, VERSION))
sys.exit(0)
elif opt in ('-U', '--uncompyle6'):
native = False
else:
print(opt)
sys.stderr.write(Usage_short)
@@ -63,7 +66,7 @@ Type -h for for full help.""" % program
for file in files:
if os.path.exists(files[0]):
disassemble_file(file, sys.stdout, native)
disassemble_file(file, sys.stdout)
else:
sys.stderr.write("Can't read %s - skipping\n" % files[0])
pass

View File

@@ -484,33 +484,15 @@ class Python3Parser(PythonParser):
token.kind = self.call_fn_name(token)
uniq_param = args_kw + args_pos
if self.version == 3.5 and opname.startswith('CALL_FUNCTION_VAR'):
# Python 3.5 changes the stack position of *args. KW args come
# after *args.
# Python 3.6+ replaces CALL_FUNCTION_VAR_KW with CALL_FUNCTION_EX
if opname.endswith('KW'):
kw = 'expr '
else:
kw = ''
rule = ('call ::= expr expr ' +
('pos_arg ' * args_pos) +
('kwarg ' * args_kw) + kw + token.kind)
self.add_unique_rule(rule, token.kind, uniq_param, customize)
# Note: 3.5+ have subclassed this method; so we don't handle
# 'CALL_FUNCTION_VAR' or 'CALL_FUNCTION_EX' here.
rule = ('call ::= expr ' +
('pos_arg ' * args_pos) +
('kwarg ' * args_kw) +
'expr ' * nak + token.kind)
self.add_unique_rule(rule, token.kind, uniq_param, customize)
if self.version >= 3.5 and seen_GET_AWAITABLE_YIELD_FROM:
rule = ('async_call ::= expr ' +
('pos_arg ' * args_pos) +
('kwarg ' * args_kw) +
'expr ' * nak + token.kind +
' GET_AWAITABLE LOAD_CONST YIELD_FROM')
self.add_unique_rule(rule, token.kind, uniq_param, customize)
self.add_unique_rule('expr ::= async_call', token.kind, uniq_param, customize)
'expr ' * nak + token.kind)
self.add_unique_rule(rule, token.kind, uniq_param, customize)
if possible_class_decorator:
if next_token == 'CALL_FUNCTION' and next_token.attr == 1:
@@ -671,6 +653,11 @@ class Python3Parser(PythonParser):
rule = ('build_map_unpack_with_call ::= ' + 'expr1024 ' * int(v//1024) +
'expr32 ' * int((v//32) % 32) +
'expr ' * (v % 32) + opname)
self.addRule(rule, nop_func)
elif opname.startswith('BUILD_TUPLE_UNPACK_WITH_CALL'):
v = token.attr
rule = ('starred ::= %s %s' % ('expr ' * v, opname))
self.addRule(rule, nop_func)
elif opname_base in ('BUILD_LIST', 'BUILD_SET', 'BUILD_TUPLE'):
v = token.attr

View File

@@ -186,6 +186,52 @@ class Python35Parser(Python34Parser):
pass
return
def custom_classfunc_rule(self, opname, token, customize,
seen_LOAD_BUILD_CLASS,
seen_GET_AWAITABLE_YIELD_FROM,
*args):
args_pos, args_kw = self.get_pos_kw(token)
# Additional exprs for * and ** args:
# 0 if neither
# 1 for CALL_FUNCTION_VAR or CALL_FUNCTION_KW
# 2 for * and ** args (CALL_FUNCTION_VAR_KW).
# Yes, this computation based on instruction name is a little bit hoaky.
nak = ( len(opname)-len('CALL_FUNCTION') ) // 3
uniq_param = args_kw + args_pos
if seen_GET_AWAITABLE_YIELD_FROM:
rule = ('async_call ::= expr ' +
('pos_arg ' * args_pos) +
('kwarg ' * args_kw) +
'expr ' * nak + token.kind +
' GET_AWAITABLE LOAD_CONST YIELD_FROM')
self.add_unique_rule(rule, token.kind, uniq_param, customize)
self.add_unique_rule('expr ::= async_call', token.kind, uniq_param, customize)
uniq_param = args_kw + args_pos
if opname.startswith('CALL_FUNCTION_VAR'):
# Python 3.5 changes the stack position of *args. KW args come
# after *args.
# Note: Python 3.6+ replaces CALL_FUNCTION_VAR and
# CALL_FUNCTION_VAR_KW with CALL_FUNCTION_EX
token.kind = self.call_fn_name(token)
if opname.endswith('KW'):
kw = 'expr '
else:
kw = ''
rule = ('call ::= expr expr ' +
('pos_arg ' * args_pos) +
('kwarg ' * args_kw) + kw + token.kind)
self.add_unique_rule(rule, token.kind, uniq_param, customize)
else:
super(Python35Parser, self).custom_classfunc_rule(opname, token, customize,
seen_LOAD_BUILD_CLASS,
seen_GET_AWAITABLE_YIELD_FROM,
*args)
class Python35ParserSingle(Python35Parser, PythonParserSingle):
pass

View File

@@ -131,6 +131,26 @@ class Python36Parser(Python35Parser):
def custom_classfunc_rule(self, opname, token, customize,
possible_class_decorator,
seen_GET_AWAITABLE_YIELD_FROM, next_token):
args_pos, args_kw = self.get_pos_kw(token)
# Additional exprs for * and ** args:
# 0 if neither
# 1 for CALL_FUNCTION_VAR or CALL_FUNCTION_KW
# 2 for * and ** args (CALL_FUNCTION_VAR_KW).
# Yes, this computation based on instruction name is a little bit hoaky.
nak = ( len(opname)-len('CALL_FUNCTION') ) // 3
uniq_param = args_kw + args_pos
if seen_GET_AWAITABLE_YIELD_FROM:
rule = ('async_call ::= expr ' +
('pos_arg ' * args_pos) +
('kwarg ' * args_kw) +
'expr ' * nak + token.kind +
' GET_AWAITABLE LOAD_CONST YIELD_FROM')
self.add_unique_rule(rule, token.kind, uniq_param, customize)
self.add_unique_rule('expr ::= async_call', token.kind, uniq_param, customize)
if opname.startswith('CALL_FUNCTION_KW'):
self.addRule("expr ::= call_kw", nop_func)
values = 'expr ' * token.attr

View File

@@ -33,6 +33,8 @@ class Scanner36(Scanner3):
t.kind = 'CALL_FUNCTION_KW_%s' % t.attr
elif t.op == self.opc.BUILD_MAP_UNPACK_WITH_CALL:
t.kind = 'BUILD_MAP_UNPACK_WITH_CALL_%d' % t.attr
elif t.op == self.opc.BUILD_TUPLE_UNPACK_WITH_CALL:
t.kind = 'BUILD_TUPLE_UNPACK_WITH_CALL_%d' % t.attr
pass
return tokens, customize

View File

@@ -43,9 +43,9 @@ def customize_for_version(self, is_pypy, version):
if version < 3.0:
TABLE_R.update({
'STORE_SLICE+0': ( '%c[:]', 0 ),
'STORE_SLICE+1': ( '%c[%p:]', 0, (1, 100) ),
'STORE_SLICE+2': ( '%c[:%p]', 0, (1, 100) ),
'STORE_SLICE+3': ( '%c[%p:%p]', 0, (1, 100), (2, 100) ),
'STORE_SLICE+1': ( '%c[%p:]', 0, (1, -1) ),
'STORE_SLICE+2': ( '%c[:%p]', 0, (1, -1) ),
'STORE_SLICE+3': ( '%c[%p:%p]', 0, (1, -1), (2, -1) ),
'DELETE_SLICE+0': ( '%|del %c[:]\n', 0 ),
'DELETE_SLICE+1': ( '%|del %c[%c:]\n', 0, 1 ),
'DELETE_SLICE+2': ( '%|del %c[:%c]\n', 0, 1 ),
@@ -251,8 +251,8 @@ def customize_for_version(self, is_pypy, version):
node.kind == 'call'
p = self.prec
self.prec = 80
self.template_engine(('%c(%P)', 0,
(1, -4, ', ', 100)), node)
self.template_engine(('%c(%P)', 0, (1, -4, ', ',
100)), node)
self.prec = p
node.kind == 'async_call'
self.prune()
@@ -270,7 +270,7 @@ def customize_for_version(self, is_pypy, version):
if key.kind.startswith('CALL_FUNCTION_VAR_KW'):
# Python 3.5 changes the stack position of *args. kwargs come
# after *args whereas in earlier Pythons, *args is at the end
# which simpilfiies things from our perspective.
# which simplifies things from our perspective.
# Python 3.6+ replaces CALL_FUNCTION_VAR_KW with CALL_FUNCTION_EX
# We will just swap the order to make it look like earlier Python 3.
entry = table[key.kind]
@@ -282,6 +282,27 @@ def customize_for_version(self, is_pypy, version):
node[kwarg_pos], node[args_pos] = node[args_pos], node[kwarg_pos]
args_pos = kwarg_pos
kwarg_pos += 1
elif key.kind.startswith('CALL_FUNCTION_VAR'):
nargs = node[-1].attr & 0xFF
if nargs > 0:
template = ('%c(%C, ', 0, (1, nargs+1, ', '))
else:
template = ('%c(', 0)
self.template_engine(template, node)
args_node = node[-2]
if args_node == 'pos_arg':
args_node = args_node[0]
if args_node == 'expr':
args_node = args_node[0]
if args_node == 'build_list_unpack':
template = ('*%P)', (0, len(args_node)-1, ', *', 100))
self.template_engine(template, args_node)
else:
template = ('*%c)', -2)
self.template_engine(template, node)
self.prune()
self.default(node)
self.n_call = n_call
@@ -326,7 +347,6 @@ def customize_for_version(self, is_pypy, version):
'func_args36': ( "%c(**", 0),
'try_except36': ( '%|try:\n%+%c%-%c\n\n', 1, 2 ),
'unpack_list': ( '*%c', (0, 'list') ),
'starred': ( '*%c', (0, 'expr') ),
'call_ex' : (
'%c(%c)',
(0, 'expr'), 1),
@@ -577,6 +597,27 @@ def customize_for_version(self, is_pypy, version):
return
self.n_kwargs_36 = kwargs_36
def starred(node):
l = len(node)
assert l > 0
pos_args = node[0]
if pos_args == 'expr':
pos_args = pos_args[0]
if pos_args == 'tuple':
star_start = 1
template = '%C', (0, -1, ', ')
self.template_engine(template, pos_args)
self.write(', ')
else:
star_start = 0
if l > 1:
template = ( '*%C', (star_start, -1, ', *') )
else:
template = ( '*%c', (star_start, 'expr') )
self.template_engine(template, node)
self.prune()
self.n_starred = starred
def return_closure(node):
# Nothing should be output here