2019-05-12 04:23:46 +00:00
|
|
|
from .code import Code
|
|
|
|
from .simplex import MakeError
|
|
|
|
from .opcodes import *
|
|
|
|
from .operations import *
|
|
|
|
from .trans_utils import *
|
2019-04-11 00:02:14 +00:00
|
|
|
|
|
|
|
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):
|
2019-05-12 04:23:46 +00:00
|
|
|
old_tape_len = len(self.exe.tape)
|
2019-04-11 00:02:14 +00:00
|
|
|
self.emit('LOAD_UNDEFINED')
|
|
|
|
self.emit(body)
|
|
|
|
# add function tape !
|
2019-05-12 04:23:46 +00:00
|
|
|
self.exe.tape = self.exe.tape[:old_tape_len] + self.function_declaration_tape + self.exe.tape[old_tape_len:]
|
2019-04-11 00:02:14 +00:00
|
|
|
|
|
|
|
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)
|
2019-05-12 04:23:46 +00:00
|
|
|
print(a.declared_vars)
|
|
|
|
print(a.exe.tape)
|
|
|
|
print(len(a.exe.tape))
|
2019-04-11 00:02:14 +00:00
|
|
|
|
|
|
|
a.exe.compile()
|
|
|
|
|
|
|
|
def log(this, args):
|
2019-05-12 04:23:46 +00:00
|
|
|
print(args[0])
|
2019-04-11 00:02:14 +00:00
|
|
|
return 999
|
|
|
|
|
2019-05-12 04:23:46 +00:00
|
|
|
print(a.exe.run(a.exe.space.GlobalObj))
|
2019-04-11 00:02:14 +00:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|