bazarr/libs/js2py/internals/byte_trans.py

754 lines
26 KiB
Python

from .code import Code
from .simplex import MakeError
from .opcodes import *
from .operations import *
from .trans_utils import *
SPECIAL_IDENTIFIERS = {'true', 'false', 'this'}
class ByteCodeGenerator:
def __init__(self, exe):
self.exe = exe
self.declared_continue_labels = {}
self.declared_break_labels = {}
self.implicit_breaks = []
self.implicit_continues = []
self.declared_vars = []
self.function_declaration_tape = []
self.states = []
def record_state(self):
self.states.append(
(self.declared_continue_labels, self.declared_break_labels,
self.implicit_breaks, self.implicit_continues, self.declared_vars,
self.function_declaration_tape))
self.declared_continue_labels, self.declared_break_labels, \
self.implicit_breaks, self.implicit_continues, \
self.declared_vars, self.function_declaration_tape = {}, {}, [], [], [], []
def restore_state(self):
self.declared_continue_labels, self.declared_break_labels, \
self.implicit_breaks, self.implicit_continues, \
self.declared_vars, self.function_declaration_tape = self.states.pop()
def ArrayExpression(self, elements, **kwargs):
for e in elements:
if e is None:
self.emit('LOAD_NONE')
else:
self.emit(e)
self.emit('LOAD_ARRAY', len(elements))
def AssignmentExpression(self, operator, left, right, **kwargs):
operator = operator[:-1]
if left['type'] == 'MemberExpression':
self.emit(left['object'])
if left['computed']:
self.emit(left['property'])
self.emit(right)
if operator:
self.emit('STORE_MEMBER_OP', operator)
else:
self.emit('STORE_MEMBER')
else:
self.emit(right)
if operator:
self.emit('STORE_MEMBER_DOT_OP', left['property']['name'],
operator)
else:
self.emit('STORE_MEMBER_DOT', left['property']['name'])
elif left['type'] == 'Identifier':
if left['name'] in SPECIAL_IDENTIFIERS:
raise MakeError('SyntaxError',
'Invalid left-hand side in assignment')
self.emit(right)
if operator:
self.emit('STORE_OP', left['name'], operator)
else:
self.emit('STORE', left['name'])
else:
raise MakeError('SyntaxError',
'Invalid left-hand side in assignment')
def BinaryExpression(self, operator, left, right, **kwargs):
self.emit(left)
self.emit(right)
self.emit('BINARY_OP', operator)
def BlockStatement(self, body, **kwargs):
self._emit_statement_list(body)
def BreakStatement(self, label, **kwargs):
if label is None:
self.emit('JUMP', self.implicit_breaks[-1])
else:
label = label.get('name')
if label not in self.declared_break_labels:
raise MakeError('SyntaxError',
'Undefined label \'%s\'' % label)
else:
self.emit('JUMP', self.declared_break_labels[label])
def CallExpression(self, callee, arguments, **kwargs):
if callee['type'] == 'MemberExpression':
self.emit(callee['object'])
if callee['computed']:
self.emit(callee['property'])
if arguments:
for e in arguments:
self.emit(e)
self.emit('LOAD_N_TUPLE', len(arguments))
self.emit('CALL_METHOD')
else:
self.emit('CALL_METHOD_NO_ARGS')
else:
prop_name = to_key(callee['property'])
if arguments:
for e in arguments:
self.emit(e)
self.emit('LOAD_N_TUPLE', len(arguments))
self.emit('CALL_METHOD_DOT', prop_name)
else:
self.emit('CALL_METHOD_DOT_NO_ARGS', prop_name)
else:
self.emit(callee)
if arguments:
for e in arguments:
self.emit(e)
self.emit('LOAD_N_TUPLE', len(arguments))
self.emit('CALL')
else:
self.emit('CALL_NO_ARGS')
def ClassBody(self, body, **kwargs):
raise NotImplementedError('Not available in ECMA 5.1')
def ClassDeclaration(self, id, superClass, body, **kwargs):
raise NotImplementedError('Not available in ECMA 5.1')
def ClassExpression(self, id, superClass, body, **kwargs):
raise NotImplementedError('Classes not available in ECMA 5.1')
def ConditionalExpression(self, test, consequent, alternate, **kwargs):
alt = self.exe.get_new_label()
end = self.exe.get_new_label()
# ?
self.emit(test)
self.emit('JUMP_IF_FALSE', alt)
# first val
self.emit(consequent)
self.emit('JUMP', end)
# second val
self.emit('LABEL', alt)
self.emit(alternate)
# end of ?: statement
self.emit('LABEL', end)
def ContinueStatement(self, label, **kwargs):
if label is None:
self.emit('JUMP', self.implicit_continues[-1])
else:
label = label.get('name')
if label not in self.declared_continue_labels:
raise MakeError('SyntaxError',
'Undefined label \'%s\'' % label)
else:
self.emit('JUMP', self.declared_continue_labels[label])
def DebuggerStatement(self, **kwargs):
self.EmptyStatement(**kwargs)
def DoWhileStatement(self, body, test, **kwargs):
continue_label = self.exe.get_new_label()
break_label = self.exe.get_new_label()
initial_do = self.exe.get_new_label()
self.emit('JUMP', initial_do)
self.emit('LABEL', continue_label)
self.emit(test)
self.emit('JUMP_IF_FALSE', break_label)
self.emit('LABEL', initial_do)
# translate the body, remember to add and afterwards remove implicit break/continue labels
self.implicit_continues.append(continue_label)
self.implicit_breaks.append(break_label)
self.emit(body)
self.implicit_continues.pop()
self.implicit_breaks.pop()
self.emit('JUMP', continue_label) # loop back
self.emit('LABEL', break_label)
def EmptyStatement(self, **kwargs):
# do nothing
pass
def ExpressionStatement(self, expression, **kwargs):
# change the final stack value
# pop the previous value and execute expression
self.emit('POP')
self.emit(expression)
def ForStatement(self, init, test, update, body, **kwargs):
continue_label = self.exe.get_new_label()
break_label = self.exe.get_new_label()
first_start = self.exe.get_new_label()
if init is not None:
self.emit(init)
if init['type'] != 'VariableDeclaration':
self.emit('POP')
# skip first update and go straight to test
self.emit('JUMP', first_start)
self.emit('LABEL', continue_label)
if update:
self.emit(update)
self.emit('POP')
self.emit('LABEL', first_start)
if test:
self.emit(test)
self.emit('JUMP_IF_FALSE', break_label)
# translate the body, remember to add and afterwards to remove implicit break/continue labels
self.implicit_continues.append(continue_label)
self.implicit_breaks.append(break_label)
self.emit(body)
self.implicit_continues.pop()
self.implicit_breaks.pop()
self.emit('JUMP', continue_label) # loop back
self.emit('LABEL', break_label)
def ForInStatement(self, left, right, body, **kwargs):
# prepare the needed labels
body_start_label = self.exe.get_new_label()
continue_label = self.exe.get_new_label()
break_label = self.exe.get_new_label()
# prepare the name
if left['type'] == 'VariableDeclaration':
if len(left['declarations']) != 1:
raise MakeError(
'SyntaxError',
' Invalid left-hand side in for-in loop: Must have a single binding.'
)
self.emit(left)
name = left['declarations'][0]['id']['name']
elif left['type'] == 'Identifier':
name = left['name']
else:
raise MakeError('SyntaxError',
'Invalid left-hand side in for-loop')
# prepare the iterable
self.emit(right)
# emit ForIn Opcode
self.emit('FOR_IN', name, body_start_label, continue_label,
break_label)
# a special continue position
self.emit('LABEL', continue_label)
self.emit('NOP')
self.emit('LABEL', body_start_label)
self.implicit_continues.append(continue_label)
self.implicit_breaks.append(break_label)
self.emit('LOAD_UNDEFINED')
self.emit(body)
self.implicit_continues.pop()
self.implicit_breaks.pop()
self.emit('NOP')
self.emit('LABEL', break_label)
self.emit('NOP')
def FunctionDeclaration(self, id, params, defaults, body, **kwargs):
if defaults:
raise NotImplementedError('Defaults not available in ECMA 5.1')
# compile function
self.record_state(
) # cleans translator state and appends it to the stack so that it can be later restored
function_start = self.exe.get_new_label()
function_declarations = self.exe.get_new_label()
declarations_done = self.exe.get_new_label(
) # put jump to this place at the and of function tape!
function_end = self.exe.get_new_label()
# skip the function if encountered externally
self.emit('JUMP', function_end)
self.emit('LABEL', function_start)
# call is made with empty stack so load undefined to fill it
self.emit('LOAD_UNDEFINED')
# declare all functions
self.emit('JUMP', function_declarations)
self.emit('LABEL', declarations_done)
self.function_declaration_tape.append(LABEL(function_declarations))
self.emit(body)
self.ReturnStatement(None)
self.function_declaration_tape.append(JUMP(declarations_done))
self.exe.tape.extend(self.function_declaration_tape)
self.emit('LABEL', function_end)
declared_vars = self.declared_vars
self.restore_state()
# create function object and append to stack
name = id.get('name')
assert name is not None
self.declared_vars.append(name)
self.function_declaration_tape.append(
LOAD_FUNCTION(function_start, tuple(p['name'] for p in params),
name, True, tuple(declared_vars)))
self.function_declaration_tape.append(STORE(name))
self.function_declaration_tape.append(POP())
def FunctionExpression(self, id, params, defaults, body, **kwargs):
if defaults:
raise NotImplementedError('Defaults not available in ECMA 5.1')
# compile function
self.record_state(
) # cleans translator state and appends it to the stack so that it can be later restored
function_start = self.exe.get_new_label()
function_declarations = self.exe.get_new_label()
declarations_done = self.exe.get_new_label(
) # put jump to this place at the and of function tape!
function_end = self.exe.get_new_label()
# skip the function if encountered externally
self.emit('JUMP', function_end)
self.emit('LABEL', function_start)
# call is made with empty stack so load undefined to fill it
self.emit('LOAD_UNDEFINED')
# declare all functions
self.emit('JUMP', function_declarations)
self.emit('LABEL', declarations_done)
self.function_declaration_tape.append(LABEL(function_declarations))
self.emit(body)
self.ReturnStatement(None)
self.function_declaration_tape.append(JUMP(declarations_done))
self.exe.tape.extend(self.function_declaration_tape)
self.emit('LABEL', function_end)
declared_vars = self.declared_vars
self.restore_state()
# create function object and append to stack
name = id.get('name') if id else None
self.emit('LOAD_FUNCTION', function_start,
tuple(p['name'] for p in params), name, False,
tuple(declared_vars))
def Identifier(self, name, **kwargs):
if name == 'true':
self.emit('LOAD_BOOLEAN', 1)
elif name == 'false':
self.emit('LOAD_BOOLEAN', 0)
elif name == 'undefined':
self.emit('LOAD_UNDEFINED')
else:
self.emit('LOAD', name)
def IfStatement(self, test, consequent, alternate, **kwargs):
alt = self.exe.get_new_label()
end = self.exe.get_new_label()
# if
self.emit(test)
self.emit('JUMP_IF_FALSE', alt)
# consequent
self.emit(consequent)
self.emit('JUMP', end)
# alternate
self.emit('LABEL', alt)
if alternate is not None:
self.emit(alternate)
# end of if statement
self.emit('LABEL', end)
def LabeledStatement(self, label, body, **kwargs):
label = label['name']
if body['type'] in ('WhileStatement', 'DoWhileStatement',
'ForStatement', 'ForInStatement'):
# Continue label available... Simply take labels defined by the loop.
# It is important that they request continue label first
self.declared_continue_labels[label] = self.exe._label_count + 1
self.declared_break_labels[label] = self.exe._label_count + 2
self.emit(body)
del self.declared_break_labels[label]
del self.declared_continue_labels[label]
else:
# only break label available
lbl = self.exe.get_new_label()
self.declared_break_labels[label] = lbl
self.emit(body)
self.emit('LABEL', lbl)
del self.declared_break_labels[label]
def Literal(self, value, **kwargs):
if value is None:
self.emit('LOAD_NULL')
elif isinstance(value, bool):
self.emit('LOAD_BOOLEAN', int(value))
elif isinstance(value, basestring):
self.emit('LOAD_STRING', unicode(value))
elif isinstance(value, (float, int, long)):
self.emit('LOAD_NUMBER', float(value))
elif isinstance(value, tuple):
self.emit('LOAD_REGEXP', *value)
else:
raise RuntimeError('Unsupported literal')
def LogicalExpression(self, left, right, operator, **kwargs):
end = self.exe.get_new_label()
if operator == '&&':
# AND
self.emit(left)
self.emit('JUMP_IF_FALSE_WITHOUT_POP', end)
self.emit('POP')
self.emit(right)
self.emit('LABEL', end)
elif operator == '||':
# OR
self.emit(left)
self.emit('JUMP_IF_TRUE_WITHOUT_POP', end)
self.emit('POP')
self.emit(right)
self.emit('LABEL', end)
else:
raise RuntimeError("Unknown logical expression: %s" % operator)
def MemberExpression(self, computed, object, property, **kwargs):
if computed:
self.emit(object)
self.emit(property)
self.emit('LOAD_MEMBER')
else:
self.emit(object)
self.emit('LOAD_MEMBER_DOT', property['name'])
def NewExpression(self, callee, arguments, **kwargs):
self.emit(callee)
if arguments:
n = len(arguments)
for e in arguments:
self.emit(e)
self.emit('LOAD_N_TUPLE', n)
self.emit('NEW')
else:
self.emit('NEW_NO_ARGS')
def ObjectExpression(self, properties, **kwargs):
data = []
for prop in properties:
self.emit(prop['value'])
if prop['computed']:
raise NotImplementedError(
'ECMA 5.1 does not support computed object properties!')
data.append((to_key(prop['key']), prop['kind'][0]))
self.emit('LOAD_OBJECT', tuple(data))
def Program(self, body, **kwargs):
old_tape_len = len(self.exe.tape)
self.emit('LOAD_UNDEFINED')
self.emit(body)
# add function tape !
self.exe.tape = self.exe.tape[:old_tape_len] + self.function_declaration_tape + self.exe.tape[old_tape_len:]
def Pyimport(self, imp, **kwargs):
raise NotImplementedError(
'Not available for bytecode interpreter yet, use the Js2Py translator.'
)
def Property(self, kind, key, computed, value, method, shorthand,
**kwargs):
raise NotImplementedError('Not available in ECMA 5.1')
def RestElement(self, argument, **kwargs):
raise NotImplementedError('Not available in ECMA 5.1')
def ReturnStatement(self, argument, **kwargs):
self.emit('POP') # pop result of expression statements
if argument is None:
self.emit('LOAD_UNDEFINED')
else:
self.emit(argument)
self.emit('RETURN')
def SequenceExpression(self, expressions, **kwargs):
for e in expressions:
self.emit(e)
self.emit('POP')
del self.exe.tape[-1]
def SwitchCase(self, test, consequent, **kwargs):
raise NotImplementedError('Already implemented in SwitchStatement')
def SwitchStatement(self, discriminant, cases, **kwargs):
self.emit(discriminant)
labels = [self.exe.get_new_label() for case in cases]
tests = [case['test'] for case in cases]
consequents = [case['consequent'] for case in cases]
end_of_switch = self.exe.get_new_label()
# translate test cases
for test, label in zip(tests, labels):
if test is not None:
self.emit(test)
self.emit('JUMP_IF_EQ', label)
else:
self.emit('POP')
self.emit('JUMP', label)
# this will be executed if none of the cases worked
self.emit('POP')
self.emit('JUMP', end_of_switch)
# translate consequents
self.implicit_breaks.append(end_of_switch)
for consequent, label in zip(consequents, labels):
self.emit('LABEL', label)
self._emit_statement_list(consequent)
self.implicit_breaks.pop()
self.emit('LABEL', end_of_switch)
def ThisExpression(self, **kwargs):
self.emit('LOAD_THIS')
def ThrowStatement(self, argument, **kwargs):
# throw with the empty stack
self.emit('POP')
self.emit(argument)
self.emit('THROW')
def TryStatement(self, block, handler, finalizer, **kwargs):
try_label = self.exe.get_new_label()
catch_label = self.exe.get_new_label()
finally_label = self.exe.get_new_label()
end_label = self.exe.get_new_label()
self.emit('JUMP', end_label)
# try block
self.emit('LABEL', try_label)
self.emit('LOAD_UNDEFINED')
self.emit(block)
self.emit(
'NOP'
) # needed to distinguish from break/continue vs some internal jumps
# catch block
self.emit('LABEL', catch_label)
self.emit('LOAD_UNDEFINED')
if handler:
self.emit(handler['body'])
self.emit('NOP')
# finally block
self.emit('LABEL', finally_label)
self.emit('LOAD_UNDEFINED')
if finalizer:
self.emit(finalizer)
self.emit('NOP')
self.emit('LABEL', end_label)
# give life to the code
self.emit('TRY_CATCH_FINALLY', try_label, catch_label,
handler['param']['name'] if handler else None, finally_label,
bool(finalizer), end_label)
def UnaryExpression(self, operator, argument, **kwargs):
if operator == 'typeof' and argument[
'type'] == 'Identifier': # todo fix typeof
self.emit('TYPEOF', argument['name'])
elif operator == 'delete':
if argument['type'] == 'MemberExpression':
self.emit(argument['object'])
if argument['property']['type'] == 'Identifier':
self.emit('LOAD_STRING',
unicode(argument['property']['name']))
else:
self.emit(argument['property'])
self.emit('DELETE_MEMBER')
elif argument['type'] == 'Identifier':
self.emit('DELETE', argument['name'])
else:
self.emit('LOAD_BOOLEAN', 1)
elif operator in UNARY_OPERATIONS:
self.emit(argument)
self.emit('UNARY_OP', operator)
else:
raise MakeError('SyntaxError',
'Unknown unary operator %s' % operator)
def UpdateExpression(self, operator, argument, prefix, **kwargs):
incr = int(operator == "++")
post = int(not prefix)
if argument['type'] == 'MemberExpression':
if argument['computed']:
self.emit(argument['object'])
self.emit(argument['property'])
self.emit('POSTFIX_MEMBER', post, incr)
else:
self.emit(argument['object'])
name = to_key(argument['property'])
self.emit('POSTFIX_MEMBER_DOT', post, incr, name)
elif argument['type'] == 'Identifier':
name = to_key(argument)
self.emit('POSTFIX', post, incr, name)
else:
raise MakeError('SyntaxError',
'Invalid left-hand side in assignment')
def VariableDeclaration(self, declarations, kind, **kwargs):
if kind != 'var':
raise NotImplementedError(
'Only var variable declaration is supported by ECMA 5.1')
for d in declarations:
self.emit(d)
def LexicalDeclaration(self, declarations, kind, **kwargs):
raise NotImplementedError('Not supported by ECMA 5.1')
def VariableDeclarator(self, id, init, **kwargs):
name = id['name']
if name in SPECIAL_IDENTIFIERS:
raise MakeError('Invalid left-hand side in assignment')
self.declared_vars.append(name)
if init is not None:
self.emit(init)
self.emit('STORE', name)
self.emit('POP')
def WhileStatement(self, test, body, **kwargs):
continue_label = self.exe.get_new_label()
break_label = self.exe.get_new_label()
self.emit('LABEL', continue_label)
self.emit(test)
self.emit('JUMP_IF_FALSE', break_label)
# translate the body, remember to add and afterwards remove implicit break/continue labels
self.implicit_continues.append(continue_label)
self.implicit_breaks.append(break_label)
self.emit(body)
self.implicit_continues.pop()
self.implicit_breaks.pop()
self.emit('JUMP', continue_label) # loop back
self.emit('LABEL', break_label)
def WithStatement(self, object, body, **kwargs):
beg_label = self.exe.get_new_label()
end_label = self.exe.get_new_label()
# scope
self.emit(object)
# now the body
self.emit('JUMP', end_label)
self.emit('LABEL', beg_label)
self.emit('LOAD_UNDEFINED')
self.emit(body)
self.emit('NOP')
self.emit('LABEL', end_label)
# with statement implementation
self.emit('WITH', beg_label, end_label)
def _emit_statement_list(self, statements):
for statement in statements:
self.emit(statement)
def emit(self, what, *args):
''' what can be either name of the op, or node, or a list of statements.'''
if isinstance(what, basestring):
return self.exe.emit(what, *args)
elif isinstance(what, list):
self._emit_statement_list(what)
else:
return getattr(self, what['type'])(**what)
import os, codecs
def path_as_local(path):
if os.path.isabs(path):
return path
# relative to cwd
return os.path.join(os.getcwd(), path)
def get_file_contents(path_or_file):
if hasattr(path_or_file, 'read'):
js = path_or_file.read()
else:
with codecs.open(path_as_local(path_or_file), "r", "utf-8") as f:
js = f.read()
return js
def main():
from space import Space
import fill_space
from pyjsparser import parse
import json
a = ByteCodeGenerator(Code())
s = Space()
fill_space.fill_space(s, a)
a.exe.space = s
s.exe = a.exe
con = get_file_contents('internals/esprima.js')
d = parse(con + (
''';JSON.stringify(exports.parse(%s), 4, 4)''' % json.dumps(con)))
# d = parse('''
# function x(n) {
# log(n)
# return x(n+1)
# }
# x(0)
# ''')
# var v = 333333;
# while (v) {
# v--
#
# }
a.emit(d)
print(a.declared_vars)
print(a.exe.tape)
print(len(a.exe.tape))
a.exe.compile()
def log(this, args):
print(args[0])
return 999
print(a.exe.run(a.exe.space.GlobalObj))
if __name__ == '__main__':
main()